【发布时间】:2015-09-23 14:40:01
【问题描述】:
我目前正在使用 Google Protocol Buffers 重新访问一个项目。
在项目中我想利用Protocol Buffers的Descriptors和Reflection的特性。
官方文档说明.proto文件的cmets可以读取:
- 使用函数
DebugStringWithOptions(),在消息或描述符上调用。 - 使用函数
GetSourceLocation(),在描述符上调用。
我无法检索 cmets,所以我认为我做错了什么,或者该功能尚未在 Protocol Buffers 中完全实现。
这里有一些代码sn-ps:
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 文件中对 cmets 使用以下两种语法,
但它们似乎都不起作用:
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。
有人可以引导我走向正确的方向和/或提供一个可行的例子吗?
编辑 2015-09-24:
在整理了官方文档中的Self Describing Messages 部分并测试了很多东西之后,在我看来,我对protobuf 描述符有了更好的理解。
如果以下一项或多项陈述不正确,请纠正我:
-
SelfDescribingMessageproto仅在另一端不知道 .proto 定义的情况下有用。 - 访问 proto 定义的 cmets 的唯一方法是使用
protoc应用程序创建一个 .desc 文件。 - 要获得注释,只有当“top”元素是
FileDescriptorSet、FileDescriptorProto或FileDesriptor时,才能使用GetSourceLocation 成员函数。如果这是正确的,Protocol Buffers 的 API 设计很差,因为google::protobuf::Message类是上帝类(提供对完整文件描述符 API 的访问,但根本不提供值)。 - 调用
concrete_message.descriptor()->file()不(和不能)包含源 cmets 信息,因为它不是编译代码的一部分。
在我看来,完成这项工作的唯一方法是:
-
使用参数调用 Message.proto 文件(引用所有其他消息)的 protoc:
--include_imports --include_source_info and --descriptor_set_out=message.desc 将
message.desc文件与应用程序/库一起发送,以便能够在运行时读取它(见下文)。- 从该文件创建一个
google::protobuf::FileDescriptorSet。 - 遍历
FileDescriptorSet中的所有google::protobuf::FileDescriptorProto。 - 使用
google::protobuf::DescriptorPool::BuildFile()将每个 FileDescriptorProto 转换为google::protobuf::FileDescriptor。 - 使用
Find…函数之一查找消息和/或字段,应用于FileDescriptor实例。 - 在消息/字段描述符实例上调用函数
GetSourceLocation。 - 通过
google::protobuf::SourceLocation::leading_comments和google::protobuf::SourceLocation::trailing_comments阅读cmets。
这对我来说似乎很复杂,所以我还有两个问题:
- 有没有办法在不使用 FileDescriptorSet 的情况下包含源信息?
- 是否可以将
FileDescriptorSet与具体的 Message 类/实例“连接”/设置,因为这会大大简化事情?
EDIT 2015-09-25:God Class 我的意思是Message 类和/或描述符类提供了或多或少无用的公共函数,因为它们在客户使用时不提供任何信息。以“正常”消息为例:所以生成的代码确实不包含源注释信息,因此所有描述符类(例如Descriptor和FieldDescriptor)中的GetSourceLocation方法完全没用.从逻辑的角度来看,如果处理消息,则应提供单独的实例 DescriptorLite 和 FieldDescriptorLite,如果处理来自 FileDescriptorSet 的信息,则应提供 Descriptor 和 FieldDescriptor(其源通常是从 .原型文件)。然后,[...]Lite 类将成为“普通”类的父类。 protoc 可能永远不会包含源 cmets 的论点强调了我的观点。
“连接”是指使用 .desc 文件中的描述符信息(始终是消息提供的描述符的超集)更新消息中的描述符信息的 API 函数,如果我理解正确的话)。
【问题讨论】:
-
是否可能与Protocol Buffers Compiler
protoc有关?我刚刚偶然发现了protoc参数-o和--include_source_info。是否必须创建FileDescriptorSet才能检索 cmets?
标签: c++ c++11 protocol-buffers descriptor proto3