1. 程式人生 > >使用protobuf的反射來動態生成Message並賦值

使用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

message 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

....

}

今天股票大跌,我已經麼有心思再多寫什麼了,有什麼問題,可以問,吃麵吧