有没有办法在C ++中创建包含预编码内部消息的协议缓冲区消息,而无需解析然后重新序列化内部消息?
为澄清一下,请考虑以下消息定义:
message Inner {
required int i = 1;
// ... more fields ...
}
message Outer {
repeated Inner inners = 1;
// ... more fields ...
}
假设您有一个10字节数组的集合,每个数组都包含一个Inner的编码版本。你想创建一个包含10个内部的外部。您不想手动编码,因为Outer有其他字段,并且可能本身也包含在其他消息中。有没有办法让协议缓冲区直接复制预编码的内部?
答案 0 :(得分:1)
没有一个干净的方式,但有一些hacky方式。一种是定义第二条消息:
message RawOuter {
repeated bytes inners = 1;
// ... same fields as Outer ...
}
RawOuter
与Outer
相同,只是inners
重复字段已从类型Inner
更改为类型bytes
。如果使用编码的inners
实例填充Inner
,然后序列化RawOuter
,则会得到与使用已解析的版本生成Outer
完全相同的结果。也就是说,嵌套消息的有线格式与包含该嵌套消息序列化的bytes
字段的有线格式相同。这是protobuf编码的一些有趣的可利用怪癖。
Outer
实例,则它不能很好地工作,因为您可能不希望维护每个包含消息的两个副本,一个使用{ {1}}和使用Outer
的人。
另一个甚至更黑的选择是将编码的消息注入RawOuter
实例的Outer
。
UnknownFieldSet
Outer outer;
for (auto& inner: inners) {
outer.mutable_unknown_fields()
->AddLengthDelimited(1, inner);
}
旨在存储解析时看到的与UnknownFieldSet
文件中定义的任何已知字段编号不匹配的字段。这个想法是,这允许您编写一个代理服务器,只需接收消息并将它们转发到另一个服务器,而无需在每次向协议添加新字段时重新编译代理。在这里,我们通过在其中添加一个实际上与已知字段对应的值来滥用它,但是实现不会注意到,因此它会写出这些字段就好了。
这种方法的主要问题是,如果其他人在此期间检查您的.proto
实例,那么它们就会显示为Outer
列表为空,因为这些值实际上隐藏在某个地方其他。这是一个非常丑陋的黑客,可能会在以后再次困扰你。如果你测量了性能差异并发现它很大,我只会推荐它。
另请注意,序列化代码始终最后写入未知字段,而已知字段按字段编号按顺序写入。解析器应该接受任何订单,但偶尔你会发现有人使用未解析的数据作为哈希映射键或其他东西,并且如果字段被重新排序则完全中断。
顺便说一句,你可以通过将字符串交换到位而不是复制来提高这两种方法的性能,即
inners
或
raw_outer->add_inners()->swap(inner);