【问题标题】:How to dynamically build a new protobuf from a set of already defined descriptors?如何从一组已定义的描述符中动态构建新的 protobuf?
【发布时间】:2012-08-13 08:34:09
【问题描述】:

在我的服务器上,我们收到自我描述的消息(定义为 here... 顺便说一句,这并不容易,因为在 c++ 中没有任何“好的”示例)。

在这一点上,我从这些自我描述的消息中创建消息没有问题。我可以使用 FileDescriptorSet,遍历每个 FileDescriptorProto,将每个添加到 DescriptorPool(使用 BuildFile,这也给了我每个定义的 FileDescriptor)。

从这里我可以创建在 FileDescriptorSet 中定义的任何消息,其中使用 DP 实例化的 DynamicMessageFactory 并调用 GetPrototype(这很容易做到,因为我们的 SelfDescribedMessage 需要消息 full_name(),因此我们可以调用 DP 的FindMessageTypeByName 方法,为我们提供正确编码的消息原型)。

问题是如何获取每个已定义的描述符或消息并动态构建一个“主”消息,其中包含所有已定义消息作为嵌套消息。这主要用于保存消息的当前状态。目前我们只是通过在服务器中实例化每条消息的一种类型来处理这个问题(以保持不同程序的中心状态)。但是当我们想要“保存”当前状态时,我们不得不按照here 的定义将它们流式传输到磁盘。它们一次流式传输一条消息(带有大小前缀)。我们希望有一条消息(一条来统治所有消息)而不是稳定的单独消息流。一旦解决,这可以用于其他事情(基于网络的共享状态,经过优化且易于序列化)

由于我们已经有了交叉链接和定义的描述符,人们会认为有一种简单的方法可以从那些已经定义的消息中构建“新”消息。到目前为止,解决方案已经暗示了我们。我们已经尝试创建自己的 DescriptorProto 并从我们已经定义的描述符中添加新的类型字段,但是迷路了(还没有深入研究这个)。我们还研究了可能将它们添加为扩展(目前未知如何这样做)。我们是否需要创建自己的DescriptorDatabase(目前也不知道该怎么做)?

有什么见解吗?


在 BitBucket 上链接 example source


希望这个解释会有所帮助。

我正在尝试从一组已定义的消息中动态构建消息。已经定义的消息集是通过使用官方 c++ protobuf 教程中解释的“自我描述”方法(简要地)创建的(即这些消息以编译形式不可用)。这个新定义的消息需要在运行时创建。

已尝试对每条消息使用直接描述符并尝试构建 FileDescriptorProto。尝试查看 DatabaseDescriptor 方法。两者都没有运气。当前尝试将这些定义的消息作为扩展添加到另一条消息(即使在编译时这些定义的消息,并且它们的“描述符集”未被归类为扩展任何内容),这是示例代码开始的地方。

【问题讨论】:

  • 哇...甚至没有评论...这是我目前所处的位置。这是我唯一拥有公共源代码的...它现在显然无法编译(直到第一次创建 ExtensionSet 的最后为止一切都很好)...目前正尝试像另一个一样走扩展路线到目前为止,有两个让我失望了。 goo.gl/VJhnk
  • 我目前遇到的问题是扩展标识符的初始化。我需要一个类来将 MessageTypeTraits 指向一个描述消息类型的类(可能必须做我自己的模板魔术?)但到目前为止还没有成功......
  • 老实说,我把你的问题读了 3 遍,仍然无法理解你在描述什么。我想大多数读者都会遇到这种情况,这就是你没有得到回复的原因。你确实需要简化一些东西。此外,你真的感觉自己在构建一些过于复杂的东西,而其中可能有一个更简单的解决方案。
  • 描述基本上是尽可能的简单。现在包含的源代码也是我所描述的非常精简的版本。我当然对有关更简单解决方案的建议感兴趣。
  • 使用原型反序列化消息后,您可以用它做什么有用的事情?一个大的 if-else if-else if 构造来弄清楚如何将其转换回原始类型?

标签: c++ dynamic protocol-buffers


【解决方案1】:

你需要一个protobuf::DynamicMessageFactory:

{
  using namespace google;

  protobuf::DynamicMessageFactory dmf;
  protobuf::Message* actual_msg = dmf.GetPrototype(some_desc)->New();

  const protobuf::Reflection* refl = actual_msg->GetReflection();

  const protobuf::FieldDescriptor* fd = trip_desc->FindFieldByName("someField");
  refl->SetString(actual_msg, fd, "whee");

  ... 

  cout << actual_msg->DebugString() << endl;
}

【讨论】:

    【解决方案2】:

    我能够通过动态创建一个 .proto 文件并使用 Importer 加载它来解决这个问题。

    唯一的要求是每个客户端都通过其 proto 文件发送(仅在初始化时需要...而不是在完整执行期间)。然后服务器将每个 proto 文件保存到临时目录。如果可能的话,另一种方法是将服务器指向一个包含所有需要的 proto 文件的中心位置。

    这是通过首先使用 DiskSourceTree 将实际路径位置映射到程序中的虚拟位置来完成的。然后构建 .proto 文件以导入发送的每个 proto 文件,并在“主消息”中定义一个可选字段。

    master.proto 保存到磁盘后,我使用 Importer 将其导入。现在使用 Importers DescriptorPool 和 DynamicMessageFactory,我能够可靠地在一条消息下生成整个消息。今晚或明天晚些时候,我将举例说明我所描述的内容。

    如果有人对如何改进此过程或如何使其与众不同有任何建议,请说出来。

    在赏金即将到期之前,我将不回答这个问题,以防其他人有更好的解决方案。

    【讨论】:

    • 你有一个例子说明你是如何实现的吗?
    【解决方案3】:

    如何将所有消息序列化为字符串,并使主消息成为(字节)字符串序列,一个拉

    message MessageSet
    {
      required FileDescriptorSet proto_files = 1;
      repeated bytes serialized_sub_message = 2;
    }
    

    【讨论】:

    • @g19fanatic:如果这不符合您的要求,您能否说明您想要实现的目标是什么?
    • 这正是我们目前正在做的方式,但问题在于快速解析该组消息。因为消息之间没有固有的分隔符,我们将每条消息的大小存储在一个 uint32 前缀中。我们使用这个前缀来单独解析出每条消息。我们试图做的是只有一条消息,然后我们将其序列化。当解析它的时候,它只是一个调用而不是重复的 getnextsize,解析下一条消息,重复。
    • 对,所以将编码部分交给协议缓冲区,serialized_sub_messagerepeated。所以它仍然是一个循环(dispatch_serialized_message(message_set.serialized_sub_message(i))),但你不必处理编码到线路的细节。
    • 对于我们目前的方法,我还没有考虑过这一点,这将解决需要详细信息的问题,谢谢。但我仍然必须解析每条消息,而不仅仅是解析一条。
    • 这是一个缓慢的解决方案,你必须反序列化两次。看看 protobuf 多态是如何实现的:indelible.org/ink/protobuf-polymorphism
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多