Protocol Buffer技术详解(C++实例)
?原文:http://www.cnblogs.com/stephen-liu74/archive/2013/01/04/2842533.html
?? 這篇Blog仍然是以Google的官方文檔為主線,代碼實例則完全取自于我們正在開發的一個Demo項目,通過前一段時間的嘗試,感覺這種結合的方式比較有利于培訓和內部的技術交流。還是那句話,沒有最好的,只有最適合的。我想寫Blog也是這一道理吧,不同的技術主題可能需要采用不同的風格。好了,還是讓我們盡早切入主題吧。
?? ?
?? ?? 一、生成目標語言代碼。
?? ?? 下面的命令幫助我們將MyMessage.proto文件中定義的一組Protocol Buffer格式的消息編譯成目標語言(C++)的代碼。至于消息的內容,我們會在后面以分段的形式逐一列出,同時也會在附件中給出所有源代碼。
?? ?? protoc -I=./message --cpp_out=./src ./MyMessage.proto
?? ?? 從上面的命令行參數中可以看出,待編譯的文件為MyMessage.proto,他存放在當前目錄的message子目錄下。--cpp_out參數則指示編譯工具我們需要生成目標語言是C++,輸出目錄是當前目錄的src子目錄。在本例中,生成的目標代碼文件名是MyMessage.pb.h和MyMessage.pb.cc。
?? ?
?? ?? 二、簡單message生成的C++代碼。
?? ?? 這里先定義一個最簡單的message,其中只是包含原始類型的字段。
?? ?? option optimize_for = LITE_RUNTIME;
?? ?? message LogonReqMessage {
?? ? ?? ? required int64 acctID = 1;
?? ?? ? ? required string passwd = 2;
? ? ? }
?? ?? 由于我們在MyMessage文件中定義選項optimize_for的值為LITE_RUNTIME,因此由該.proto文件生成的所有C++類的父類均為::google::protobuf::MessageLite,而非::google::protobuf::Message。在上一篇博客中已經給出了一些簡要的說明,MessageLite類是Message的父類,在MessageLite中將缺少Protocol Buffer對反射的支持,而此類功能均在Message類中提供了具體的實現。對于我們的項目而言,整個系統相對比較封閉,不會和更多的外部程序進行交互,與此同時,我們的客戶端部分又是運行在Android平臺,有鑒于此,我們考慮使用LITE版本的Protocol Buffer。這樣不僅可以得到更高編碼效率,而且生成代碼編譯后所占用的資源也會更少,至于反射所能帶來的靈活性和極易擴展性,對于該項目而言完全可以忽略。下面我們來看一下由message LogonReqMessage生成的C++類的部分聲明,以及常用方法的說明性注釋。
????? 下面是讀寫LogonReqMessage對象的C++測試代碼和說明性注釋。
1 void testSimpleMessage() 2 { 3 printf("==================This is simple message.================\n"); 4 //序列化LogonReqMessage對象到指定的內存區域。 5 LogonReqMessage logonReq; 6 logonReq.set_acctid(20); 7 logonReq.set_passwd("Hello World"); 8 //提前獲取對象序列化所占用的空間并進行一次性分配,從而避免多次分配 9 //而造成的性能開銷。通過該種方式,還可以將序列化后的數據進行加密。 10 //之后再進行持久化,或是發送到遠端。 11 int length = logonReq.ByteSize(); 12 char* buf = new char[length]; 13 logonReq.SerializeToArray(buf,length); 14 //從內存中讀取并反序列化LogonReqMessage對象,同時將結果打印出來。 15 LogonReqMessage logonReq2; 16 logonReq2.ParseFromArray(buf,length); 17 printf("acctID = %I64d, password = %s\n",logonReq2.acctid(),logonReq2.passwd().c_str()); 18 delete [] buf; 19 }????? 三、嵌套message生成的C++代碼。
?? ?? enum UserStatus {
?? ? ? ?? OFFLINE = 0;
?? ? ? ?? ONLINE = 1;
?? ?? }
? ? ? enum LoginResult {
?? ?? ? ? LOGON_RESULT_SUCCESS = 0;
?? ?? ??? LOGON_RESULT_NOTEXIST = 1;
?? ??? ?? LOGON_RESULT_ERROR_PASSWD = 2;
?? ?? ? ? LOGON_RESULT_ALREADY_LOGON = 3;
?? ?? ? ? LOGON_RESULT_SERVER_ERROR = 4;
?? ?? }
? ? ? message UserInfo {
?? ? ?? ? required int64 acctID = 1;
?? ?? ? ? required string name = 2;
?? ? ?? ? required UserStatus status = 3;
?? ?? }
?? ?? message LogonRespMessage {
?? ?? ??? required LoginResult logonResult = 1;
?? ?? ??? required UserInfo userInfo = 2; //這里嵌套了UserInfo消息。
? ? ? }
?? ?? 對于上述消息生成的C++代碼,UserInfo因為只是包含了原始類型字段,因此和上例中的LogonReqMessage沒有太多的差別,這里也就不在重復列出了。由于LogonRespMessage消息中嵌套了UserInfo類型的字段,在這里我們將僅僅給出該消息生成的C++代碼和關鍵性注釋。
? ? ? 下面是讀寫LogonRespMessage對象的C++測試代碼和說明性注釋。
1 void testNestedMessage() 2 { 3 printf("==================This is nested message.================\n"); 4 LogonRespMessage logonResp; 5 logonResp.set_logonresult(LOGON_RESULT_SUCCESS); 6 //如上所述,通過mutable_userinfo函數返回userInfo字段的指針,之后再初始化該對象指針。 7 UserInfo* userInfo = logonResp.mutable_userinfo(); 8 userInfo->set_acctid(200); 9 userInfo->set_name("Tester"); 10 userInfo->set_status(OFFLINE); 11 int length = logonResp.ByteSize(); 12 char* buf = new char[length]; 13 logonResp.SerializeToArray(buf,length); 14 15 LogonRespMessage logonResp2; 16 logonResp2.ParseFromArray(buf,length); 17 printf("LogonResult = %d, UserInfo->acctID = %I64d, UserInfo->name = %s, UserInfo->status = %d\n" 18 ,logonResp2.logonresult(),logonResp2.userinfo().acctid(),logonResp2.userinfo().name().c_str(),logonResp2.userinfo().status()); 19 delete [] buf; 20 }?? ?? 四、repeated嵌套message生成的C++代碼。
?? ?? message BuddyInfo {
?? ? ? ?? required UserInfo userInfo = 1;
?? ? ?? ? required int32 groupID = 2;
?? ?? }
?? ?? message RetrieveBuddiesResp {
?? ? ?? ? required int32 buddiesCnt = 1;
?? ? ?? ? repeated BuddyInfo buddiesInfo = 2;
?? ?? }
? ? ? 對于上述消息生成的代碼,我們將只是針對RetrieveBuddiesResp消息所對應的C++代碼進行詳細說明,其余部分和前面小節的例子基本相同,可直接參照。而對于RetrieveBuddiesResp類中的代碼,我們也僅僅是對buddiesInfo字段生成的代碼進行更為詳細的解釋。
? ? ? 下面是讀寫RetrieveBuddiesResp對象的C++測試代碼和說明性注釋。
1 void testRepeatedMessage() 2 { 3 printf("==================This is repeated message.================\n"); 4 RetrieveBuddiesResp retrieveResp; 5 retrieveResp.set_buddiescnt(2); 6 BuddyInfo* buddyInfo = retrieveResp.add_buddiesinfo(); 7 buddyInfo->set_groupid(20); 8 UserInfo* userInfo = buddyInfo->mutable_userinfo(); 9 userInfo->set_acctid(200); 10 userInfo->set_name("user1"); 11 userInfo->set_status(OFFLINE); 12 13 buddyInfo = retrieveResp.add_buddiesinfo(); 14 buddyInfo->set_groupid(21); 15 userInfo = buddyInfo->mutable_userinfo(); 16 userInfo->set_acctid(201); 17 userInfo->set_name("user2"); 18 userInfo->set_status(ONLINE); 19 20 int length = retrieveResp.ByteSize(); 21 char* buf = new char[length]; 22 retrieveResp.SerializeToArray(buf,length); 23 24 RetrieveBuddiesResp retrieveResp2; 25 retrieveResp2.ParseFromArray(buf,length); 26 printf("BuddiesCount = %d\n",retrieveResp2.buddiescnt()); 27 printf("Repeated Size = %d\n",retrieveResp2.buddiesinfo_size()); 28 //這里僅提供了通過容器迭代器的方式遍歷數組元素的測試代碼。 29 //事實上,通過buddiesinfo_size和buddiesinfo函數亦可循環遍歷。 30 RepeatedPtrField<BuddyInfo>* buddiesInfo = retrieveResp2.mutable_buddiesinfo(); 31 RepeatedPtrField<BuddyInfo>::iterator it = buddiesInfo->begin(); 32 for (; it != buddiesInfo->end(); ++it) { 33 printf("BuddyInfo->groupID = %d\n", it->groupid()); 34 printf("UserInfo->acctID = %I64d, UserInfo->name = %s, UserInfo->status = %d\n" 35 , it->userinfo().acctid(), it->userinfo().name().c_str(),it->userinfo().status()); 36 } 37 delete [] buf; 38 }????? 最后需要說明的是,Protocol Buffer仍然提供了很多其它非常有用的功能,特別是針對序列化的目的地,比如文件流和網絡流等。與此同時,也提供了完整的官方文檔和規范的命名規則,在很多情況下,可以直接通過函數的名字便可獲悉函數所完成的工作。
轉載于:https://www.cnblogs.com/wnnily/p/5979817.html
總結
以上是生活随笔為你收集整理的Protocol Buffer技术详解(C++实例)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java,关键字static
- 下一篇: 用C++实现二分查找