主页 > 创业  > 

protobuf自动填充字段数据

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

protobuf_24_3\bin\windows\release\protoc.exe:

这是 Protocol Buffers 编译器的可执行文件路径,用于将 .proto 文件编译成 C++ 源代码。

-I="protobuf_24_3\include":

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

-I=../ 和 -I=./:

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

--cpp_out=dllexport_decl=MY_EXPORT:../out_dir/:

--cpp_out

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

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

--experimental_allow_proto3_optional:

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

./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对象进行填充值。

标签:

protobuf自动填充字段数据由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“protobuf自动填充字段数据