ProtoBuf—— 反射原理解析
- ProtoBuf—— 反射原理
- 1、反射原理
- 1.1、反射机制的背景
- 1.2、定义
- 1.3、反射原理关注的一些问题
- 1.4、反射原理的优势和应用
- 2、ProtoBuf反射原理——获取并改造 元信息
- 2.1 、 .proto 文件
- 2.2 、 反射原理过程
- 2.3 、 反射相关的类和API
- 2.3.1、google::protobuf::Message
- 2.3.2、 google::protobuf::Descriptor
- 2.3.3、 google::protobuf::Reflection
- 2.3.4、 google::protobuf::FieldDescriptor
- 2.3.5、 其他相关接口
- 2.3.5.1、DescriptorPool(元信息池)
- 2.3.5.2、DescriptorDatabase( .proto 文件库)
- 2.3.5.3、MessageFactory( 实例工厂)
- 2.4 、 反射原理过程具体解析
- 2.4.1、获取元信息
- 2.4.2、创建和获取实例(查表)
- 2.4.3、实例对象的读写
- 2.5 、 反射原理具体实例介绍
ProtoBuf—— 反射原理
1、反射原理
1.1、反射机制的背景
有关某个外部世界表示”的计算过程, 并通过它来对那个外部世界进行推理; 那么我们也可以构造能够对自身表示和计算进行推理的计算过程,它包含负责管理有关自身的操作和结构表示的内部过程。
—— 1982年 Smith, Brian Cantwell 博士论文首次提出
- 所谓编程实际上就是在构造 “关于外部世界” 的计算过程。如果用
F
表示这个构造过程,用X
表示外部世界,那么编写一个计算系统可表示为F(X)
。 - 我们完全可以构造对上述过程本身进行描述和推理的计算过程。即将
F(X)
视为新的 “世界” 和 研究对象,构造F(F(X))
。
- 所以我们为了构建和改造世界,前提需要充分认识世界
计算机程序在运行时可以访问、检测和修改它本身状态或行为
—— 反射 (计算机科学) Wikipedia
我们平时编写的计算系统是面向特定领域的(通常是面向现实建模), 系统中包含 用来描述领域中的实体
和 实体间关系 的数据结构以及处理这些数据结构的规则
。那么反射系统面向领域便是这个系统本身。
1.2、定义
在对程序本身进行构建的同时,在运行时可以访问和修改自身状态和行为,这建立在对系统系统元信息充分感知的基础上。
系统元信息:
元信息:即系统自描述信息,用于描述系统本身。举例来讲,即系统有哪些类?类中有哪些字段、哪些方法?字段属于什么类型、方法又有怎样的参数和返回值?
反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
1.3、反射原理关注的一些问题
- 1、如何在程序运行过程中通过类型名字(一个字符串,合法但是内容在编译期间未知,比如是在配置文件中获取的)创建出类型对象.
- 2、如何在程序运行过程中通过对象和对象的属性的名字(一个字符串,合法但是内容在编译期间未知,比如是通过通讯包获取的)获取,修改对应属性.
- 3、如何在程序运行过程中通过对象和对象方法的名字(一个字符串,合法但是内容在编译期间未知,比如是从用户输入获取的)调用对应的方法.
1.4、反射原理的优势和应用
优势:
反射在运行时检测或修改程序行为的程序中,提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类.
应用:
- 1、多并发的服务器框架
- 2、网络序列化数据传输方法等
- 3、微软的MFC
在开发编辑器的阶段,并没有相应的类,但是使用者想通过在编辑器中传入类的名字,然后在开发中,根据编辑器传入的类名,新建一个类去实现的话,这种方法非常适用。
2、ProtoBuf反射原理——获取并改造 元信息
2.1 、 .proto 文件
ProtoBuf 反射所需的元信息在哪?答案便是使用 ProtoBuf 的第一步就会接触到的:.proto 文件。
- .proto 文件
- 使用 ProtoBuf 内置的工具 protoc 编译器编译,protoc 将 .proto 文件内容编码并写入生成的代码中(.pb.cc文件)
- 使用 ProtoBuf 提供的编译 API 在运行时手动(指编码)解析 .proto 文件内容。实际上 protoc底层调用的也正是这个编译 API。
// 在 xxx.proto 文件中定义 Example1 message
message Example1 {
optional string stringVal = 1;
optional bytes bytesVal = 2;
message EmbeddedMessage {
int32 int32Val = 1;
string stringVal = 2;
}
optional EmbeddedMessage embeddedExample1 = 3;
repeated int32 repeatedInt32Val = 4;
repeated string repeatedStringVal = 5;
}
//提供我们数据元信息的过程,这些元信息包括数据由哪些字段构成,字段又属于什么类型以及字段之间的组合关系等
2.2 、 反射原理过程
无论 .proto 文件来源于何处,我们都需要对其做进一步的处理,将其解析成内存对象,并构建其与实例的映射,同时也要计算每个字段的内存偏移:
- 1、提供
.proto
(范指ProtoBuf Message
语法描述的元信息) - 2、解析
.proto
构建FileDescriptor、FieldDescriptor
等,即.proto
对应的内存模型(对象) - 3、之后每创建一个实例,就将其存到相应的实例池中
- 4、将
Descriptor
和instance
的映射维护到表中备查 - 5、通过 Descriptor 可查到相应的
instance
,又由于了解instance
中字段类型(FieldDescriptor
),所以知道字段的内存偏移,那么就可以访问或修改字段的值
- 任何一个对象最终都对应一段内存,有内存起始
(start_addr)
和结束地址, 而对象的每一个属性,都位于start_addr+$offset
,所以当对象和对应属性的offset已知的时候, 属性的内存地址也就是可以获取的。
2.3 、 反射相关的类和API
- 1、google::protobuf::Message
- 2、google::protobuf::Descriptor
- 3、google::protobuf::Reflection
- 4、google::protobuf::FieldDescriptor
Person是自定义的protobuf类型
2.3.1、google::protobuf::Message
-
Person
是自定义的pb
类型,继承自Message.
-
MessageLite
作为Message
基类,更加轻量级一些。 - 通过Message的两个接口
GetDescriptor/GetReflection
,可以获取该类型对应的Descriptor/Reflection。
//google::protobuf::Message
const Descriptor* GetDescriptor() const;
const Reflection* GetReflection() const;
2.3.2、 google::protobuf::Descriptor
Descriptor是对message类型定义的描述,包括message的名字、所有字段的描述、原始的proto文件内容等。
//Descriptor
//返回message的属性个数
int field_count() const;
//返回第index个属性
const FieldDescriptor* field(int index) const;
//通过proto文件里面定义的tag, 返回属性
const FieldDescriptor* FindFieldByNumber(int number) const;
//通过属性名,返回属性
const FieldDescriptor* FindFieldByName(const std::string& name) const;
//通过小写的属性名,返回属性
const FieldDescriptor* FindFieldByLowercaseName(
const std::string& lowercase_name) const;
//通过驼峰属性名,返回属性
const FieldDescriptor* FindFieldByCamelcaseName(
const std::string& camelcase_name) const;
//返回枚举类型的数量
int enum_type_count() const;
//返回第index个枚举类型属性
const EnumDescriptor* enum_type(int index) const;
//通过名称,返回枚举属性
const EnumDescriptor* FindEnumTypeByName(const std::string& name) const;
//通过属性值,返回枚举值属性
const EnumValueDescriptor* FindEnumValueByName(const std::string& name) const;
2.3.3、 google::protobuf::Reflection
- Reflection:接口类,主要提供了动态读写pb字段的接口,对pb对象的自动读写主要通过该类完成。
- 提供方法来动态访问/修改message中的field的接口类,对每种类型,Reflection都提供了一个单独的接口用于读写字段对应的值。
//检查message中的非repeated属性是否存在
bool HasField(const Message& message, const FieldDescriptor* field) const;
//返回message中repeated属性的长度
int FieldSize(const Message& message, const FieldDescriptor* field) const;
//清除message中的属性,非repeated属性HashField返回false,repeated属性FieldSize返回0
void ClearField(Message* message, const FieldDescriptor* field) const;
//返回message中除Unknown的属性,包括所有HashField返回为true,FieldSize非0的属性
void ListFields(const Message& message,
std::vector<const FieldDescriptor*>* output) const;
//返回message对应属性的,对应类型的返回值
int32 GetInt32(const Message& message, const FieldDescriptor* field) const;
int64 GetInt64(const Message& message, const FieldDescriptor* field) const;
uint32 GetUInt32(const Message& message, const FieldDescriptor* field) const;
uint64 GetUInt64(const Message& message, const FieldDescriptor* field) const;
float GetFloat(const Message& message, const FieldDescriptor* field) const;
double GetDouble(const Message& message, const FieldDescriptor* field) const;
bool GetBool(const Message& message, const FieldDescriptor* field) const;
std::string GetString(const Message& message,
const FieldDescriptor* field) const;
const EnumValueDescriptor* GetEnum(const Message& message,
const FieldDescriptor* field) const;
//返回message,属性的value的int类型值
int GetEnumValue(const Message& message, const FieldDescriptor* field) const;
const Message& GetMessage(const Message& message,
const FieldDescriptor* field,
MessageFactory* factory = nullptr) const;
//设置message,对应属性的对应类型的值
void SetInt32(Message* message, const FieldDescriptor* field,
int32 value) const;
void SetInt64(Message* message, const FieldDescriptor* field,
int64 value) const;
void SetUInt32(Message* message, const FieldDescriptor* field,
uint32 value) const;
void SetUInt64(Message* message, const FieldDescriptor* field,
uint64 value) const;
void SetFloat(Message* message, const FieldDescriptor* field,
float value) const;
void SetDouble(Message* message, const FieldDescriptor* field,
double value) const;
void SetBool(Message* message, const FieldDescriptor* field,
bool value) const;
void SetString(Message* message, const FieldDescriptor* field,
const std::string& value) const;
void SetEnum(Message* message, const FieldDescriptor* field,
const EnumValueDescriptor* value) const;
void SetEnumValue(Message* message, const FieldDescriptor* field,
int value) const;
Message* MutableMessage(Message* message, const FieldDescriptor* field,
MessageFactory* factory = nullptr) const;
//返回message对应属性repeated字段的index位置的值
int32 GetRepeatedInt32(const Message& message, const FieldDescriptor* field,
int index) const;
int64 GetRepeatedInt64(const Message& message, const FieldDescriptor* field,
int index) const;
uint32 GetRepeatedUInt32(const Message& message, const FieldDescriptor* field,
int index) const;
uint64 GetRepeatedUInt64(const Message& message, const FieldDescriptor* field,
int index) const;
float GetRepeatedFloat(const Message& message, const FieldDescriptor* field,
int index) const;
double GetRepeatedDouble(const Message& message, const FieldDescriptor* field,
int index) const;
bool GetRepeatedBool(const Message& message, const FieldDescriptor* field,
int index) const;
std::string GetRepeatedString(const Message& message,
const FieldDescriptor* field, int index) const;
const EnumValueDescriptor* GetRepeatedEnum(const Message& message,
const FieldDescriptor* field,
int index) const;
int GetRepeatedEnumValue(const Message& message, const FieldDescriptor* field,
int index) const;
const Message& GetRepeatedMessage(const Message& message,
const FieldDescriptor* field,
int index) const;
//设置message的repeated属性的index位置对应类型的value的值
void SetRepeatedInt32(Message* message, const FieldDescriptor* field,
int index, int32 value) const;
void SetRepeatedInt64(Message* message, const FieldDescriptor* field,
int index, int64 value) const;
void SetRepeatedUInt32(Message* message, const FieldDescriptor* field,
int index, uint32 value) const;
void SetRepeatedUInt64(Message* message, const FieldDescriptor* field,
int index, uint64 value) const;
void SetRepeatedFloat(Message* message, const FieldDescriptor* field,
int index, float value) const;
void SetRepeatedDouble(Message* message, const FieldDescriptor* field,
int index, double value) const;
void SetRepeatedBool(Message* message, const FieldDescriptor* field,
int index, bool value) const;
void SetRepeatedString(Message* message, const FieldDescriptor* field,
int index, const std::string& value) const;
void SetRepeatedEnum(Message* message, const FieldDescriptor* field,
int index, const EnumValueDescriptor* value) const;
void SetRepeatedEnumValue(Message* message, const FieldDescriptor* field,
int index, int value) const;
Message* MutableRepeatedMessage(Message* message,
const FieldDescriptor* field,
int index) const;
2.3.4、 google::protobuf::FieldDescriptor
FieldDescriptor描述message中的单个字段,例如字段名,字段属(optional/required/repeated)等。对于proto定义里的每种类型,都有一种对应的C++类型,
//是否是必须的
bool is_required() const; // shorthand for label() == LABEL_REQUIRED
//是否是可选的
bool is_optional() const; // shorthand for label() == LABEL_OPTIONAL
//是否是repeated
bool is_repeated() const; // shorthand for label() == LABEL_REPEATED
//是否可以pack
bool is_packable() const; // shorthand for is_repeated() &&
// IsTypePackable(type())
//是否是packed
bool is_packed() const; // shorthand for is_packable() &&
// options().packed()
//是否是message
bool is_map() const; // shorthand for type() == TYPE_MESSAGE &&
// message_type()->options().map_entry()
//属性在message的索引位置
int index() const;
2.3.5、 其他相关接口
2.3.5.1、DescriptorPool(元信息池)
- 任何时候想要查询一个
Descriptor
, 都是去DescriptorPool里面查询。 - 缓存所有查询的文件的Descriptor 。对外提供了诸如
FindServiceByName
、FindMessageTypeByName
等各类接口以便外部查询所需的元信息。 - 当 DescriptorPool 不存在时需要查询的元信息时,将进一步到
DescriptorDatabase
中去查找。
2.3.5.2、DescriptorDatabase( .proto 文件库)
-
DescriptorDatabase
是一个纯虚基类,描述了一系列符合通过名字(文件名,符号名。。。)
来获取FileDescriptorProt
o的接口 - 可从硬编码或磁盘中查询对应名称的 .proto 文件内容,解析后返回查询需要的元信息。
// 这里我干掉了里面的英文注释.
class LIBPROTOBUF_EXPORT DescriptorDatabase {
public:
inline DescriptorDatabase() {}
virtual ~DescriptorDatabase();
virtual ~DescriptorDatabase();
// 通过文件名字找.
virtual bool FindFileByName(const string& filename,
FileDescriptorProto* output) = 0;
// 通过符号名字找.
virtual bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output) = 0;
// 通过扩展信息找.
virtual bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output) = 0;
// 通过扩展信息的tag数字找...
virtual bool FindAllExtensionNumbers(const string& /* extendee_type */,
vector<int>* /* output */) {
return false;
}
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase);
};
核心的两个派生类是 :
- EncodedDescriptorDatabase
- 支持
DescriptorDatabase
的全部接口 - 接收序列化之后的
FileDescriptorProto,
保存在map
中备查. - 这个类对应着预先编译链接好的那些类型的反射机制。
- SourceTreeDescriptorDatabase
- 仅支持
DescriptorDatabase
的FindFileByName
接口。其余直接返回false. - 每次查询某个文件都是从磁盘读入proto的源文件,编译解析后返回对应的
FileDescriptorProto
. - 这个类对应着动态编译proto源文件的时候的反射机制.
2.3.5.3、MessageFactory( 实例工厂)
任何时候想要获取一个类型的instance
, 都要去MessageFactory
里面获取。
MessageFactory 是一个纯虚的基类,定义了通过Descripor
来获取对应类型instance
的接口.
{
public:
inline MessageFactory() {}
virtual ~MessageFactory();
// 了通过Descripor来获取对应类型instance 的接口
virtual const Message* GetPrototype(const Descriptor* type) = 0;
// 这个是获取编译链接好的那些类型的factory单例的入口.
static MessageFactory* generated_factory();
// 这个是对应的像上面哪个单例内填装数据的接口,protoc自动生成的文件都会有调用.
static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
const Message* prototype);
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory);
}
同样有两个核心的派生类
- GeneratedMessageFactory
- 一个
map
, 保存着Descriptor
和Message
- 这个类对应着预先编译链接好的那些类型的反射机制。
- DynamicMessageFactory
- 有简单的缓存,保存自己解析过的
Descriptor`` < /li >
-
< li >
可以通过Descriptor
,动态的基于内存构造出一个Message
2.4 、 反射原理过程具体解析
/* 反射创建实例 */
auto descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("Dog");
auto prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
auto instance = prototype->New();
/* 反射相关接口 */
auto reflecter = instance.GetReflection();
auto field = descriptor->FindFieldByName("name");
reflecter->SetString(&instance, field, "鸡你太美") ;
// 获取属性的值.
std::cout<<reflecter->GetString(instance , field)<< std::endl ;
return 0 ;
2.4.1、获取元信息
- 数据存储在哪里
- 1、所有的Descriptor存储在单例的DescriptorPool 中。
- google::protobuf::DescriptorPool::generated_pool()来获取他的指针。
- 2、所有的instance 存储在单例的MessageFactory中。
- google::protobuf::MessageFactory::generated_factory()来获取他的指针。
- 3、将所有的Descriptor & instance 提前维护到表中备查
DescriptorPool
相当于缓存了文件的 Descriptor
(底层使用 Map
),查询时将先到缓存中查询,如果未能找到再进一步到 DB
中(即 DescriptorDatabase
)查询,此时可能需要从磁盘中读取文件内容,然后再解析成 Descriptor
返回,这里需要消耗一定的时间。
不难看出,DescriptorPool
和 DescriptorDatabase
通过缓存机制提高了反射运行效率,但这只是反射工程实现上的一种优化,我们更感兴趣的应该是 Descriptor
的来源。
DescriptorDatabase
从磁盘中读取 .proto
内容并解析成 Descriptor
这一来源很容易理解,但我们大多数时候并不会采用这种方式,反射时也不会去读取 .proto
文件。那么我们的 .proto 内容在哪?
实际上我们在使用 protoc
生成 xxx.pb.cc
和 xxx.pb.h
文件时,其中不仅仅包含了读写数据的接口,还包含了 .proto
文件内容。阅读任意一个 xxx.pb.cc
的内容,你可以看到如下类似代码
static void AddDescriptorsImpl() {
InitDefaults();
// .proto 内容
static const char descriptor[] GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {
"\n\022single_int32.proto\"\035\n\010Example1\022\021\n\010int3"
"2Val\030\232\005 \001(\005\" \n\010Example2\022\024\n\010int32Val\030\377\377\377\377"
"\001 \003(\005b\006proto3"
};
// 注册 descriptor
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
descriptor, 93);
// 注册 instance
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"single_int32.proto", &protobuf_RegisterTypes);
}
其中 descriptor
数组存储的便是 .proto
内容。这里当然不是简单的存储原始文本字符串,而是经过了 SerializeToString
序列化处理,而后将结果以硬编码的形式保存在 xxx.pb.cc
中
硬编码的 .proto
元信息内容将以懒加载(类似于单例模式中的懒汉模式)的方式(被调用时才触发)被 DescriptorDatabase
加载、解析,并缓存到 DescriptorPool
中。
2.4.2、创建和获取实例(查表)
根据 MessageFactory 获得了一个实例。MessageFactory 是实例工厂,对外提供了根据元信息 descriptor 获取相应实例的能力。
// 注册对应 descriptor 的 instance 到 MessageFactory
// InternalRegisterGeneratedFile 函数内部,会将创建一个实例并做好 descriptor 与 instance 的映射
// xxx 应该替换为文件名,根据自己的需求取`在这里插入代码片`
namespace {
//! 将本文件内的全部类型的instance注册进入MessageFactory的接口.
void protobuf_RegisterTypes(const ::std::string&) {
// 初始化本文件的reflection数据.
protobuf_AssignDescriptorsOnce();
::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
Test_descriptor_, &Test::default_instance());
}
//! 本文件的初始接口.
void protobuf_AddDesc_xxx_2eproto() {
static bool already_here = false;
if (already_here) return;
already_here = true;
GOOGLE_PROTOBUF_VERIFY_VERSION;
// 注册本文件的Descriptor包. 这样就可以用名字通过generated_pool获取对应的Descriptor。
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
"\n\013xxx.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);
// 将本文件的类型instance注册接口注册给MessageFactory.
// 这里注册接口是为了实现类型的lazy注册。如果没有使用请求某个文件的类型,就不注册对应文件的类型。
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"xxx.proto", &protobuf_RegisterTypes);
// 构造并且初始化全部instance.
Test::default_instance_ = new Test();
Test::default_instance_->InitAsDefaultInstance();
// 注册清理接口.
::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_xxx_2eproto);
}
//! 下面利用全局变量的构造函数确保main函数执行之前数据已经进行注册.
struct StaticDescriptorInitializer_xxx_2eproto {
StaticDescriptorInitializer_xxx_2eproto() {
protobuf_AddDesc_xxx_2eproto();
}
} static_descriptor_initializer_xxx_2eproto_;
}
每次构建实例后,都将 descriptor 和 instance
维护到一个 _table
中,即映射表以便获取。后续所谓通过反射获得某个类的某个实例子,实际就是查表的过程。
2.4.3、实例对象的读写
实例对象的 reflection
里面存储了对象属性的偏移地址,而这些信息其实与 .proto
内容信息一样,在 protoc
编译时通过解析 proto
文件内容获得且记录在 xxx.pb.cc
中。
有了属性的内存偏移,自然可以对属性进行读写操作
- 任何一个对象最终都对应一段内存,有内存起始
(start_addr)
和结束地址, 而对象的每一个属性,都位于start_addr+$offset
,所以当对象和对应属性的offset
已知的时候, 属性的内存地址也就是可以获取的。 -
GeneratedMessageReflection
的填装和获取
对于每一个message
, 都有一个对应的GeneratedMessageReflection
对象.
这个对象保存了对应message
反射操作需要的信息.
//!初始化本文件的所有GeneratedMessageReflection对象.
void protobuf_AssignDesc_xxx_2eproto() {
protobuf_AddDesc_xxx_2eproto();
const ::google::protobuf::FileDescriptor* file =
::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
"xxx.proto");
GOOGLE_CHECK(file != NULL);
Test_descriptor_ = file->message_type(0);
static const int Test_offsets_[1] = {
//这里在计算属性的内存偏移.
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, id_),
};
// 这里是个test包填装的GeneratedMessageReflection对象.
Test_reflection_ =
new ::google::protobuf::internal::GeneratedMessageReflection(
Test_descriptor_,
Test::default_instance_,
Test_offsets_,
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _has_bits_[0]),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _unknown_fields_),
-1,
::google::protobuf::DescriptorPool::generated_pool(),
::google::protobuf::MessageFactory::generated_factory(),
sizeof(Test));
}
inline void protobuf_AssignDescriptorsOnce() {
::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
&protobuf_AssignDesc_xxx_2eproto);
}
// message.h 中 message的基本接口.
virtual const Reflection* GetReflection() const {
return GetMetadata().reflection;
}
// 每个message获取自己基本信息的接口.
::google::protobuf::Metadata Test::GetMetadata() const {
protobuf_AssignDescriptorsOnce();
::google::protobuf::Metadata metadata;
metadata.descriptor = Test_descriptor_;
metadata.reflection = Test_reflection_;
return metadata;
}
内存赋值,可以执行修改操作:
// 找到对应的内存地址,返回合适类型的指针.
template <typename Type>
inline Type* GeneratedMessageReflection::MutableRaw(
Message* message, const FieldDescriptor* field) const {
int index = field->containing_oneof() ?
descriptor_->field_count() + field->containing_oneof()->index() :
field->index();
void* ptr = reinterpret_cast<uint8*>(message) + offsets_[index];
return reinterpret_cast<Type*>(ptr);
}
// 设置protobuf的标志bit.
inline void GeneratedMessageReflection::SetBit(
Message* message, const FieldDescriptor* field) const {
if (has_bits_offset_ == -1) {
return;
}
MutableHasBits(message)[field->index() / 32] |= (1 << (field->index() % 32));
}
// 设置某个字段的值
template <typename Type>
inline void GeneratedMessageReflection::SetField(
Message* message, const FieldDescriptor* field, const Type& value) const {
if (field->containing_oneof() && !HasOneofField(*message, field)) {
ClearOneof(message, field->containing_oneof()); // V3 oneof 类型的清理。
}
*MutableRaw<Type>(message, field) = value; // 先直接覆盖
field->containing_oneof() ?
SetOneofCase(message, field) : SetBit(message, field); // 添加标记bit
}
2.5 、 反射原理具体实例介绍
参考 下面的代码展示了protobuf 对象反射的例子。将一个对象按照反射的字段顺序序列化到string,然后反序列化到对象。最后调用反射打印其字段值,可以看到对象能够还原。
1、proto文件
package tutorial;
message Person {
required string name = 1;
required int32 id = 2;
optional string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
required string number = 1;
optional PhoneType type = 2 [default = HOME];
}
optional PhoneNumber phone = 4;
}
#include <string>
#include <map>
#include <iostream>
#include <stdio.h>
#include "person.pb.h"
using namespace tutorial;
using namespace google::protobuf;
using std::cout;
using std::endl;
using std::string;
void serialize_message(const google::protobuf::Message& message, std::string* serialized_string) {
const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
const google::protobuf::Reflection* reflection = message.GetReflection();
for (int i = 0; i < descriptor->field_count(); ++i) {
const google::protobuf::FieldDescriptor* field = descriptor->field(i);
bool has_field = reflection->HasField(message, field);
if (has_field) {
//arrays not supported
assert(!field->is_repeated());
switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype:{\
valuetype value = reflection->Get##method(message, field);\
int wsize = field->name().size();\
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));\
serialized_string->append(field->name().c_str(), field->name().size());\
wsize = sizeof(value);\
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));\
serialized_string->append(reinterpret_cast<char*>(&value), sizeof(value));\
break;\
}
CASE_FIELD_TYPE(INT32, Int32, int);
CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);
CASE_FIELD_TYPE(FLOAT, Float, float);
CASE_FIELD_TYPE(DOUBLE, Double, double);
CASE_FIELD_TYPE(BOOL, Bool, bool);
CASE_FIELD_TYPE(INT64, Int64, int64_t);
CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);
#undef CASE_FIELD_TYPE
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
int value = reflection->GetEnum(message, field)->number();
int wsize = field->name().size();
//写入name占用字节数
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
//写入name
serialized_string->append(field->name().c_str(), field->name().size());
wsize = sizeof(value);
//写入value占用字节数
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
//写入value
serialized_string->append(reinterpret_cast<char*>(&value), sizeof(value));
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
std::string value = reflection->GetString(message, field);
int wsize = field->name().size();
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
serialized_string->append(field->name().c_str(), field->name().size());
wsize = value.size();
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
serialized_string->append(value.c_str(), value.size());
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
std::string value;
int wsize = field->name().size();
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
serialized_string->append(field->name().c_str(), field->name().size());
const google::protobuf::Message& submessage = reflection->GetMessage(message, field);
serialize_message(submessage, &value);
wsize = value.size();
serialized_string->append(reinterpret_cast<char*>(&wsize), sizeof(wsize));
serialized_string->append(value.c_str(), value.size());
break;
}
}
}
}
}
void parse_message(const std::string& serialized_string, google::protobuf::Message* message) {
const google::protobuf::Descriptor* descriptor = message->GetDescriptor();
const google::protobuf::Reflection* reflection = message->GetReflection();
std::map<std::string, const google::protobuf::FieldDescriptor*> field_map;
for (int i = 0; i < descriptor->field_count(); ++i) {
const google::protobuf::FieldDescriptor* field = descriptor->field(i);
field_map[field->name()] = field;
}
const google::protobuf::FieldDescriptor* field = NULL;
size_t pos = 0;
while (pos < serialized_string.size()) {
int name_size = *(reinterpret_cast<const int*>(serialized_string.substr(pos, sizeof(int)).c_str()));
pos += sizeof(int);
std::string name = serialized_string.substr(pos, name_size);
pos += name_size;
int value_size = *(reinterpret_cast<const int*>(serialized_string.substr(pos, sizeof(int)).c_str()));
pos += sizeof(int);
std::string value = serialized_string.substr(pos, value_size);
pos += value_size;
std::map<std::string, const google::protobuf::FieldDescriptor*>::iterator iter =
field_map.find(name);
if (iter == field_map.end()) {
fprintf(stderr, "no field found.\n");
continue;
} else {
field = iter->second;
}
assert(!field->is_repeated());
switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
case google::protobuf::FieldDescriptor::CPPTYPE_##cpptype: {\
reflection->Set##method(\
message,\
field,\
*(reinterpret_cast<const valuetype*>(value.c_str())));\
std::cout << field->name() << "\t" << *(reinterpret_cast<const valuetype*>(value.c_str())) << std::endl;\
break;\
}
CASE_FIELD_TYPE(INT32, Int32, int);
CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);
CASE_FIELD_TYPE(FLOAT, Float, float);
CASE_FIELD_TYPE(DOUBLE, Double, double);
CASE_FIELD_TYPE(BOOL, Bool, bool);
CASE_FIELD_TYPE(INT64, Int64, int64_t);
CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);
#undef CASE_FIELD_TYPE
case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: {
const google::protobuf::EnumValueDescriptor* enum_value_descriptor =
field->enum_type()->FindValueByNumber(*(reinterpret_cast<const int*>(value.c_str())));
reflection->SetEnum(message, field, enum_value_descriptor);
std::cout << field->name() << "\t" << *(reinterpret_cast<const int*>(value.c_str())) << std::endl;
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_STRING: {
reflection->SetString(message, field, value);
std::cout << field->name() << "\t" << value << std::endl;
break;
}
case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: {
google::protobuf::Message* submessage = reflection->MutableMessage(message, field);
parse_message(value, submessage);
break;
}
default: {
break;
}
}
}
}
void print_field(const Message& message)
{
const Descriptor* descriptor = message.GetDescriptor();
const Reflection* reflection = message.GetReflection();
for (int i = 0; i < descriptor->field_count(); ++i) {
const FieldDescriptor* field = descriptor->field(i);
bool has_field = reflection->HasField(message, field);
assert(!field->is_repeated());
switch (field->cpp_type()) {
#define CASE_FIELD_TYPE(cpptype, method, valuetype)\
case FieldDescriptor::CPPTYPE_##cpptype:{\
valuetype value = reflection->Get##method(message, field);\
if (has_field) {\
cout << field->name() << " : " << value << ", type : " << #valuetype << "\n";\
} else {\
cout << field->name() << " : " << "None" << ", type : " << #valuetype << "\n";\
}\
break;\
}
CASE_FIELD_TYPE(INT32, Int32, int);
CASE_FIELD_TYPE(UINT32, UInt32, uint32_t);
CASE_FIELD_TYPE(FLOAT, Float, float);
CASE_FIELD_TYPE(DOUBLE, Double, double);
CASE_FIELD_TYPE(BOOL, Bool, bool);
CASE_FIELD_TYPE(INT64, Int64, int64_t);
CASE_FIELD_TYPE(UINT64, UInt64, uint64_t);
#undef CASE_FIELD_TYPE
case FieldDescriptor::CPPTYPE_ENUM: {
int value = reflection->GetEnum(message, field)->number();
if (has_field) {
cout << field->name() << " : " << value << ", type : " << "enum \n";
}
else {
cout << field->name() << " : " << "None" << ", type : " << "enum \n";
}
break;
}
case FieldDescriptor::CPPTYPE_STRING: {
string value = reflection->GetString(message, field);
if (has_field) {
cout << field->name() << " : " << value << ", type : " << "string \n";
}
else {
cout << field->name() << " : " << "None" << ", type : " << "string \n";
}
break;
}
case FieldDescriptor::CPPTYPE_MESSAGE: {
const Message& submessage = reflection->GetMessage(message, field);
print_field(submessage);
break;
}
}
}
}
int main()
{
string str;
Person person;
person.set_name("shonm");
person.set_id(123);
person.mutable_phone()->set_number("1380000");
person.mutable_phone()->set_type(Person_PhoneType_WORK);
serialize_message(person, &str); //按照自己的方式(反射的字段)序列化
Person person2;
parse_message(str, &person2); //按照自己的方式反序列化
printf("\n\n");
print_field(person); //根据反射打印字段
printf("\n\n");
print_field(person2);
}