所谓反射机制,就是能够在运行时知道任意类的所有属性和方法,能够调用任意对象的任意方法和属性。这种动态获取的信息以及动态调用对象方向的功能称为反射机制。

不像Jave等语言,C++本身没有反射机制,在使用C++版本Protobuf时,protobuf通过proto文件产生响应的message和service,protobuf可以通过proto文件提供反射机制,程序在运行时可以通过proto获取任意message和任意service的属性和方法,也可以在运行时调用message的属性和方法。

1.获取message和service的属性和方法

protobuf通过Descriptor获取任意message或service的属性和方法,Descriptor主要包括了一下几种类型:

FileDescriptor 获取Proto文件中的Descriptor和ServiceDescriptor
Descriptor 获取类message属性和方法,包括FieldDescriptor和EnumDescriptor
FieldDescriptor 获取message中各个字段的类型、标签、名称等
EnumDescriptor 获取Enum中的各个字段名称、值等
ServiceDescriptor 获取service中的MethodDescriptor
MethodDescriptor 获取各个RPC中的request、response、名称等

也就是说,如果能够获取到proto文件的FileDescriptor,就能获取proto文件中的所有的内容。那么如何获取proto文件的FileDescriptor呢?protobuf提供两种方法。

1.1 使用protoc将proto文件生成.h和.cc文件

这种法法直接根据生成的类来获取响应的FileDescriptor,比如现在有test.proto文件,那么可以通过DescriptorPool::generated_pool()获取到其FileDescriptor

并且对于任意的message和service都可以根据其名称,通过DescriptorPool对应的Descriptor和ServiceDescriptor。

1.2 只使用proto文件,不使用protoc进行编译

这种情况需要手动解析proto文件,再获取FileDescriptor。protobuf提供了响应的解析器compiler,通过compoiler可以方便的获取proto文件的FileDescriptor

2. 调用message的属性和方法

首先获取message的对象:

有了message对象,因为所有的message都是Message* 类型的,但不同的message对象的属相和方法是不一样的,所以并不能直接调用其对象和方法。

protobuf是通过Reflection来调用message的属性和方法的。message中的方法只有对各个属性的get和set,而调用message的属性其实也就是调用属性的get。调用message的某一个属性的get和set,就需要该属性的Descriptor,通过Reflection将相应的值写入到message对应的属性:

3.Protobuf反射使用案例

这部分主要是重复上两节的内容,进行完整示例。以下是使用的test.proto文件

3.1通过类型名字创建出类型对象

预编译好proto模式

直接解析proto文件模式

3.2 通过对象和对象的属性获取名字,修改对应属性

4. 处理Protobuf反射,通过协议字串动态生成协议

下面两部分是针对protobuf通讯协议的内容。下面这两个函数可以动态生成pb的message,其中ProtoMsg是pb package的名字

5. 通过反射将配置中的值设置进pb字段

可以通过如下方法,自动创建message并对field赋值:

6. serialize_message

serialize_message遍历提取message中各个字段以及对应的值,序列化到string中。主要思路就是通过Descriptor得到每个字段的描述符:字段名、字段的cpp类型。通过Reflection的GetX接口获取对应的value。

7. parse_message

parse_message通过读取field/value,还原message对象。
主要思路跟serialize_message很像,通过Descriptor得到每个字段的描述符FieldDescriptor,通过Reflection的SetX填充message。

只是基本介绍下,在遇到问题时候还是多求助官方文档的。

发表评论

电子邮件地址不会被公开。 必填项已用*标注