Skip to content

v2_0_cpp_unpack_visit

Takatoshi Kondo edited this page May 2, 2016 · 4 revisions

Visitor interface for unpacking APIs

msgpack-c provides unpacking APIs. They convert from msgpack formatted bytes to msgpack::object. And then, using msgpack::object for many purposes. It is similar to DOM(Document Object Model) API for XML. The visitor interface provides more direct access for msgpack formatted bytes like the SAX API for XML. You can define your own visitor and pass it to unpacking APIs for visitor. During unpacking process, an iterator traversing the data. When the iterator meets a msgpack element, a visitor member function that corresponding to the element is called.

Visitor concept

struct visitor {
    bool visit_nil();
    bool visit_boolean(bool v);
    bool visit_positive_integer(uint64_t v);
    bool visit_negative_integer(int64_t v);
    bool visit_float(double v);
    bool visit_str(const char* v, uint32_t size);
    bool visit_bin(const char* v, uint32_t size);
    bool visit_ext(const char* v, uint32_t size);
    bool start_array(uint32_t num_elements);
    bool start_array_item();
    bool end_array_item();
    bool end_array();
    bool start_map(uint32_t num_kv_pairs);
    bool start_map_key();
    bool end_map_key();
    bool start_map_value();
    bool end_map_value();
    bool end_map();
    void parse_error(size_t parsed_offset, size_t error_offset);
    void insufficient_bytes(size_t parsed_offset, size_t error_offset);
};

A visitor must implement all member functions above. If you want to implement only a few member functions, then you can inherit pre-defined msgpack::null_visitor.

struct null_visitor {
    bool visit_nil() {
        return true;
    }
    bool visit_boolean(bool /*v*/) {
        return true;
    }
    bool visit_positive_integer(uint64_t /*v*/) {
        return true;
    }
    bool visit_negative_integer(int64_t /*v*/) {
        return true;
    }
    bool visit_float(double /*v*/) {
        return true;
    }
    bool visit_str(const char* /*v*/, uint32_t /*size*/) {
        return true;
    }
    bool visit_bin(const char* /*v*/, uint32_t /*size*/) {
        return true;
    }
    bool visit_ext(const char* /*v*/, uint32_t /*size*/) {
        return true;
    }
    bool start_array(uint32_t /*num_elements*/) {
        return true;
    }
    bool start_array_item() {
        return true;
    }
    bool end_array_item() {
        return true;
    }
    bool end_array() {
        return true;
    }
    bool start_map(uint32_t /*num_kv_pairs*/) {
        return true;
    }
    bool start_map_key() {
        return true;
    }
    bool end_map_key() {
        return true;
    }
    bool start_map_value() {
        return true;
    }
    bool end_map_value() {
        return true;
    }
    bool end_map() {
        return true;
    }
    void parse_error(size_t /*parsed_offset*/, size_t /*error_offset*/) {
    }
    void insufficient_bytes(size_t /*parsed_offset*/, size_t /*error_offset*/) {
    }
};

All member function except error notifying one have bool return type. Normally you need to return true. That means continue unpacking. If you return false, unpacking process doesn't continue.

The member functions named visit_xxx() are visitors for leaf elements. start_array()/end_array() is called when array starts/finishes. start_array_item()/end_array_item() is called just before/after each array items.

start_map()/end_map() is called when map starts/finishes. start_map_key()/end_map_key() is called just before/after each map keys. start_map_value()/end_map_value() is called just before/after each map values.

parse_error() is called when a parse error happens. insufficient_bytes() is called when the buffer doesn't contain enough bytes to build complete msgpack.

Example

Here is an example that coverts msgpack formatted bytes to JSON:

struct json_visitor : msgpack::v2::null_visitor {
    json_visitor(std::string& s):m_s(s) {}

    bool visit_nil() {
        m_s += "null";
        return true;
    }
    bool visit_boolean(bool v) {
        if (v) m_s += "true";
        else m_s += "false";
        return true;
    }
    bool visit_positive_integer(uint64_t v) {
        std::stringstream ss;
        ss << v;
        m_s += ss.str();
        return true;
    }
    bool visit_negative_integer(int64_t v) {
        std::stringstream ss;
        ss << v;
        m_s += ss.str();
        return true;
    }
    bool visit_float(double v) {
        std::stringstream ss;
        ss << v;
        m_s += ss.str();
        return true;
    }
    bool visit_str(const char* v, uint32_t size) {
        m_s += '"' + std::string(v, size) + '"';
        return true;
    }
    bool start_array(uint32_t /*num_elements*/) {
        m_s += "[";
        return true;
    }
    bool end_array_item() {
        m_s += ",";
        return true;
    }
    bool end_array() {
        m_s.erase(m_s.size() - 1, 1); // remove the last ','
        m_s += "]";
        return true;
    }
    bool start_map(uint32_t /*num_kv_pairs*/) {
        m_s += "{";
        return true;
    }
    bool end_map_key() {
        m_s += ":";
        return true;
    }
    bool end_map() {
        m_s += "}";
        return true;
    }
    void parse_error(size_t /*parsed_offset*/, size_t /*error_offset*/) {
        EXPECT_TRUE(false);
    }
    void insufficient_bytes(size_t /*parsed_offset*/, size_t /*error_offset*/) {
        EXPECT_TRUE(false);
    }
    std::string& m_s;
};

int main()
{
    std::stringstream ss;
    msgpack::packer<std::stringstream> p(ss);
    p.pack_map(1);
    p.pack("key");
    p.pack_array(3);
    p.pack(42);
    p.pack_nil();
    p.pack(true);

    std::string json;
    json_visitor v(json);
    std::size_t off = 0;
    bool ret = msgpack::v2::unpack_visit(ss.str().data(), ss.str().size(), off, v);
    assert(ret);
    assert("{\"key\":[42,null,true]}" == json);
}