Protobuf反射(未完待续3/01)

零、文章背景

有些后台同学将自己称为 SQL Boy,因为负责的业务主要是对数据库进行增删改查。经常和 Proto 打交道的同学,是不是也会叫自己 PB Boy? 因为大部分工作也是对 Proto 进行 SET 和 GET。面对大量重复且丑陋的代码,除了宏是否有更好的解决方法?本文结合 PB 反射给出了我在运营系统开发工作中的一些代码优化实践。

一、反射的背景知识(反射消息来修改消息的某些字段)

  • 反射定义
    反射的一般定义如下:计算机程序在运行时可以访问、检测和修改它本身状态或行为。

  • 类图
    在这里插入图片描述

  • 类图解析
    1)从上图我们可以看出,Message 类继承于 MessageLite 类,业务一般自定义的 Person 类继承于 Message 类。
    2)Descriptor 类和 Reflection 类都聚合于 Message,是弱依赖的关系。

  • 过程(粗略过程,一般使用 PB 反射的步骤)

1. 通过Message获取单个字段的FieldDescriptor
2. 通过Message获取其Reflection
3. 通过Reflection来操作FieldDescriptor,从而动态获取或修改单个字段
  • 获取 Descript、Reflection 的函数:
const google::protobuf::Reflection* pReflection = pMessage->GetReflection();
const google::protobuf::Descriptor* pDescriptor = pMessage->GetDescriptor();
  • 获取 FieldDescriptor 的函数:
    const google::protobuf::FieldDescriptor * pFieldDesc = pDescriptor->FindFieldByName(id);

二、反射消息来传输数据需要注意的点

  • 在网络编程中使用Protobuf需要解决以下两个问题:

1)长度

Protobuf打包的数据没有自带长度信息或终结符,需要由应用程序自己在发生和接收的时候做正确的切分。

2)类型

Protobuf打包的数据没有自带类型信息,需要由发送方把类型信息传给给接收方,接收方创建具体的Protobuf Message对象,再做反序列化

三、过程(反射消息来传输数据的详细过程)

1)根据type name自动创建Message的关键代码

(1)1.用DescriptorPool: : generated_pool() 找到一个DescriptorPool对象,它包含了程序编译的时候所链接的全部Protobuf Message types。
(2)根据type name用DescriptorPool: :FindMessageTypeByName()查找Descriptor。
(3)再用MessageFactory: : generated_factory() 找到MessageFactory对象,它能创建程序编译的时候所链接的全部Protobuf Message types。
(4)然后,用MessageF actory: :GetPrototype()找到具体Message type 的default instance。
(5)最后,用prototype->New()创建对象。

2)代码示例

Message* createMessage(const std::string& typeName)
{
    Message* message = NULL;
    //1)generated_pool() 找到一个DescriptorPool对象
    //2)type name用DescriptorPool: :FindMessageTypeByName()查找Descriptor。
    const Descriptor* descriptor = 
    DescriptorPool::generated_pool()->
    FindMessageTypeByName(typeName) ;
    if (descriptor)
    {
     //3)再用MessageFactory: : generated_factory() 找到
     //MessageFactory对象
     //4)用MessageF actory: :GetPrototype()找到具体Message type 的default instance
        const Message* prototype
               = MessageFactory::generated_factory()->GetPrototype(descriptor);
        if (prototype)
        {
        //5)用prototype->New()创建对象
            message = prototype->New(); .
        }
    }
    return message ;
}

3)createMessage()返回的是动态创建的对象的指针,调用方有责任释放它,不然就会使内存泄漏。

  • 备注
    拿到Message*之后怎么办呢? 怎么调用这个具体消息类型的处理函数? 这就需要消息分发器( dispatcher)出马了

  • 做分发器的思路
    传送门

四、API介绍

1)std::string Descriptor::DebugString(); // 将message转化成人可以识别出的string信息

传送门

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值