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";

原文地址:https://www.cnblogs.com/tsecer/p/10696330.html