使用protobuf的反射來動態生成Message並賦值
前言:
公司做了個專案,使用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 int32 RoleId= 1;
required bytes RoleName= 2;
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
....
}
今天股票大跌,我已經麼有心思再多寫什麼了,有什麼問題,可以問,吃麵吧