【问题标题】:binary reading and writing complicated stucts in c++在 C++ 中二进制读写复杂的结构
【发布时间】:2014-04-24 01:22:24
【问题描述】:

我需要能够在我的项目中保存/读取我的数据结构,但是所有数据本身都是非常复杂和独特的结构形式,我通常通过其他结构和向量来实现这些结构。我已经将它们全部包装到一个结构中,这样我就有了类似的东西

struct master{
    std::vector<apprentice_type1> a;
    std::vector<apprentice_type2> b; //etc.

    std::string label;
};

与其他定义类似

struct apprentice_type1{
    vec location;
    int point_label;
    std::vector<int> relational_data;
};

struct vec{
    double x,y,z;
};

所以它变得相当复杂!我非常希望有一些好的、快速和天真的像

master obj;
//write to obj....
std::ofstream ofs("data.dat", std::ios::binary);
ofs.write((char *)&obj, sizeof(obj));

会起作用,但目前似乎不起作用。在我迷失在调试兔子洞之前,这实际上是我接近它的方式还是我需要重新考虑?如果有,怎么做?

谢谢。

【问题讨论】:

  • 这可能是更好的方法,为您的master 类型编写一个serialize(std::ostream&amp;) 方法。向量不会以有用的方式序列化,就像您实际编写代码的方式一样。
  • 您希望的ofs.write 调用无法工作,因为obj 在内存中不连续。例如,结构的 std::vector 成员从堆中分配内存,因此它不在 &obj -> (&obj + sizeof(obj)) 的区域中。
  • 使用现有的序列化协议(例如 Google 协议缓冲区),免去轮子改造。
  • 看看boost serialization,它似乎有一些现成的实现来保存STL类型,这可以节省你很多时间。

标签: c++ serialization input binary output


【解决方案1】:

如果您想要替代 Boost 序列化并访问 C++11 编译器,您还可以查看cereal。它的工作方式与 Boost 序列化几乎相同,但只是一个标头库,因此没有可链接的内容。

【讨论】:

  • 我喜欢。我目前正在使用 boost,但我有时间让它静态链接,所以我可能会看看这个,谢谢。
【解决方案2】:

[...] 还是我需要重新考虑?如果有,怎么做?

您可能需要提供完整的实现(即探索“兔子洞”)。

这是一个已知问题(流序列化),没有单一的最佳解决方案,因为大多数实现需要解决不同的需求。

您可以执行以下操作之一:

  • 实现 std::i/ostream 序列化;这意味着您将检查您的课程并实现operator&gt;&gt;(std::istream*, your_type&amp;),它是相反的operator&lt;&lt;(std::ostream*, your_type&amp;)。

  • 基于流库(如 boost.archive)实现序列化。

  • 使用 JSON 或 XML 库。

  • 使用谷歌协议缓冲区(或其他)

  • 根据您的需要推出您自己的实施。

【讨论】:

    【解决方案3】:

    如果您选择 boost::serialization,这里有一个小示例:

    #include <fstream>
    #include <vector>
    
    #include <boost/archive/binary_iarchive.hpp>
    #include <boost/archive/binary_oarchive.hpp>
    #include <boost/serialization/vector.hpp>
    
    
    template <typename T>
    inline const boost::serialization::nvp<T> archive_value(const char* name, T& value) {
        return boost::serialization::make_nvp(name, value);
    }
    
    const unsigned Version = 0;
    
    class Point{
        public:
        double x,y,z;
    
        private:
        template <typename P, typename Archive>
        static void serialize(P& p, Archive& ar, const unsigned int version) {
            std::cout << "Point\n";
            ar & archive_value("X", p.x);
            ar & archive_value("Y", p.y);
            ar & archive_value("Z", p.z);
        }
    
        public:
        template <typename Archive>
        void serialize(Archive& ar, const unsigned int version) const {
            serialize(*this, ar, version);
        }
    
        template <typename Archive>
        void serialize(Archive& ar, const unsigned int version) {
            serialize(*this, ar, version);
        }
    };
    
    BOOST_CLASS_VERSION(Point, Version)
    
    struct Scene{
        std::vector<Point> points;
    
        private:
        template <typename S, typename Archive>
        static void serialize(S& s, Archive& ar, const unsigned int version) {
            std::cout << "Scene\n";
            ar & archive_value("Points", s.points);
        }
    
        public:
        template <typename Archive>
        void serialize(Archive& ar, const unsigned int version) const {
            serialize(*this, ar, version);
        }
    
        template <typename Archive>
        void serialize(Archive& ar, const unsigned int version) {
            serialize(*this, ar, version);
        }
    };
    
    BOOST_CLASS_VERSION(Scene, Version)
    
    
    template <typename Archive>
    void register_types(Archive& ar) {
        ar.template register_type<Point>();
        ar.template register_type<Scene>();
    }
    
    int main() {
        Scene scene;
        scene.points = { { 0, 0, 0 }, { 1, 1, 1 }, { 2, 2, 2 } };
    
        // Output
        {
            std::ofstream out("test.dat", std::ios_base::binary);
            boost::archive::binary_oarchive output(out);
    
    
            // First the version!
            output & archive_value("Version", Version);
            // Next the types!
            register_types(output);
            // Finally the data
            output & archive_value("Scene", scene);
        }
    
        scene.points = {};
    
        // Input
        {
            int version;
            std::ifstream in("test.dat", std::ios_base::binary);
            boost::archive::binary_iarchive input(in);
            // First the version!
            input & archive_value("Version", Version);
            // Next the types!
            register_types(input);
            // Finally the data
            input & archive_value("Scene", scene);
        }
    
        for(const auto& p : scene.points)
            std::cout << p.x << '\n';
    }
    

    注意:文件格式可能会演变,序列化功能(输入和/或输出)可能会根据文件版本进行调整。

    【讨论】:

    • 这看起来很有帮助。我已经尽我所能实现了它,但有两个问题:当在文件“boost::archive::text_oarchive oa(ofs);”中写入和读取时实际编译和链接时“oa >object;”没有,我得到很多错误:我正在链接 -lboost_serialization 我错过了什么吗?此外,在设置 ofstream 时,我指定 std::ios::binary 但它仍以文本形式输出。我需要不同的增强功能吗?
    • 有三个(我猜)存档版本:文本 xml 和二进制 - 选择二进制一个 注意:使用 boost::serialization::nvp 传递对象archive_value&lt; ia &gt;("Object", object);(有也是一个boost宏BOOST_SERIALIZATION_NVP,但我更喜欢手动设置名称)
    • 是的,您对不同的选项是正确的。恐怕我对你的下一点不太清楚,我想我正在使用一种稍微不同的方法,就像在这里找到的 boost.org/doc/libs/1_55_0/libs/serialization/example/demo.cpp 一样,烦人的是它确实为我编译,所以我把头发扯掉了我正在做不同的事情。我在没有输入的情况下编译,它输出序列化数据很好......
    • BOOST_CLASS_VERSION 必须在任何命名空间之外(看看它的定义)
    • 我只是按照文档,在我序列化的每个结构的定义之后写 BOOST_CLASS_VERSION(struct_name,1)...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 2022-01-11
    相关资源
    最近更新 更多