美文网首页
protoc go插件编写之三 (自定义选项)

protoc go插件编写之三 (自定义选项)

作者: 癞痢头 | 来源:发表于2021-03-15 14:24 被阅读0次

    选项 [options]

    .proto文件中的各个声明【message呀,service呀等等】可以使用许多选项进行注释。选项不会改变声明的整体含义,但可能会影响在特定上下文中处理声明的方式。

    可用选项的完整列表在google/protobuf/descriptor.proto中定义。

    一些选项是文件级别的, 只能在顶级内容区, 而不能在任何 message, 或enum,或service中定义。一些是消息级别的, 只能定义在message内, 一些是字段级别的,只能定义在filed内。选项也可以写在枚举类型,枚举值,字段,服务类型和服务方法中;但是,目前尚无任何针对这些选项的有用选项。

    • file level options

      • 例1: go_package 想要的golang中使用的包名
      options go_package = github.com/kekek/test; 
      
      • 例2: optimize_for, 可以设置为 SPEED, CODE_SIZE, 或 LITE_RUNTIME, 只对C++ 或者java起作用
      option optimize_for = CODE_SIZE;
      
    • message level options

      例1:message_set_wire_format, 如果设置为true,则消息使用另一种二进制格式,旨在与Google内部使用的旧格式MessageSet兼容。 Google外部的用户可能永远不需要使用此选项。

      message Foo {
          option message_set_wire_format = true;
          extensions 4 to max;
      }
      
      
    • filed options

      例1: packed, 如果在基本数字类型的重复字段上设置为true,则使用更紧凑的编码。使用此选项没有任何弊端。但是,请注意,在版本2.3.0之前,解析器在不期望收到打包数据时将忽略该数据。因此,不可能在不破坏电线兼容性的情况下将现有字段更改为打包格式。在2.3.0及更高版本中,此更改是安全的,因为可打包字段的解析器将始终接受两种格式,但是如果必须使用旧的protobuf版本处理旧程序,请务必小心。

      repeated int32 samples = 4 [packed=true];
      

      例2: deprecated, 如果设置为true,则表明该字段已弃用,并且不应由新代码使用。在大多数语言中,这没有实际效果。在Java中,这成为@Deprecated批注。

      optional int32 old_field = 6 [deprecated=true];
      

    自定义选项 [Custom Options]

    Protocol Buffers 允许自定义选项,这是大多数人用不到的功能, 且自定义选项仅支持proto3。

    由于"选项" 是由 google/protobuf/descriptor.proto中定义的消息定义的(例如FileOptions或FieldOptions),因此自定义选项仅是扩展这些消息而已。

    例如:

    import "google/protobuf/descriptor.proto";
    
    extend google.protobuf.MessageOptions {
      optional string my_option = 51234;
    }
    
    message MyMessage {
      option (my_option) = "Hello world!";
    }
    

    在这里,我们通过扩展MessageOptions定义了一个新的消息级选项。当我们使用选项时,选项名称必须用括号括起来以表明它是扩展名。我们现在可以像这样在C++中读取my_option的值:

    
    string value = MyMessage::descriptor()->options().GetExtension(my_option);
    
    

    在这里,MyMessage :: descriptor()-> options()返回MyMessage的MessageOptions协议消息。从中读取自定义选项就像读取任何其他扩展名一样。

    在protobuf protocol 中,可以为每种构造定义自定义选项。这是使用各种选项的示例:

    import "google/protobuf/descriptor.proto";
    
    extend google.protobuf.FileOptions {
      optional string my_file_option = 50000;
    }
    extend google.protobuf.MessageOptions {
      optional int32 my_message_option = 50001;
    }
    extend google.protobuf.FieldOptions {
      optional float my_field_option = 50002;
    }
    extend google.protobuf.OneofOptions {
      optional int64 my_oneof_option = 50003;
    }
    extend google.protobuf.EnumOptions {
      optional bool my_enum_option = 50004;
    }
    extend google.protobuf.EnumValueOptions {
      optional uint32 my_enum_value_option = 50005;
    }
    extend google.protobuf.ServiceOptions {
      optional MyEnum my_service_option = 50006;
    }
    extend google.protobuf.MethodOptions {
      optional MyMessage my_method_option = 50007;
    }
    
    option (my_file_option) = "Hello world!";
    
    message MyMessage {
      option (my_message_option) = 1234;
    
      optional int32 foo = 1 [(my_field_option) = 4.5];
      optional string bar = 2;
      oneof qux {
        option (my_oneof_option) = 42;
    
        string quux = 3;
      }
    }
    
    enum MyEnum {
      option (my_enum_option) = true;
    
      FOO = 1 [(my_enum_value_option) = 321];
      BAR = 2;
    }
    
    message RequestType {}
    message ResponseType {}
    
    service MyService {
      option (my_service_option) = FOO;
    
      rpc MyMethod(RequestType) returns(ResponseType) {
        // Note:  my_method_option has type MyMessage.  We can set each field
        //   within it using a separate "option" line.
        option (my_method_option).foo = 567;
        option (my_method_option).bar = "Some string";
      }
    }
    
    
    

    请注意,如果要在自定义选项之外的其他程序包中使用自定义选项,则必须在选项名称前加上程序包名称,就像键入类型名称一样。例如:

    • foo.proto
    import "google/protobuf/descriptor.proto";
    package foo;
    extend google.protobuf.MessageOptions {
      optional string my_option = 51234;
    }
    
    • bar.proto
    import "foo.proto";
    package bar;
    message MyMessage {
      option (foo.my_option) = "Hello world!";
    }
    

    最后一件事:由于自定义选项是扩展名,因此必须像其他任何字段或扩展名一样为它们分配字段编号。在上面的示例中,我们使用了范围为50000-99999的字段号。此范围保留供各个组织内部使用,因此您可以在内部应用程序中自由使用此范围内的数字。但是,如果您打算在公共应用程序中使用自定义选项,那么确保您的字段编号在全球范围内是唯一的,这一点很重要。要获取全局唯一的字段号,请发送请求以将条目添加到protobuf全局扩展注册表中。通常,您只需要一个分机号码。您可以通过将多个选项放在一个子消息中来声明多个仅具有一个分机号的选项:

    message FooOptions {
      optional int32 opt1 = 1;
      optional string opt2 = 2;
    }
    
    extend google.protobuf.FieldOptions {
      optional FooOptions foo_options = 1234;
    }
    
    // usage:
    message Bar {
      optional int32 a = 1 [(foo_options).opt1 = 123, (foo_options).opt2 = "baz"];
      // alternative aggregate syntax (uses TextFormat):
      optional int32 b = 2 [(foo_options) = { opt1: 123 opt2: "baz" }];
    }
    
    

    另外,请注意,每个选项类型(文件级,消息级,字段级等)都有自己的数字空间,例如您可以使用相同的编号声明FieldOptions和MessageOptions的扩展名。

    参考文献:

    相关文章

      网友评论

          本文标题:protoc go插件编写之三 (自定义选项)

          本文链接:https://www.haomeiwen.com/subject/zaskcltx.html