使用 Protocol Buffers 描述符对象从 .proto 文件中读取注释

Reading comments from .proto files using a Protocol Buffers descriptor object

我目前正在使用 Google Protocol Buffers.

重新访问一个项目

在项目中我想利用 Protocol Buffers 的特性 DescriptorsReflection

官方文档说可以阅读.proto个文件的注释:

  1. 使用函数 DebugStringWithOptions(),在消息或描述符上调用。
  2. 使用函数 GetSourceLocation(),在描述符上调用。

我无法检索评论,所以我认为我做错了一些事情,或者该功能尚未在 Protocol Buffers 中完全实现。

以下是一些代码片段:

google::protobuf::DebugStringOptions options;
options.include_comments = true;
std::cout << "google::protobuf::Descriptor::DebugStringWithOptions(): "
          << message.descriptor()->DebugStringWithOptions(options) << std::endl
          << std::endl;

const google::protobuf::FieldDescriptor* field_descriptor{
    message.descriptor()->field(1)};

// TODO(wolters): Why doesn't this work?
google::protobuf::SourceLocation* source_location{
    new google::protobuf::SourceLocation};
field_descriptor->GetSourceLocation(source_location);

// if (field_descriptor->GetSourceLocation(source_location)) {
std::cout << "start_line: " << source_location->leading_comments
          << std::endl;
std::cout << "end_line: " << source_location->leading_comments << std::endl;
std::cout << "start_column: " << source_location->start_column << std::endl;
std::cout << "end_column: " << source_location->end_column << std::endl;
std::cout << "leading_comments: " << source_location->leading_comments
          << std::endl;
std::cout << "trailing_comments: " << source_location->trailing_comments
          << std::endl;
// }

我已经尝试使用以下两种语法在 .proto 文件中进行注释, 但其中 none 似乎有效:

MessageHeader header = 1;  // The header of this `Message`.

/**
 * The header of this `Message`.
 */
MessageHeader header = 1;

我正在使用 GCC 4.7.1(启用了 C++11 支持)和最新的 Protocol Buffers 版本 3.0.0-alpha-4.1。

谁能指导我正确的方向and/or给我提供一个工作示例?

编辑 2015-09-24:

在阅读了官方文档中的 Self Describing Messages 部分并测试了很多东西之后,在我看来,我对 protobuf 描述符有了更好的理解。

如果以下一个或多个陈述不正确,请纠正我:

  1. 如果另一端不知道 .proto 定义,SelfDescribingMessage proto 有用。
  2. 访问原型定义注释的唯一方法是使用 protoc 应用程序创建一个 .desc 文件。
  3. 要获取评论,GetSourceLocation 成员函数只能在 "top" 元素为 FileDescriptorSetFileDescriptorProtoFileDesriptor 时使用。如果这是正确的,Protocol Buffers 的 API 设计很差,因为 google::protobuf::Message class 是神 Class(提供对完整文件描述符的访问 API ,但根本没有提供值)。
  4. 调用concrete_message.descriptor()->file()不(不能)包含源注释信息,因为它不是编译代码的一部分。

在我看来,完成这项工作的唯一方法是:

  1. 使用以下参数为 theMessage.proto 文件(引用所有其他消息)调用协议:

    --include_imports --include_source_info and --descriptor_set_out=message.desc
    
  2. message.desc 文件与 application/library 一起发送,以便能够在运行时读取它(见下文)。

  3. 从该文件创建 google::protobuf::FileDescriptorSet
  4. 遍历 FileDescriptorSet 的所有 google::protobuf::FileDescriptorProto
  5. 使用 google::protobuf::DescriptorPool::BuildFile().
  6. 将每个 FileDescriptorProto 转换为 google::protobuf::FileDescriptor
  7. 使用应用于 FileDescriptor 实例的 Find… 函数之一查找消息 and/or 字段。
  8. 在 message/field 描述符实例上调用函数 GetSourceLocation
  9. 通过 google::protobuf::SourceLocation::leading_commentsgoogle::protobuf::SourceLocation::trailing_comments 阅读评论。

这对我来说似乎很复杂,所以我还有两个问题:

  1. 没有不使用 FileDescriptorSet 就可以包含源信息的方法吗?
  2. 是否可以 "connect"/设置 FileDescriptorSet 使用具体的消息 class/instance,因为那样会大大简化事情?

编辑 2015-09-25: 上帝 Class 我的意思是 Message class and/or 描述符 classes 提供 public 或多或少无用的功能,因为它们在客户端使用时不提供任何信息。以"normal"消息为例: 所以生成的代码包含源注释信息,因此所有描述符classes中的GetSourceLocation方法(例如 DescriptorFieldDescriptor) 是完全没用的。从逻辑角度来看,如果处理消息,应提供单独的实例 DescriptorLiteFieldDescriptorLite,如果处理来自 FileDescriptorSet 的信息(其来源通常是从 .proto 文件生成的 .desc 文件)。 [...]Lite class 将成为 "normal" class 的父 class。 protoc 可能永远不会包含来源注释的论点强调了我的观点。

"connecting",我的意思是一个 API 函数来 更新 消息中的描述符信息和来自 .desc 文件的描述符信息(它如果我理解正确的话,它始终是消息提供的描述符的超集)。

看来你已经基本明白了。

您正在深入了解协议编译器中的 API,这些 API 并非真正为 public 消费而设计。它变得复杂,因为没有人编写帮助层来简化事情,因为没有多少人使用这些功能。

我不确定你所说的 Message 是 "God class" 是什么意思。 Message 只是 protobuf 实例的抽象接口。描述符描述了 类型 的 protobuf 实例。 Message::getDescriptor() return是消息的类型,但除此之外,这些 API 之间没有太多直接联系...

Isn't there a way to include the source information without using a FileDescriptorSet?

有意从嵌入到生成代码中的描述符中剥离注释,因此您需要单独 运行 解析器,生成描述符集,并动态使用它。

Is it possible to "connect"/set the FileDescriptorSet with a concrete Message class/instance, since that would drastically simplify things?

你的意思是你想要 Message::getDescriptor() 到 return 一个包含来自源文件的评论数据的描述符?这将需要将评论数据嵌入到生成的代码中,这对于 protoc 的实现来说是微不足道的(它目前有意将它们删除,因此它只需要 而不是 这样做)但可能会膨胀和危险(可能会泄露使用 protobufs 构建的闭源二进制文件的人的秘密)。