使用Protocol Buffers描述符对象

时间:2015-09-23 14:40:02

标签: c++ c++11 protocol-buffers descriptor proto3

我目前正在使用Google Protocol Buffers重新审视一个项目。

在项目中,我想利用协议缓冲区的 Descriptors Reflection 功能。

官方文档指出.proto文件的注释可以读取:

  1. 使用函数DebugStringWithOptions(),调用消息或描述符。
  2. 使用函数GetSourceLocation(),调用描述符。
  3. 我无法检索评论,因此我认为我做的事情完全错误,或者该功能尚未在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文件中使用以下两种语法进行评论, 但它们似乎都不起作用:

    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描述符有了更好的理解。

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

    1. 如果另一端不知道.proto定义,SelfDescribingMessage proto 有用。
    2. 访问proto定义注释的唯一方法是使用protoc应用程序创建.desc文件。
    3. 要获取注释,只有在&#34; top&#34;时才能使用GetSourceLocation成员函数。元素可以是FileDescriptorSetFileDescriptorProtoFileDesriptor。如果这是正确的,协议缓冲区的API设计很差,因为google::protobuf::Message类是神类(提供对完整文件描述符API的访问,但根本不提供这些值)。
    4. 调用concrete_message.descriptor()->file()没有(不能)包含源注释信息,因为它不是编译代码的一部分。
    5. 在我看来,完成这项工作的唯一方法是:

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

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

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

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

        1. 是否有办法在不使用FileDescriptorSet的情况下包含源信息?
        2. 是否可以&#34; connect&#34; /将FileDescriptorSet设置为具体的Message类/实例,因为这会大大简化事情?
        3. 编辑2015-09-25:通过上帝类我的意思是Message类和/或描述符类提供更多或更多的公共函数不那么无用,因为它们在客户端使用时不提供任何信息。采取正常的&#34;消息例如:因此生成的代码包含源注释信息,因此所有描述符类(例如GetSourceLocationDescriptor)中的FieldDescriptor方法完全是无用。从逻辑角度来看,如果处理消息,则应提供单独的实例DescriptorLiteFieldDescriptorLite;如果处理来自Descriptor的信息,则应提供FieldDescriptorFileDescriptorSet通常是.proto文件生成的.desc文件)。然后[...]Lite类将成为&#34; normal&#34;的父类。类。 protoc可能永远不会包含源注释的论点强调了我的观点。

          通过&#34;连接&#34;,我的意思是一个API函数,用更新消息中的描述符信息,其中包含来自.desc文件的描述符信息(它始终是.desc文件的超集消息提供的描述符,如果我理解正确的话。)

1 个答案:

答案 0 :(得分:1)

听起来你基本上已经弄明白了。

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

我不确定你对Message成为上帝班级&#34;的意思。 Message仅仅是protobuf实例的抽象接口。描述符描述了protobuf实例的类型Message::getDescriptor()会返回消息的类型,但除此之外,这些API之间没有多少直接连接...

  

是否有办法在不使用FileDescriptorSet的情况下包含源信息?

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

  

是否可以&#34; connect&#34; /将FileDescriptorSet设置为具体的Message类/实例,因为这会大大简化事情?

您是否希望Message :: getDescriptor()返回包含源文件中注释数据的描述符?这需要将注释数据嵌入到生成的代码中,这对于protoc实现来说是微不足道的(它目前有意剥离它们,所以它只需要那样做)但潜在的膨胀和危险(可能揭示运送使用protobufs构建的闭源二进制文件的人的秘密)。