当前位置: 首页 > news >正文

protobuf自动填充字段数据

Protobuf自动填充字段数据

〇、需求

需要从一个结构中,比如map或variant,把数据填充到protobuf的各个字段中。

源数据:map

目的数据:protobuf

注意:只有protobuf有的字段才能进行填充,variant中有的值,但是protobuf中没有的字段,是填充不了的。

一、解决方案

生成protobuf文件时,打开使用反射机制的开关,然后遍历map或variant,利用protobuf的反射机制进行字段的填充。

二、详细步骤

2.1生成protobuf

反射是默认启用的,不需要特定的编译开关,去打开编译,避免使用–cpp_out=optimized这样的编译选项,可能会关闭掉反射。

protobuf_24_3\bin\windows\release\protoc.exe -I="protobuf_24_3\include" -I=../ -I=./ --cpp_out=dllexport_decl=MY_EXPORT:../out_dir/ --experimental_allow_proto3_optional ./myProto.proto
  1. protobuf_24_3\bin\windows\release\protoc.exe

    • 这是 Protocol Buffers 编译器的可执行文件路径,用于将 .proto 文件编译成 C++ 源代码。
  2. -I="protobuf_24_3\include"

    • -I 选项用于指定包含目录。这告诉编译器在编译过程中查找 .proto 文件时还需要在 protobuf_24_3\include 目录中查找。
  3. -I=../-I=./

    • 这两个也是包含目录的指定。../ 表示上一级目录,而 ./ 表示当前目录。这样 .proto 文件或依赖的其他 .proto 文件可以在这些目录中被找到。
  4. --cpp_out=dllexport_decl=MY_EXPORT:../out_dir/

    • --cpp_out
      

      选项指定输出 C++ 代码的目录和特定选项:

      • dllexport_decl=MY_EXPORT 是编译选项,它指定生成的 C++ 代码在使用时定义了一个宏 MY_EXPORT。这个宏通常用于控制 DLL 中的符号导出,这对于 Windows 动态链接库(DLL)是很重要的。
      • ../out_dir/ 是生成的 C++ 文件将被输出到的目录路径(相对于当前工作目录)。
  5. --experimental_allow_proto3_optional

    • 这个标志允许使用 Protocol Buffers 3 中的可选字段特性(optional fields)模型。这意味着可以在 .proto 文件中定义字段为可选,使用这个选项可以让你的代码兼容这一特性。
  6. ./myProto.proto

    • 这是输入的 .proto 文件,编译器将对其进行处理并生成相应的 C++ 代码。

2.2 开发功能

主要提供三种方法,dllLoad,getMessage,Map2PB.

  • dllLoad,负责对protobuf共享库的动态加载
  • getMessage,通过protobuf的message名字获取对应的message对象
  • ConvertTo,负责map或者Variant中数据到protobuf字段数据内容的填充
2.2.1 dllLoad函数
bool dllLoad(IN const std::string& dllPath){
    boost::dll::shared_library dllHelper;
    try{
        dllHelper.load(dllPath);
    }catch(const std::exception& e){
        return false;
    }
    if(!dllHelper.is_loaded()){
        return false;
    }
    return true;
}
2.2.2 getMessage函数
::google::protobuf::DynamicMessageFactory g_MessageFactory;

::google::protobuf::Message* getMessage(const std::string& messageName){
    std::string prefix = "your own protobuf prefix";
    const ::google::protobuf::DescriptorPool* pool = ::google::protobuf::DescriptorPool::generated_pool();
    auto descriptor = pool->FindMessageTypeByName(prefix+messageName);
    if(!descriptor){
        return nullptr;
    }
    return g_MessageFactory.GetPrototype(descriptor)->New();
    
}
2.2.3 ConvertTo函数

每个人自定义的variant可能不一样,我的variant定义如下

message Dict{
    map<string,Variant> list=1;
}
message Variant{
    oneof oneof_variant{
        //basic
        bool boolvalue = 1;
        int32 int32value=2;
        ...
        //struct
        Dict dictvalue=3;
    }
}

variant可以是一个map,这个map存的键值对也是string,variant。

bool ConvertTo(IN Variant& in, OUT ::google::protobuf::Message* out){
    auto& map = in.dictvalue().list();
    for(auto iter :map){
        auto fieldname = iter.first;
        auto& fieldvalue = iter.second;
        ret = SetPB(out,fieldname,fieldvalue);
        if(ret!= true) return false;
    }
    return true; 
}

其中SetPB函数如下

bool SetPB(OUT ::google::protobuf::Message* rootMsg,IN const std::string& fieldname,IN Variant& var){
    auto descriptor = rootMsg->GetDescriptor();
    auto reflection = rootMsg->GetReflection();
    auto field = descriptor->FindFieldByName(fieldname);
    if(nullptr ==field){return false;}
    auto toType = field->type();
    auto fromType = var.oneof_variant_case();
    switch(toType){
        case FieldDescriptor::TYPE_DOUBLE:{
            ret = SET_DOUBLE(field,reflection,rootMsg,var);
        }break;
    }
}

各种类型值的转换的就随自定义的variant类型来提供不同类型的设置方法了,这里只展示设置bool类型变量方法。

bool SET_DOUBLE(const ::google::protobuf::fieldDescriptor* field,const ::google::protobuf::Reflection* reflection,::google::protobuf::Message* rootMsg,Variant& var){
    if(!field->is_repeated()){
        reflection->SetDouble(rootMsg,field,var.doublevalue());
        return true;
    }
    return true;
}

在这一步field可能是数组类型,也可能不是,需要进行判断。根据自己需求提供给protobuf填充值的方法。

2.2.4 总结

最后调用步骤就是,先获取到message的dllpath,然后调用dllLoad加载动态库,然后再利用messagename调用getMessage获取改messagename对应的message对象,最后利用ConvertTo函数,对该message对象进行填充值。

相关文章:

  • 金融时间序列【量化理论】
  • DHCPV6
  • SOLIDWORKS无法卸载解决方法 - 强制卸载程序
  • 淘宝关键词搜索API接口系列,json数据示例参考
  • 【ST-LINK未能被keil识别STM32 ST-LINK Utility出现“Can not connect to target】
  • 用 PyMuPDF 和 Pillow 打造 PDF 超级工具
  • 自动驾驶之BEV概述
  • tidb实时同步到mysql
  • 多模态论文笔记——TECO
  • OpenCV二值化处理
  • 上位机知识篇---Docker容器
  • 用AI学历史1——中国通史
  • Python pathlib模块介绍
  • 【面试】Redis 常见面试题
  • 专利申请流程详解:从创意到授权的完整指南
  • Nginx 负载均衡详解
  • 在UBUNTU下搭建Deepseek
  • 力扣hot100——螺旋矩阵 超简单易懂的模拟搜索方法
  • 专题--Redis
  • STM32 HAL库标准库+ESP8266+机智云
  • 许峰已任江苏省南京市副市长
  • 国泰海通合并后首份业绩报告出炉:一季度净利润增逾391%
  • 澎湃回声|山东莱州、潍坊对“三无”拖拉机产销市场展开调查排查
  • 昆明破获一起算命破灾诈骗案,民警:大师算不到自己的未来
  • 法治日报调查直播间“杀熟”乱象:熟客越买越贵,举证难维权不易
  • 王毅出席金砖国家外长会晤