【问题标题】:Protobuf message object creation by name按名称创建 Protobuf 消息对象
【发布时间】:2015-04-30 06:49:07
【问题描述】:

我有很多 protobuf 消息,我目前使用手动编写的查找函数通过其名称生成消息。 由于随着项目的发展消息越来越多,我已经厌倦了手动维护这个查找代码。

那么,有没有办法使这个过程自动化? 也许使用 protoc 插件向 protobuf 代码添加一些代码以便它可以注册自己?

【问题讨论】:

  • 有一种使用消息描述符的机制,它提供通过使用名称字符串 IIRC 创建消息类型和字段。

标签: c++ protocol-buffers


【解决方案1】:

C++ Protobuf 库已经为编译到二进制文件中的所有类型维护了一个“描述符”池。

https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.descriptor#DescriptorPool.generated_pool.details

所以,你可以这样做:

google::protobuf::Descriptor* desc =
    google::protobuf::DescriptorPool::generated_pool()
        ->FindMessageTypeByName("mypkg.MyType");
assert(desc != NULL);

该库还维护一个对象,该对象可用于构造任何已编译类型的实例,给定它的描述符:

https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message#MessageFactory.generated_factory.details

所以你会这样做:

google::protobuf::Message* message =
    google::protobuf::MessageFactory::generated_factory()
        ->GetPrototype(desc)->New();

【讨论】:

  • 如下所述,要使 Protobuf 反射工作,必须加载描述符。这通常在第一个消息创建时完成。因此,为了FindMessageTypeByName 工作,必须至少为每个命名空间(protobuf 包)实例化一条消息。像这样:my_package::messages::SuccessMessage::default_instance()。然后查找适用于 my_package::messages 命名空间。
  • @ph4r05 这不是真的。所有描述符都在启动时在全局构造函数中进行索引。我编写了 C++ protobuf 实现。
  • 有趣...我的代码显然没有注册任何描述符,因此 FindMessageTypeByName 对来自给定 protobuf package / 命名空间的所有消息都失败了。我尝试在启动时解决该消息,因此之前没有显式实例化任何消息。描述符在程序生命周期中的准确时间是什么时候注册的?我从不同的库中调用 FindMessageTypeByName(与 protobuf 消息所在的位置相比),所以在这种情况下可能不调用构造函数?或者从调用 lib 的上下文中看不到?我之前也遇到过类似的问题...
  • 会不会与:stackoverflow.com/a/1271692/1378053 相关?即,初始化可以推迟到第一次使用翻译单元中的任何函数/对象?
  • @ph4r05 该标准允许将全局构造函数推迟到翻译单元的第一个条目,但实际上我相信在所有常见平台上它们都是在可执行加载时调用的。但是,也许可执行文件本身——如果它是一个共享库——是延迟加载的?我在 Linux 上没有经历过这种行为,但我当然可以想象它因操作系统而异,甚至基于链接器标志。
【解决方案2】:

这种方法不太可能不能用作创建任何消息实例的通用方法。 generated_pool() 中的消息类型描述仅在这种类型的消息至少被实例化一次之后(例如在MyMessageType* msg = new MyMessageType() 的时刻)出现,因此FindMessageTypeByName 永远不会找到尚未实例化的消息的消息类型还没有。

【讨论】:

  • 这不是真的。所有编译的描述符都在启动时从全局构造函数中索引。我写了这段代码。你是否以某种方式禁用了全局构造函数,或者让它们变得懒惰?
【解决方案3】:

我想对上述回复之一添加评论,但因为我没有足够的声誉计数,所以我将其添加为答案。寻求赦免。

我正在使用协议缓冲区 3.6.1,我注意到生成的 .pb.cc 文件中的一些代码可能与 Kenton 所指的内容有关。

namespace protobuf_foo_5fcp_5fplayer_5fcommon_5fevent_5ftypes_2eproto {
void InitDefaults() {
}
//...
//...

// Force AddDescriptors() to be called at dynamic initialization time.
struct StaticDescriptorInitializer {
  StaticDescriptorInitializer() {
    AddDescriptors();
  }
} static_descriptor_initializer;
}  // namespace protobuf_foo_5fcp_5fplayer_5fcommon_5fevent_5ftypes_2eproto

似乎从未调用过全局变量 static_descriptor_initializer。 我通过如下修改代码并验证向 cout 引入的消息从未被调用过来发现这一点!

//...
//...

#include <iostream>

// Force AddDescriptors() to be called at dynamic initialization time.
struct StaticDescriptorInitializer {
  StaticDescriptorInitializer() {
    AddDescriptors();
    std::cout << "##################> DESCRIPTORS ADDED\n";
  }
} static_descriptor_initializer;

现在我想我必须找出 g++(我正在使用的)中是否有一个选项可以在应用程序启动序列期间构造“static_descriptor_initializer”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-15
    • 2022-08-05
    • 2020-05-10
    • 1970-01-01
    • 2016-11-22
    • 2013-05-26
    相关资源
    最近更新 更多