因爲extension是protobuf2中一個比較高級,可是在proto3中禁用的功能,因此在這裏仍是看下這個內容的實現,完整的實現參考來自下面文章。爲了不跳轉或者鏈接失效,這裏把原文章內容拷貝一份:git
proto文件
package communication;github
message BaseMessage {
required uint64 server_id = 1;
required string uuid = 2;
required uint64 message_id = 3;golang
extensions 100 to max;
}api
message GetIdentify {數組
extend BaseMessage {
optional GetIdentify message = 100;
}ide
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");ui
從這個地方的註釋也能夠看到,若是一個message有"extension"聲明,則有一個宏在這個類的定義中,例如,使用前面生成的例子,能夠看到有一個這種宏的定義。其中使用的_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;this
// helper for ByteSizeLong()
size_t RequiredFieldsByteSizeFallback() const;google
::PROTOBUF_NAMESPACE_ID::internal::ExtensionSet _extensions_;
……
}
protobuf-master\src\google\protobuf\extension_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_); \
}
能夠看到,這裏定義的是一個比較複雜的類型生成的靜態變量,配置前面的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-master\src\google\protobuf\extension_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));
}
其中的prototype.New(arena_)建立新的實例
protobuf-master\src\google\protobuf\extension_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);
}
因爲全部非基礎類型都是派生自MessageLite,因此其中的message_value就是一個MessageLite指針
protobuf-master\src\google\protobuf\extension_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;
};
在proto3中依然可使用這個添加通用結構中的屬性,可是因爲擴展的基礎結構使用的是proto2語法,因此沒什麼特殊之處:
protobuf-master\src\google\protobuf\descriptor.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";