【问题标题】:C++ way to serialize a message?C++ 序列化消息的方法?
【发布时间】:2011-11-15 15:02:53
【问题描述】:

在我当前的项目中,我有几个不同的接口需要我将消息序列化到字节缓冲区中。我觉得我做这件事的方式可能不会让真正的 C++ 程序员开心(我也愿意)。

我通常会这样做:

struct MyStruct {
    uint32_t x;
    uint64_t y;
    uint8_t  z[80];
};

uint8_t* serialize(const MyStruct& s) {
    uint8_t* buffer = new uint8_t[sizeof(s)];
    uint8_t* temp = buffer;
    memcpy(temp, &s.x, sizeof(s.x));
    temp += sizeof(s.x);

    //would also have put in network byte order...
    ... etc ...

    return buffer;
}

请原谅任何错别字,这只是我脑海中的一个例子。显然,如果我要序列化的结构具有内部指针,它会变得更加复杂。

所以,我有两个密切相关的问题:

  1. 假设我知道目标系统具有相同的字节序,通过将结构直接转换为字符缓冲区进行序列化,在上述特定场景中是否存在任何问题?

  2. 主要问题:有没有更好的……呃……C++?除了添加智能指针之外,还有什么方法可以做到这一点?我觉得这是一个常见的问题,STL 可能会处理它 - 如果没有,我相信无论如何使用 C++ 机制都有更好的方法。

编辑如果您可以使用标准 C++/STL 在不添加库的情况下以更好的方式序列化此结构的干净示例,则可加分。

【问题讨论】:

标签: c++ serialization


【解决方案1】:

您可能想看看 Google Protocol Buffers(也称为 protobuf)。您在语言中立的 IDL 中定义您的数据,然后通过生成器运行它以生成您的 C++ 类。它会处理字节排序问题,并且可以提供非常紧凑的二进制形式。

通过使用它,您不仅可以保存您的 C++ 数据,还可以在其他语言(C#、Java、Python 等)中使用,因为它们有可用的 protobuf 实现。

【讨论】:

  • +1 因为这看起来很有趣 - 但如果你可以添加一个可以用原生 C++ 流/STL 巧妙地完成的答案,我肯定会接受 - 我在嵌入式平台上工作,并且获得批准的过程添加库非常困难:(
【解决方案2】:

您应该可以直接使用 Boost::serialization 或流。右侧任一链接中的更多信息。

Is it possible to serialize and deserialize a class in C++?

【讨论】:

  • +1 因为这看起来很有趣 - 但如果你可以添加一个可以用原生 C++ 流/STL 巧妙地完成的答案,我肯定会接受 - 我在嵌入式平台上工作,并且获得批准的过程添加库非常困难:(
  • 好吧,我可以添加一个答案,但我只是认为“右边的链接”有很多信息,我不会在这里复制它 :)
【解决方案3】:

刚刚投票给 AzPs 回复作为答案,首先检查 Boost 是要走的路。

另外关于您的代码示例:

1 - 将序列化函数的签名更改为获取文件的方法:

void MyStruct::serialize(FILE* file) // or stream
{
    int size = sizeof(this);
    fwrite(&size, sizeof(int), 1, file); // write size
    fwrite(this, 1, size, file);         // write raw bytes of struct
}

减少了复制结构的必要性。

2 - 是的,您的代码使序列化字节取决于您的平台、编译器和编译器设置。这没有好坏之分,如果相同的二进制文件写入和读取序列化字节,这可能是有益的,因为它具有简单性和性能。但不仅是字节序,打包和结构布局也会影响兼容性。例如,您的应用程序的 32 位或 64 位版本肯定会改变我们结构的布局。最后序列化原始足迹也序列化填充字节 - 编译器可能放置在结构字段之间的字节,对于高流量网络流来说是不可取的开销(请参阅谷歌协议缓冲区,因为它们会寻找可以节省的每一位)。

编辑:

我看到您添加了“嵌入式”。是的,那么这种简单的序列化/反序列化(上述序列化的镜像实现)方法可能是一个不错且简单的选择。

【讨论】:

  • 而stdio的FILE指针是C++的序列化方式?我知道您在评论中提到了一个流,但它不属于评论,而是属于代码!否则,将文件或流传递给函数的想法当然比在每个函数调用中分配新缓冲区更好。
  • 好吧,我明白你的意思。 c++ 方式将是 std::ostream,是的。但特别是 std::stream 库通常会带来惊喜,并且原始 c API 没有任何问题,尤其是完全平台中立的文件 api。在这里,避免 std::stream 例如从考虑这些库的内部缓冲和刷新中解放出来。如果它真的只是将结构保存在一个文件中,而我并不真正了解我必须处理的 std::stream 的内部结构,那么原始文件只是实用、简单并且肯定比 std::stream 更快。
  • @thomas stdio FILEs 也不能使用缓冲吗?而且我不确定fwrite 是否真的比ostream::write 快,也许是通过函数调用,无论如何,这将被写作本身所排除。当然你不要使用operator<< 进行二进制序列化。我只是看不出out.write(...)fwrite(FILE, ...) 有什么不同,除了是 OP 要求的 C++ 方式。
  • 嗯,很多教科书中的“c++方式”和“真正的c++程序员”做的不一样。我不是在这里进行边展讨论,应该足以说许多(如果不是大多数)伟大的 C++ 商店限制 iostreams。正如我们在这里谈到的协议缓冲区,获取 C++ 源代码并在那里找到 FILE*。我同意的是 FILE* 看起来像 C-ish,因此我个人小心地使用 int 作为文件句柄,对于 *nix,windows 兼容性这是可以的。是的,许多或大多数知识渊博的 C++ 程序员出于多种原因在 stdio 级别进行二进制文件或套接字访问。我为其他人留了空间......
猜你喜欢
  • 2010-11-24
  • 2016-05-27
  • 2020-09-07
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2019-08-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多