【问题标题】:Is there a better way to do object serialization for this class?有没有更好的方法来为这个类进行对象序列化?
【发布时间】:2022-01-13 23:16:52
【问题描述】:

我在看别人的代码,有这个类的序列化版本:

struct ObjectInfo
{
    int32_t m_typeId;
    string m_objectName;
    vector<int32_t> m_haveKeysId; 
    map<int32_t,double> m_objectFeatures;
    
    ObjectInfo():m_typeId(-1),m_objectName("")
    {
        m_objectFeatures.clear();
        m_haveKeysId.clear();
    }
}

它的二进制版本如下:

struct ObjectInfo_B
{
    int32_t m_typeId;
    int32_t m_objectNamePos;
    
    int32_t m_startIndex;
    int32_t m_endIndex;

    int32_t m_haveKeysIdStartIndex;
    int32_t m_haveKeysIdEndIndex;
    
    ObjectInfo_B()
    {
        m_typeId = -1;
        m_objectNamePos = 0;
        m_startIndex = -1;
        m_endIndex = -1;
        m_haveKeysIdStartIndex = -1;
        m_haveKeysIdEndIndex = -1;
    }

然后有一个ObjectInfo的向量:

vector<ObjectInfo> *objectsVec;
ObjectInfo_B *bObjects;

...

现在要转换的代码如下:

startIndex = 0;
int32_t curBufferSize = 0;
for(size_t i = 0;i<objectsVec->size();i++)
{
    bObjects[i].m_typeId = (*objectsVec)[i].m_typeId;
    bObjects[i].m_objectNamePos = curBufferSize;
    
    strcpy(m_objectNameBuffer+curBufferSize,(*objectsVec)[i].m_objectName.c_str());
    curBufferSize += (*objectsVec)[i].m_objectName.size() + 1;
    
    bObjects[i].m_startIndex = startIndex;
    bObjects[i].m_endIndex = startIndex + (*objectsVec)[i].m_objectFeatures.size();
    startIndex = bObjects[i].m_endIndex;

    bObjects[i].m_haveKeysIdStartIndex = haveKeyStartIndex;
    bObjects[i].m_haveKeysIdEndIndex = haveKeyStartIndex +(*objectsVec)[i].m_haveKeysId.size();

...

fwrite((char*)bObjects,sizeof(ObjectInfo_B),wcount,output);

这个好像很复杂,我不是连载的农家乐。有没有更简单的方法在 C++ 中做到这一点?快速搜索一下,下面这个可以做类似的事情,但是它可以用更简单的方式为上面的代码做转换吗?

https://www.boost.org/doc/libs/1_37_0/libs/serialization/doc/index.html

【问题讨论】:

  • 您是否考虑过将其转换为 JSON?或者this怎么样?
  • 二进制序列化是出了名的困难并且通常不可移植。(不能保证精确的内存布局)。您最好序列化为某些文本格式(如 xml、json、yaml)。如果您需要存储大量数据,请寻找可以执行此操作的库(或者您最终不得不自己编写代码,其规范如下:一个 int 需要 4 个字节存储小端,一个字符串以一个整数开头,说明它的大小后跟 utf-8 编码字符...等等..等等..
  • @PepijnKramer 在没有完全理解作者意图的情况下,我不想将其转换为 JSON。此代码将纯文本数据加载到 ObjectInfo 中,将其转换为 ObjectInfo_B 并将其序列化为二进制格式文件作为输出。
  • 有没有更好的方法来为这个类进行对象序列化? -- 以基于意见的方式结束。如果您的方式有效,而其他人认为“更好的方式”是使用库或知名格式,那么...
  • 我也不知道意图;)但是编写的二进制版本很脆弱,只有在所有编译器设置等(操作系统、目标 cpu 等)都完全保留后才能读回一样的。

标签: c++ serialization object-serialization


【解决方案1】:

我将重构代码的方向草图:

#include <cstdint>
#include <iostream>
#include <fstream>
#include <bitcast>

struct B
{
};

struct ObjectInfo_B
{
    std::int32_t m_typeId = -1;
    std::int32_t m_objectNamePos = -1;
    
    std::int32_t m_startIndex = -1;
    std::int32_t m_endIndex = -1;

    std::int32_t m_haveKeysIdStartIndex = -1;
    std::int32_t m_haveKeysIdEndIndex = -1;

};

auto get_data_from_objects(const std::vector<B>& objects)
{
    std::int32_t startIndex{0};
    std::int32_t curBufferSize{0};
    std::vector<ObjectInfo_B> object_infos(objects.size());
    
    for (std::size_t n = 0; n < object.size(); n++ )
    {
        auto& object_info = objects_info[n];
        auto& object = objects[n];
        
        object_info.m_typeId = object.m_typeId;
        object_info.m_objectNamePos = curBufferSize;
        ....
    }
    
}


std::ostream operator<<(std::ostream& os, const std::vector<ObjectInfo_B>& object_infos)
{
    for(const ObjectInfo_B& object_info : object_infos)
    {
        os.fwrite(std::bitcast<char*>(&object_info), sizeof(ObjectInfo_B));
    }
}


void save_to(std::ostream& os, const std::vector<B>& objects)
{
   auto object_infos = get_data_from_objects(objects);
   os << object_infos();
}

【讨论】:

    【解决方案2】:

    主要问题是你为什么要持有同一个班级的两个不同版本。如果二进制版本的主要目的是提供二进制 I/O,我建议编写相应的 I/O 函数。这是一个简单的example。

    如果您正在处理遗留代码,ObjectInfo 的 ObjectInfo_b 构造函数会使代码更具可读性:

    ObjectInfo_b(const ObjectInfo& obj) {
        // copy all member variables according to your code snippet
        m_typeId = obj.m_typeId;
        // ...
    }
    

    那么序列化部分是这样的

    vector<ObjectInfo> objectsVec;  // Note: removed pointer
    vector<ObjectInfo_b> bObjects; // also using a std::vector
    
    for (const ObjectInfo obj : objectsVec) {
        // create ObjectInfo_b from obj and append to vector via move semantics
        bObjects.emplace_back(ObjectInfo_b(obj)); 
    }
    // ...
    // Pointer to objects
    bObjects* bObject_ptr = &bObjects[0];
    

    同样,根据提供的信息,该类似乎是重复的,应该被删除。以二进制格式写入 ObjectInfo 的函数就足够了。

    【讨论】:

    • 但是这两个类的定义有很大的不同。在第二个中,作者 m_startIndex 和 m_endIndex 的目的是什么?我想应该有一种更简单的方法可以在不损害作者意图的情况下达到相同的目的。此代码将纯文本数据加载到 ObjectInfo 中,将其转换为 ObjectInfo_B 并将其序列化为二进制格式文件作为输出。
    • 我假设 m_startIndex 和 m_endIndex 用于保存地图的二进制版本的地址。如果需要这些,可以即时计算。我的猜测是更好的 I/O 处理使它们变得不必要。
    猜你喜欢
    • 2013-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-11
    相关资源
    最近更新 更多