Protobuf-Reflection类

本文详细介绍了Protobuf的Reflection类,特别是GeneratedMessageReflection的内部实现和应用。Reflection接口提供了动态访问和修改message字段的方法,而GeneratedMessageReflection是针对特定descriptor的实现。内容包括Reflection的内存管理和操作流程,以及在read和write操作中的工作原理。此外,还探讨了内存分布和不同类型的field如何存储。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

类Reflection

接口类,提供方法来动态访问/修改message中的field的接口类。调用Message::GetReflection()获得messge对应的reflection。
这个类没有放到Message类中,是从效率角度考虑的。因为绝大多数message的实现共用同一套Reflection(GeneratedMessageReflection),并且一个Message所有的object是共享同一个reflection object的。

注意:

  1. 针对所有不同的field类型FieldDescriptor::TYPE_*,需要使用不同的Get*()/Set*()/Add*() 接口;
  2. repeated类型需要使用GetRepeated*()/SetRepeated*()接口,不可以和非repeated类型接口混用;
  3. message对象只可以被由它自身的reflection(message.GetReflection())来操作;

那么为什么需要针对每种FieldDescriptor::TYPE_*有单独的Get*()/Set*()呢?
因为如果使用抽象的type来解决,需要增加一层处理,这会导致message占用内存变大,也增加了内存泄漏的风险,所以在用这种flat的接口设计。

类GeneratedMessageReflection

类Reflection的子类(也是当前版本中唯一的子类),服务于某一个固定的descriptor(构造GeneratedMessageReflection对象时就确定了对应的descriptor)。反射机制中最为核心的类。

内部实现:

操作任何一个数据时,需要知道2个信息即可:

  1. 内存地址;
  2. 类型信息;

GeneratedMessageReflection也是这样设计的。GeneratedMessageReflection通过base_addr + $offset[i] 的方式管理message所有的field,$offset[i]记录了message中每个field在message内存对象中的偏移,并且descriptor中有每个field的类型信息。

需要针对某个(message, field)做处理的时候:

  1. 直接通过descriptor获取对应field在message中的index
  2. 再查询offset[$index]获取内存地址
  3. 然后通过descriptor中type信息
  4. 做reinterpret_cast就获得对应数据。

构建GeneratedMessageReflection对象时,传入的核心数据是:

  1. descriptor:被管理的message的descriptor指针;
  2. offsets:message类的所有成员在message类内存对象的偏移;
  3. has_bits_offset: 用于”记录某个field是否存在的bitmap”的偏移(这个bitmap是message子类内部成员,其实是取这个数组0元素_has_bits_[0]的偏移),这个bitmap最终是用来判断optional类型的field是否存在;
  4. unknown_fields_offset: 和has_bits_offset功能类似,用于记录unkown数据;

field有不同的类型,所以需要将void*转化为相应的类型。

  1. 对于primitive类型和string类型,直接使用对应primitive类型/string*表示;
  2. 单个Message类型field,通过Message的指针来保存;
  3. Enum类型field,通过int来保存,这个int作为EnumDescriptor::FindValueByNumber()的输入;
  4. Repeated类型field(细节见《repeated字段》一章):
  5. 其中Strings/Message类型使用RepeatedPtrFields
  6. 其它primitive类型使用RepeatedFields

应用举例:

在每个.pb.cc文件中,对应每个message都有对应的GeneratedMessageReflection对象。例如针对protobuf/compiler/plugin.proto文件中的message CodeGeneratorRequest,在protobuf/compiler/plugin.pb.cc中:

    namespace {

    const ::google::protobuf::Descriptor* CodeGeneratorR
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值