前言:
公司做了个项目,使用protobuf,各功能号会不断增加,message也会一直变,我做为测试人员,需要做一个测试工具,前期可以通过mfc画图,就是画出message的各个属性对应的输入框,之后按照格式,遍历对应的message,读取输入框的数据来一个个赋值。这样一来,功能号少的时候,还没什么,多画点输入框就是了。后来,message嵌套越来越复杂,其中还夹杂着repeated的属性,repeated中还有repeated、子message,直接崩溃。于是就决心查资料做了这个动态生成的,用CMarku从xml读取数据,感觉挺好用,留个档,也希望对其他人能有个帮助。
预期读者:
对protobuf有一定了解,至少已经跑通过程序,只是遇到了和我类似的问题,现在要使用protobuf的动态生成message并赋值、发送消息给服务端。
理论方面,可以查看大牛的http://blog.csdn.net/solstice/article/details/6300108介绍,说的很细。
正文:
如果我在这里大谈理论,可能就是班门弄斧了,那么废话不多说,直接上代码,上核心代码。
根据message name来生成message
Message* createMessage(const std::string& typeName)
{
google::protobuf::Message* message = NULL;
const google::protobuf::Descriptor* descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(typeName);
if (descriptor)
{
const google::protobuf::Message* prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
if (prototype)
{
message = prototype->New();
}
}
return message;
}
typeName就是下面定义的 Role
required string Status= 3;
repeated Permission p=4;
}
message Permission
{
required id=1;
required pname=XXX;
}
总结一下,我是这么理解的,如要根据message name动态生成message,先生成message的descriptor,再根据descriptor生成Message类,类再new一个message对象,即可使用。有理解的不对的地方,请大家指教。关于message中的属性项,如上面Role的RoleId、RoleName、Status,也是一个流程,只是descriptor变成了field 的descriptor等。
由于message内部属性的各种嵌套repeated message,而要给一个message对象的属性赋值,使用Role的反射只能赋值一层,比如上面例子Role,只能给message ROle 的RoleId、RoleName、Status、p赋值,但是不能为Permission的id、pname赋值,如果要给id、pname赋值,需要使用Permission的反射,道理同Role。这里还涉及到一个repeated,先说反射,再来说这个repeated的处理。
pMsg=createMessage(packageName);
pDescriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(packageName);
pReflection = pMsg->GetReflection();
上面的pMsg、pDescriptor、pReflection在嵌套赋值的使用要用到,先不考虑冗余,暂且弄个
struct returnStruct
{
const google::protobuf::Descriptor* pDescriptor;
const google::protobuf::Reflection* pReflection;
google::protobuf::Message* pMsg;
};
struct returnStruct re={pDescriptor,pReflection,pMsg}
参照后面的代码,可能会好理解一点。大概说一下,把pDescriptor,pReflection,pMsg搜集起来,在对Role属性赋值的时候,带上这个returnStruct,这样在子message Permission的赋值处理时候,Permission赋值完毕,可以直接使用returnStruct的Permission的父级Role的反射来把Permission对象赋值给Role的p,递归的时候特别好用。
pFieldDescriptor=re->pDescriptor->FindFieldByName(tag_name);//tag_name 是属性项的名字,就是Role中的RoleId、RoleName之类。
if(pFieldDescriptor->is_repeated()){//考虑到googel对protobuf 中属性为repeated的赋值接口不同,这里分开
switch(pFieldDescriptor->type())
{
case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
re->pReflection->AddDouble(re->pMsg,pFieldDescriptor,atof(tag_value.c_str()));
break;
case google::protobuf::FieldDescriptor::TYPE_FLOAT:
re->pReflection->AddFloat(re->pMsg,pFieldDescriptor,atof(tag_value.c_str()));
break;
.........
case google::protobuf::FieldDescriptor::TYPE_MESSAGE://嵌套的message的处理
{
pDescriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(packageName);
if(xml.IntoElem()) //xml是CMarkUP的xml当前节点的读取位置,xml数据格式和message.proto文件一模一样,当前情况相当于对Role的前三项赋值完毕,来处理permission项
{
pMsg=re->pReflection->AddMessage(re->pMsg,pFieldDescriptor);//注意这里是AddMessage,和下面的MutableMessage对应,处理repeated类型的
pReflection = pMsg->GetReflection();
struct returnStruct ret={pDescriptor,pReflection,pMsg};
parseXml(xml,pTestLogger,"",&ret);
xml.OutOfElem();
}
break;
}
}
}else //属性项不是repeated的处理
{
//这里主要就是个接口不同
....
pMsg=re->pReflection->MutableMessage(re->pMsg,pFieldDescriptor);//注意这里是MutableMessage
....
}
今天股票大跌,我已经么有心思再多写什么了,有什么问题,可以问,吃面吧