【问题标题】:MessagePack C++ - How to iterate through an unknown data structure?MessagePack C++ - 如何遍历未知的数据结构?
【发布时间】:2017-06-23 15:39:34
【问题描述】:

我想像这样使用 MessagePack 在 C++ 和 Python 语言之间共享结构化数据:

{
  "t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ],
  "x" : [ x0,..,xN],
  "P" : [ [P00, ..., P0N], ..., [PM0,...,PMN] ]
}

变量的数量是可选的,因此在某些情况下,我将仅作为示例:

{
 "t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ]
}

用 Python 解码很简单,我的问题是弄清楚 如果我事先不知道的结构,如何在 C++ 中对此进行解码 数据 ?或我将拥有的变量的确切数量;是吗 在这些情况下可以迭代结构吗?

我设法处理了一个“固定”的数据结构(总是使用相同的 变量的数量)定义一个结构,例如:

struct variables
{
   std::vector< std::vector<double> > t;
   std::vector< double > x;
   std::vector< std::vector<double> > P;
   MSPACK_DEFINE_MAP( t, x, P );
};

std::stringstream inBuffer;

.... (read data )

std::string str( inBuffer.str() );
msgpack::object_handle oh = msgpack::unpack( str.data(), str.size() );
msgpack::object deserialized = oh.get();

variables var;
deserialized.convert( var );

有没有更好的方法来完成这个?,如何管理可选 不能出现在结构中的变量?我重复 上一个问题:我可以在 C++ 中迭代一个未知的数据结构吗? 怎么样?

提前致谢!

问候,埃内斯托

【问题讨论】:

  • 我没有使用过消息包,但您需要实际遍历每个级别的数据,并且知道或能够查询每个级别预期/存在的数据类型水平。
  • 嗨,xaxxon,感谢您的回复,但我完全不知道是否可以迭代或“导航”消息包中的数据,也许我必须编辑我的问题。
  • 你使用的是哪个 C++ msgpack 实现?
  • 看起来你通过调用 unpack 得到了你的顶级 msgpack::object,然后你可以使用 ::as() 操作符尝试将其转换为特定类型,例如作为地图。然后从那里,您可以查看映射的键/值对并检索与每个键关联的 msgpack::object - 您可以在这些对象上调用 as() 等等。您可以使用的不同类型尝试转换为:c.msgpack.org/cpp/annotated.html object_map、object_array 等。我从未这样做过,但查看 API 并使用过类似的 API,这似乎是正确的。

标签: c++ msgpack


【解决方案1】:

有两种方法可以处理未知数据结构。

第一种方式是使用解析/访问机制。 这是一个例子:

#include <msgpack.hpp>
#include <sstream>
#include <iostream>

// This is a simple print example visitor.
// You can do any processing in your visitor.
struct my_visitor : msgpack::null_visitor {
    bool start_map_key() {
        processing_map_key = true;
        return true;
    }
    bool end_map_key() {
        processing_map_key = false;
        return true;
    }
    bool start_array(uint32_t size) {
        std::cout << "array (size:" << size << ")[" << std::endl;
        return true;
    }
    bool end_array() {
        std::cout << "]" << std::endl;
        return true;
    }

    bool visit_str(const char* v, uint32_t size) {
        if (processing_map_key) {
            std::cout << "map key:" << std::string(v, size) << std::endl;
        }
        return true;
    }
    bool visit_positive_integer(uint64_t v) {
        std::cout << "found value:" << v << std::endl;
        return true;
    }

    bool processing_map_key = false;
    std::string indent;
};


int main() {
    // create test data
    std::stringstream ss;
    msgpack::packer<std::stringstream> pk(ss);
    pk.pack_map(1);
    pk.pack("t");
    pk.pack_array(2);
    pk.pack_array(3);
    pk.pack(1);
    pk.pack(2);
    pk.pack(3);
    pk.pack_array(3);
    pk.pack(4);
    pk.pack(5);
    pk.pack(6);

    // print data (for debug)
    {
        auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
        std::cout << oh.get() << std::endl;
    }

    // apply visitor
    {
        my_visitor mv;
        msgpack::parse(ss.str().data(), ss.str().size(), mv);
    }
}

运行演示:https://wandbox.org/permlink/3NrR4IMDIuLTk9e9

https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_visitor

另一种方法是使用msgpack::type::variant 或`msgpack::type::variant_ref。 前者复制数据,你可以更新它。后者不复制数据。你不能更新它。 这种方法需要提升。所以你需要定义MSGPACK_USE_BOOST。我建议定义为编译器选项。

// Boost is required
#define MSGPACK_USE_BOOST

#include <msgpack.hpp>
#include <sstream>
#include <iostream>

struct my_visitor:boost::static_visitor<void> {
    void operator()(uint64_t v) const {
        std::cout << "positive insteger:" << v << std::endl;
    }
    // const is required for map key because std::multimap's key (first) is const.
    void operator()(std::string const& v) const {
        std::cout << "string:" << v << std::endl;
    }
    void operator()(std::vector<msgpack::type::variant>& v) const {
        std::cout << "array found" << std::endl;
        for (auto& e : v) {
            boost::apply_visitor(*this, e);
        }
    }
    void operator()(std::multimap<msgpack::type::variant, msgpack::type::variant>& v) const {
        std::cout << "map found" << std::endl;
        for (auto& e : v) {
            std::cout << "key:" << std::endl;
            boost::apply_visitor(*this, e.first);
            std::cout << "value:" << std::endl;
            boost::apply_visitor(*this, e.second);
        }
    }
    template <typename T>
    void operator()(T const&) const {
        std::cout << "  match others" << std::endl;
    }
};

int main() {
    // create test data
    std::stringstream ss;
    msgpack::packer<std::stringstream> pk(ss);
    pk.pack_map(1);
    pk.pack("t");
    pk.pack_array(2);
    pk.pack_array(3);
    pk.pack(1);
    pk.pack(2);
    pk.pack(3);
    pk.pack_array(3);
    pk.pack(4);
    pk.pack(5);
    pk.pack(6);

    auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
    std::cout << oh.get() << std::endl;

    msgpack::type::variant v = oh.get().as<msgpack::type::variant>();
    boost::apply_visitor(my_visitor(), v);
}

运行演示:https://wandbox.org/permlink/HQwJjfwW8rLEMi0d

https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_variant

以下是示例: https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_capitalize.cpp https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_mapbased.cpp

这两种方式都可以处理不可预测的数据结构。你需要做一些访客处理。如果数据结构在某种程度上是可预测的,那么您的原始方法也是不错的方法。

【讨论】:

  • 您好近藤孝敏,非常感谢您的回答!在使用 xaxxon 给出的提示查看您的帖子之前,我解决了这个问题:我使用公共属性“type”(object_type)和“via”(union_type)迭代了 msgpack::object,因此相应地访问了不同的类型。
【解决方案2】:

实际上有一种更简单的方法,如果您正在处理地图(如问题中所述),而不是数组。

msgpack::object_handle oh = msgpack::unpack(/* some data */);
std::map<std::string,msgpack::type::variant> map = obj.convert();

这样您将获得包含所有数据的map,无需访问者或提升。

【讨论】:

    猜你喜欢
    • 2012-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-02
    • 2016-06-02
    • 1970-01-01
    相关资源
    最近更新 更多