• protobuf中extension的使用


    零、更新

    proto3中用法参考这个

    一、extension的用法

    由于extension是protobuf2中一个比较高级,但是在proto3中禁用的功能,所以在这里还是看下这个内容的实现,完整的实现参考来自下面文章。为了避免跳转或者连接失效,这里把原文章内容拷贝一份:

    proto文件
    package communication;

    message BaseMessage {
    required uint64 server_id = 1;
    required string uuid = 2;
    required uint64 message_id = 3;

    extensions 100 to max;
    }

    message GetIdentify {

    extend BaseMessage {
    optional GetIdentify message = 100;
    }

    required string hostname = 1;
    }

    使用代码
    communication::BaseMessage base_message;
    base_message.set_message_id(123456);
    base_message.set_server_id(112313123);
    base_message.set_uuid("asdaskdjasd213123123asd");
    base_message.MutableExtension(communication::GetIdentify::message)->set_hostname("http://test123123123ing");

    二、MutableExtension的定义在哪里

    从这个地方的注释也可以看到,如果一个message有"extension"声明,则有一个GOOGLE_PROTOBUF_EXTENSION_ACCESSORS宏在这个类的定义中,这个宏主要访问的是类的_extensions_成员。例如,使用前面生成的例子,可以看到有一个这种宏的定义。其中使用的_extensions_是一个PROTOBUF_NAMESPACE_ID::internal::ExtensionSet类型的定义。
    class BaseMessage final :
    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:communication.BaseMessage) */ {
    ……
    GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(BaseMessage)
    ……
    private:
    class HasBitSetters;

    // helper for ByteSizeLong()
    size_t RequiredFieldsByteSizeFallback() const;

    ::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
    ……
    }
    protobuf-mastersrcgoogleprotobufextension_set.h
    // Generated accessors

    // This macro should be expanded in the context of a generated type which
    // has extensions.
    //
    // We use "_proto_TypeTraits" as a type name below because "TypeTraits"
    // causes problems if the class has a nested message or enum type with that
    // name and "_TypeTraits" is technically reserved for the C++ library since
    // it starts with an underscore followed by a capital letter.
    //
    // For similar reason, we use "_field_type" and "_is_packed" as parameter names
    // below, so that "field_type" and "is_packed" can be used as field names.
    #define GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(CLASSNAME)
    /* Has, Size, Clear */
    template <typename _proto_TypeTraits,
    ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
    bool _is_packed>
    inline bool HasExtension(
    const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
    CLASSNAME, _proto_TypeTraits, _field_type, _is_packed>& id) const {
    return _extensions_.Has(id.number());
    }

    ……
    template <typename _proto_TypeTraits,
    ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
    bool _is_packed>
    inline typename _proto_TypeTraits::Singular::MutableType MutableExtension(
    const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
    CLASSNAME, _proto_TypeTraits, _field_type, _is_packed>& id) {
    return _proto_TypeTraits::Mutable(id.number(), _field_type,
    &_extensions_);
    }
    ……
    template <typename _proto_TypeTraits,
    ::PROTOBUF_NAMESPACE_ID::internal::FieldType _field_type,
    bool _is_packed>
    inline typename _proto_TypeTraits::Repeated::RepeatedFieldType*
    MutableRepeatedExtension(
    const ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier<
    CLASSNAME, _proto_TypeTraits, _field_type, _is_packed>& id) {
    return _proto_TypeTraits::MutableRepeated(id.number(), _field_type,
    _is_packed, &_extensions_);
    }

    三、派生类中使用的message是什么

    可以看到,这里定义的是一个比较复杂的类型生成的静态变量,配置前面的GOOGLE_PROTOBUF_EXTENSION_ACCESSORS定义的模版函数,可以将这里面包含的所有信息提取出来,其中比较关键的就是消息的类型,
    class GetIdentify final :
    public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:communication.GetIdentify) */ {
    public:
    ……
    static ::PROTOBUF_NAMESPACE_ID::internal::ExtensionIdentifier< ::communication::BaseMessage,
    ::PROTOBUF_NAMESPACE_ID::internal::MessageTypeTraits< ::communication::GetIdentify >, 11, false >
    message;
    ……
    };

    其中MessageTypeTraits类的定义:由于知道了扩展字段对应的数据类型,所以可以动态的创建这种类型的变量,并把它添加到集合中
    protobuf-mastersrcgoogleprotobufextension_set.h
    // ExtensionSet guarantees that when manipulating extensions with message
    // types, the implementation used will be the compiled-in class representing
    // that type. So, we can static_cast down to the exact type we expect.
    template <typename Type>
    class MessageTypeTraits {
    public:
    typedef const Type& ConstType;
    typedef Type* MutableType;
    ……
    static inline MutableType Mutable(int number, FieldType field_type,
    ExtensionSet* set) {
    return static_cast<Type*>(set->MutableMessage(
    number, field_type, Type::default_instance(), NULL));
    }

    四、field的具体创建

    其中的prototype.New(arena_)创建新的实例
    protobuf-mastersrcgoogleprotobufextension_set.cc
    MessageLite* ExtensionSet::MutableMessage(int number, FieldType type,
    const MessageLite& prototype,
    const FieldDescriptor* descriptor) {
    Extension* extension;
    if (MaybeNewExtension(number, descriptor, &extension)) {
    extension->type = type;
    GOOGLE_DCHECK_EQ(cpp_type(extension->type), WireFormatLite::CPPTYPE_MESSAGE);
    extension->is_repeated = false;
    extension->is_lazy = false;
    extension->message_value = prototype.New(arena_);
    extension->is_cleared = false;
    return extension->message_value;
    } else {
    GOOGLE_DCHECK_TYPE(*extension, OPTIONAL_FIELD, MESSAGE);
    extension->is_cleared = false;
    if (extension->is_lazy) {
    return extension->lazymessage_value->MutableMessage(prototype);
    } else {
    return extension->message_value;
    }
    }
    }

    内部维护map结构,根据字段的ID作为键值来查找某个类型的字段是否已经创建
    std::pair<ExtensionSet::Extension*, bool> ExtensionSet::Insert(int key) {
    if (PROTOBUF_PREDICT_FALSE(is_large())) {
    auto maybe = map_.large->insert({key, Extension()});
    return {&maybe.first->second, maybe.second};
    }
    KeyValue* end = flat_end();
    KeyValue* it =
    std::lower_bound(flat_begin(), end, key, KeyValue::FirstComparator());
    if (it != end && it->first == key) {
    return {&it->second, false};
    }
    if (flat_size_ < flat_capacity_) {
    std::copy_backward(it, end, end + 1);
    ++flat_size_;
    it->first = key;
    it->second = Extension();
    return {&it->second, true};
    }
    GrowCapacity(flat_size_ + 1);
    return Insert(key);
    }

    五、不同类型的field如何放在一个数组结构中

    由于所有非基础类型都是派生自MessageLite,所以其中的message_value就是一个MessageLite指针
    protobuf-mastersrcgoogleprotobufextension_set.h
    struct Extension {
    // The order of these fields packs Extension into 24 bytes when using 8
    // byte alignment. Consider this when adding or removing fields here.
    union {
    int32 int32_value;
    int64 int64_value;
    uint32 uint32_value;
    uint64 uint64_value;
    float float_value;
    double double_value;
    bool bool_value;
    int enum_value;
    std::string* string_value;
    MessageLite* message_value;
    LazyMessageExtension* lazymessage_value;

    RepeatedField<int32>* repeated_int32_value;
    RepeatedField<int64>* repeated_int64_value;
    RepeatedField<uint32>* repeated_uint32_value;
    RepeatedField<uint64>* repeated_uint64_value;
    RepeatedField<float>* repeated_float_value;
    RepeatedField<double>* repeated_double_value;
    RepeatedField<bool>* repeated_bool_value;
    RepeatedField<int>* repeated_enum_value;
    RepeatedPtrField<std::string>* repeated_string_value;
    RepeatedPtrField<MessageLite>* repeated_message_value;
    };

    六、自定义option

    在proto3中依然可以使用这个添加通用结构中的属性,但是由于扩展的基础结构使用的是proto2语法,所以没什么特殊之处:
    protobuf-mastersrcgoogleprotobufdescriptor.proto
    // The messages in this file describe the definitions found in .proto files.
    // A valid .proto file can be translated directly to a FileDescriptorProto
    // without any other information (e.g. without reading its imports).


    syntax = "proto2";

    package google.protobuf;
    option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";

  • 相关阅读:
    《Maven实战》第6章 仓库
    Log4j2中的同步日志与异步日志
    CSDN VIP如何添加自定义栏目
    CSDN VIP如何添加引流自定义栏目
    kubernetes垃圾回收器GarbageCollector Controller源码分析(二)
    java图形验证码实现
    java这个404你能解决吗?
    innodb笔记
    java项目启动脚本
    服务器初始化脚本
  • 原文地址:https://www.cnblogs.com/tsecer/p/10696330.html
Copyright © 2020-2023  润新知