有没有使用这些库经验的人对他们更喜欢哪一个有任何评论?使用中是否存在性能差异或困难?
11 回答
我已经使用 Boost Serialization 很长时间了,只是深入研究了协议缓冲区,我认为它们的用途并不完全相同。BS(没看到)将您的 C++ 对象保存到流中,而 PB 是您读取/读取的交换格式。
PB 的数据模型要简单得多:您可以获得各种整数和浮点数、字符串、数组、基本结构,仅此而已。BS 允许您一步直接保存所有对象。
这意味着使用 BS,您可以在线获得更多数据,但不必重建所有对象结构,而协议缓冲区更紧凑,但在读取存档后还有更多工作要做。顾名思义,一个用于协议(与语言无关,节省空间的数据传递),另一个用于序列化(节省对象)。
那么对您来说更重要的是:速度/空间效率还是干净的代码?
我在这两个系统上都玩了一点,没什么大不了的,只是一些简单的骇人听闻的东西,但我觉得你应该如何使用这些库有很大的不同。
使用 boost::serialization,您首先编写自己的结构/类,然后添加归档方法,但您仍然留下了一些非常“苗条”的类,它们可以用作数据成员、继承等等。
使用协议缓冲区,即使是一个简单的结构,生成的代码量也相当可观,并且生成的结构和代码更多地用于操作,并且您使用协议缓冲区的功能将数据传输到您自己的内部结构或从您自己的内部结构传输数据.
boost.serialization 有几个额外的问题,我将添加到组合中。警告:除了浏览文档之外,我对协议缓冲区没有任何直接经验。
请注意,虽然我认为 boost 和 boost.serialization 的功能非常出色,但我得出的结论是,它附带的默认存档格式对于有线格式并不是一个很好的选择。
区分您的类的版本(如其他答案中所述, boost.serialization 对数据版本控制有一些支持)和不同版本的序列化库之间的兼容性很重要。
较新版本的 boost.serialization可能不会生成旧版本可以反序列化的档案。(反之则不然:新版本总是旨在反序列化旧版本制作的档案)。这给我们带来了以下问题:
- 我们的客户端和服务器软件都创建了另一个使用的序列化对象,因此如果我们同步升级客户端和服务器,我们只能移动到更新的 boost.serialization。(在您无法完全控制客户的环境中,这是一个相当大的挑战)。
- Boost 捆绑为一个带有共享部分的大库,序列化代码和 boost 库的其他部分(例如 shared_ptr)可能在同一个文件中使用,我无法升级boost 的任何部分,因为我可以' t 升级 boost.serialization。我不确定尝试将多个版本的 boost 链接到一个可执行文件中是否可能/安全/理智,或者我们是否有预算/精力将需要保留在旧版本 boost 上的位重构为单独的可执行文件(在我们的例子中是 DLL)。
- 我们坚持使用的旧版本的 boost 不支持我们使用的最新版本的编译器,所以我们也坚持使用旧版本的编译器。
Google 似乎实际上发布了协议缓冲区有线格式,维基百科将它们描述为向前兼容、向后兼容(尽管我认为 Wikipedia 指的是数据版本控制而不是协议缓冲区库版本控制)。虽然这些都不是前向兼容性的保证,但对我来说似乎是一个更强有力的指示。
总而言之,当我无法同步升级客户端和服务器时,我更喜欢像协议缓冲区这样的众所周知的、已发布的有线格式。
脚注:我的相关答案的无耻插件。
提升序列化
- 是一个用于将数据写入流的库。
- 不压缩数据。
- 不支持自动数据版本控制。
- 支持 STL 容器。
- 写入数据的属性取决于选择的流(例如字节序、压缩)。
协议缓冲区
- 从接口描述生成代码(默认支持 C++、Python 和 Java。C、C# 和其他第三方)。
- 可选择压缩数据。
- 自动处理数据版本控制。
- 处理平台之间的字节序交换。
- 不支持 STL 容器。
Boost 序列化是一个用于将对象转换为序列化数据流的库。Protocol Buffers 做同样的事情,但也为你做其他工作(如版本控制和字节序交换)。对于“小型简单任务”,Boost 序列化更简单。协议缓冲区可能更适合“更大的基础设施”。
编辑:24-11-10:将“自动”添加到 BS 版本控制中。
我没有提升序列化的经验,但我使用过协议缓冲区。我非常喜欢协议缓冲区。请记住以下几点(我在没有提升知识的情况下这么说)。
- 协议缓冲区非常有效,所以我不认为这是一个严重的问题。
- 协议缓冲区提供了一种与其他语言(Python 和 Java ......以及更多正在开发中的语言)一起使用的中间表示。如果你知道你只使用 C++,也许 boost 更好,但使用其他语言的选项很好。
- 协议缓冲区更像是数据容器......没有面向对象的性质,例如继承。想想你想要序列化的结构。
- 协议缓冲区很灵活,因为您可以添加“可选”字段。这基本上意味着您可以在不破坏兼容性的情况下更改协议缓冲区的结构。
希望这可以帮助。
boost.serialization 只需要 C++ 编译器并为您提供一些语法糖,例如
serialize_obj >> archive;
// ...
unserialize_obj << archive;
用于保存和加载。如果 C++ 是您使用的唯一语言,您应该认真对待 boost.serialization。
我快速浏览了 google 协议缓冲区。从我所看到的情况来看,我会说它与 boost.serialization 没有直接的可比性。您必须将 .proto 文件的编译器添加到您的工具链并维护 .proto 文件本身。API 不像 boost.serialization 那样集成到 C++ 中。
boost.serialization 很好地完成了其设计的工作:序列化 C++ 对象 :) OTOH 像 google 协议缓冲区这样的查询 API 为您提供了更大的灵活性。
由于到目前为止我只使用了 boost.serialization,我无法评论性能比较。
我从来没有使用 boost 的库实现过任何东西,但我发现 Google protobuff 更加深思熟虑,而且代码更加简洁易读。我建议您查看您想要使用的各种语言,并阅读代码和文档并下定决心。
我对 protobufs 的一个困难是他们在生成的代码 GetMessage() 中命名了一个非常常用的函数,这当然与 Win32 GetMessage 宏冲突。
我仍然强烈推荐protobufs。它们非常有用。
我知道这是一个较老的问题,但我想我会投入 2 便士!
有了 boost,您就有机会在您的课程中编写一些数据验证;这很好,因为数据定义和有效性检查都在一个地方。
使用 GPB,您可以做的最好的事情是在 .proto 文件中添加注释,并希望所有使用它的人都阅读它,关注它,并自己实施有效性检查。
不用说,如果您依赖网络流另一端的其他人以与自己相同的活力来执行此操作,这不太可能且不可靠。此外,如果对有效性的约束发生变化,则需要计划、协调和完成多个代码更改。
因此,我认为 GPB 不适用于几乎没有机会与所有团队成员定期会面和交谈的开发。
==编辑==
我的意思是这样的:
message Foo
{
int32 bearing = 1;
}
现在谁说有效范围bearing
是多少?我们可以有
message Foo
{
int32 bearing = 1; // Valid between 0 and 359
}
但这取决于其他人阅读并为其编写代码。例如,如果您对其进行编辑并且约束变为:
message Foo
{
int32 bearing = 1; // Valid between -180 and +180
}
您完全依赖于使用此 .proto 更新其代码的每个人。这是不可靠且昂贵的。
至少对于 Boost 序列化,您正在分发单个 C++ 类,并且可以在其中内置数据有效性检查。如果这些约束发生变化,那么除了确保他们使用与您相同版本的源代码外,其他人不需要做任何工作。
选择
还有一个替代方案:ASN.1。这是古老的,但有一些非常非常方便的东西:
Foo ::= SEQUENCE
{
bearing INTEGER (0..359)
}
注意约束。因此,每当有人使用此 .asn 文件并生成代码时,他们最终都会得到自动检查bearing
介于 0 到 359 之间的代码。如果您更新 .asn 文件,
Foo ::= SEQUENCE
{
bearing INTEGER (-180..180)
}
他们需要做的就是重新编译。不需要其他代码更改。
你也可以这样做:
bearingMin INTEGER ::= 0
bearingMax INTEGER ::= 360
Foo ::= SEQUENCE
{
bearing INTEGER (bearingMin..<bearingMax)
}
注意<
. 而且在大多数工具中,bearingMin 和bearingMax 可以在生成的代码中显示为常量。这非常有用。
约束可能非常复杂:
Garr ::= INTEGER (0..10 | 25..32)
查看此PDF中的第 13 章;你能做的真是太棒了;
数组也可以被约束:
Bar ::= SEQUENCE (SIZE(1..5)) OF Foo
Sna ::= SEQUENCE (SIZE(5)) OF Foo
Fee ::= SEQUENCE
{
boo SEQUENCE (SIZE(1..<6)) OF INTEGER (-180<..<180)
}
ASN.1 是老式的,但仍在积极开发、广泛使用(您的手机经常使用它),并且比大多数其他序列化技术灵活得多。我能看到的唯一不足是没有像样的 Python 代码生成器。如果您使用的是 C/C++、C#、Java、ADA,那么免费(C/C++、ADA)和商业(C/C++、C#、JAVA)工具的组合将为您提供很好的服务。
我特别喜欢二进制和基于文本的线格式的广泛选择。这使得它在某些项目中非常方便。有线格式列表当前包括:
- BER(二进制)
- PER(二进制,对齐和未对齐。这是超位效率。例如,和 INTEGER 之间的约束
0
,15
将只占用4 bits
电线) - 开放式反应堆
- DER(另一个二进制文件)
- XML(也是 XER)
- JSON(全新,工具支持仍在开发中)
加上其他。
注意最后两个?是的,您可以在 ASN.1 中定义数据结构、生成代码并以 XML 和 JSON 格式发出/使用消息。对于一项始于 1980 年代的技术来说还不错。
版本控制与 GPB 不同。您可以允许扩展:
Foo ::= SEQUENCE
{
bearing INTEGER (-180..180),
...
}
这意味着以后我可以添加到Foo
,并且具有此版本的旧系统仍然可以工作(但只能访问该bearing
字段)。
我对 ASN.1 的评价很高。处理起来可能很痛苦(工具可能要花钱,生成的代码不一定漂亮,等等)。但是限制是一个真正奇妙的功能,它一次又一次地为我节省了大量的心痛。当编码器/解码器报告他们生成了 duff 数据时,开发人员会抱怨很多。
其他链接:
观察
共享数据:
- 代码优先方法(例如 Boost 序列化)将您限制在原始语言(例如 C++),或者强迫您使用另一种语言做大量额外工作
- 首先架构更好,但是
- 其中很多在共享合同中留下了很大的空白(即没有约束)。GPB 在这方面很烦人,因为它在其他方面非常好。
- 有些有限制(例如 XSD、JSON),但受到不完整的工具支持。
- 例如,微软的 xsd.exe 主动忽略了 xsd 文件中的约束(MS 的借口实在是微不足道)。XSD 是好的(从约束的角度来看),但是如果你不能相信其他人会使用一个好的 XSD 工具来为他/她强制执行它们,那么 XSD 的价值就会降低
- JSON 验证器没问题,但它们一开始就没有帮助您形成 JSON,并且不会自动调用。无法保证向您发送 JSON 消息的人已经通过验证器运行了它。您必须记住自己验证它。
- ASN.1 工具似乎都实现了约束检查。
所以对我来说,ASN.1 做到了。它是最不可能导致其他人犯错的那个,因为它具有正确的功能,并且所有工具似乎都在努力完全实现这些功能,并且对于大多数目的来说它是语言中立的。
老实说,如果 GPB 添加了一个约束机制,那将是赢家。XSD 很接近,但这些工具几乎都是垃圾。如果有其他语言的不错的代码生成器,JSON 模式会非常好。
如果 GPB 添加了约束(注意:这不会改变任何有线格式),那将是我向每个人推荐的几乎所有目的的约束。虽然 ASN.1 的 uPER 对于无线电链路非常有用。
与工程领域的几乎所有事情一样,我的回答是……“这取决于。”
两者都是经过充分测试和审查的技术。两者都会获取您的数据并将其转换为适合发送到某个地方的数据。两者都可能足够快,如果你真的在这里或那里计算一个字节,你可能不会对任何一个感到满意(让我们面对它创建的数据包将是 XML 或 JSON 的一小部分)。
对我来说,这真的归结为工作流程,以及你是否需要 C++ 以外的东西。
如果您想首先弄清楚您的消息内容并且您正在从头开始构建系统,请使用协议缓冲区。您可以以抽象的方式考虑消息,然后以您想要的任何语言自动生成代码(几乎所有内容都可以使用 3rd 方插件)。此外,我发现使用 Protocol Buffers 简化了协作。我只需发送一个 .proto 文件,然后其他团队就可以清楚地了解正在传输的数据。我也不会对他们强加任何东西。如果他们想使用 Java,请继续!
如果我已经在 C++ 中构建了一个类(并且这种情况经常发生)并且我现在想通过网络发送该数据,那么 Boost 序列化显然很有意义(尤其是在我已经在其他地方有 Boost 依赖的情况下) )。
您可以将 boost 序列化与您的“真实”域对象紧密结合使用,并序列化完整的对象层次结构(继承)。Protobuf 不支持继承,因此您必须使用聚合。人们认为 Protobuf 应该用于 DTO(数据传输对象),而不是核心域对象本身。我同时使用了 boost::serialization 和 protobuf。应该考虑 boost::serialization 的性能,谷物可能是一种替代方案。