Visitor interface

msgpack-c提供解包API。它们将msgpack格式的字节转换为msgpack::object。 然后,将msgpack::object用于多种用途。它类似于XML的DOM(文档对象模型)API。visitor接口为msgpack格式的字节提供了更直接的访问,比如SAXAPI for XML。 您可以定义自己的访问者并将其传递给parse()API。在分析过程中,遍历数据的迭代器。当迭代器遇到msgpack元素时,将调用与该元素对应的访问者成员函数。

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_float32(float v);
    bool visit_float64(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);
    bool referenced() const; // Only when you want to use the visitor with a parser
};

visitor必须实现上述所有成员功能。如果只想实现几个成员函数,那么可以继承预定义的 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_float32(float /*v*/) {
        return true;
    }
    bool visit_float64(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*/) {
    }
};

除错误通知函数外,所有成员函数都具有布尔返回类型。一般情况下,返回true代表要继续解包,false则表示停止解包。

名为visit_xxx()的成员函数是叶元素的访问者。在数组开始/结束时调用start_array()/end_array()。在每个数组项之前/之后调用start_array_item()/end_array_item()。

当map开始/结束时会调用start_map()/end_map()。在每个映射键之前/之后调用start_map_key()/end_map_key()。在每个映射值之前/之后调用start_map_value()/end_map_value()。

发生解析错误时,将调用parse_error()。当缓冲区包含的字节不足,无法生成完整的msgpack时,将调用uncipled_bytes()。

Example

下面是一个将msgpack格式的字节转换为类似JSON的结构的示例(我省略了字符串转义,因为这不是我想演示的一点):

struct json_like_visitor : msgpack::v2::null_visitor {
    json_like_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_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_value() {
        m_s += ",";
        return true;
    }
    bool end_map() {
        m_s.erase(m_s.size() - 1, 1); // remove the last ','
        m_s += "}";
        return true;
    }
    void parse_error(size_t /*parsed_offset*/, size_t /*error_offset*/) {
        // report error.
    }
    void insufficient_bytes(size_t /*parsed_offset*/, size_t /*error_offset*/) {
        // report error.
    }
    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_like;
    json_like_visitor v(json_like);
    std::size_t off = 0;
    std::string const& str = ss.str();
    bool ret = msgpack::v2::parse(str.data(), str.size(), off, v);
    assert(ret);
    assert("{\"key\":[42,null,true]}" == json_like);
}

Parse API

为了使用visitor接口,您需要调用parse API。以下是解析API:

template <typename Visitor>
bool parse(const char* data, size_t len, size_t& off, Visitor& v);
template <typename Visitor>
bool parse(const char* data, size_t len, Visitor& v);

它们类似于unpack APIs。

当您调用一个解析函数时,解析函数会回调您传递的访问者。如果解析过程成功完成,则解析函数返回true,否则返回false。如果访问者的visit函数返回false,那么parse函数将返回false。

你可以像unpacker一样使用流接口的访问者。

template <typename VisitorHolder, typename ReferencedBufferHook>
class parser

VisitorHolder需要一个名为visitor()的函数,该函数返回满足visitor概念的visitor对象的引用。
ReferencedBufferHook需要一个操作符()(char*)。仅当满足以下所有条件时,才会使用当前缓冲区指针调用运算符()

  1. Called parser::reserve_buffer(std::size_t).
  2. 当前缓冲区没有足够的大小,然后分配了新的缓冲区。
    3.访客引用当前缓冲区。在这种情况下,visitor::referenced()应该返回true。

如果visitor::referenced()返回true,则parserdoesn不会释放当前缓冲区,而是调用referencedbufferhook`。因此,您可以有机会为当前缓冲区设置一些清理功能。

请参阅[unpacker的内存管理机制](unpacker’s memory management mechanism.)。

除了bool next()之外,parse与unpacker具有相同的公共成员函数。unpacker具有以下名为next的成员函数:

    bool next(msgpack::object_handle& result, bool& referenced);
    bool next(msgpack::object_handle& result);

parser有以下名为next的成员函数

    bool next();

调用next()时,解析一个MessagePack格式的数据。所以访问者的访问函数被调用。如果解析过程成功完成,则返回true,否则返回false。

Q.E.D.