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++類的部分聲明,以及常用方法的說明性注釋。

復制代碼
 1     class LogonReqMessage : public ::google::protobuf::MessageLite {
 2     public:  3  LogonReqMessage();  4 virtual ~LogonReqMessage();  5  6 // implements Message ----------------------------------------------  7 //下面的成員函數均實現自MessageLite中的虛函數。  8 //創建一個新的LogonReqMessage對象,等同于clone。  9 LogonReqMessage* New() const; 10 //用另外一個LogonReqMessage對象初始化當前對象,等同于賦值操作符重載(operator=) 11 void CopyFrom(const LogonReqMessage& from); 12 //清空當前對象中的所有數據,既將所有成員變量置為未初始化狀態。 13 void Clear(); 14 //判斷當前狀態是否已經初始化。 15 bool IsInitialized() const; 16 //在給當前對象的所有變量賦值之后,獲取該對象序列化后所需要的字節數。 17 int ByteSize() const; 18 //獲取當前對象的類型名稱。 19 ::std::string GetTypeName() const; 20 21 // required int64 acctID = 1; 22 //下面的成員函數都是因message中定義的acctID字段而生成。 23 //這個靜態成員表示AcctID的標簽值。命名規則是k + FieldName(駝峰規則) + FieldNumber。 24 static const int kAcctIDFieldNumber = 1; 25 //如果acctID字段已經被設置返回true,否則false。 26 inline bool has_acctid() const; 27 //執行該函數后has_acctid函數將返回false,而下面的acctid函數則返回acctID的缺省值。 28 inline void clear_acctid(); 29 //返回acctid字段的當前值,如果沒有設置則返回int64類型的缺省值。 30 inline ::google::protobuf::int64 acctid() const; 31 //為acctid字段設置新值,調用該函數后has_acctid函數將返回true。 32 inline void set_acctid(::google::protobuf::int64 value); 33 34 // required string passwd = 2; 35 //下面的成員函數都是因message中定義的passwd字段而生成。這里生成的函數和上面acctid 36 //生成的那組函數基本相似。因此這里只是列出差異部分。 37 static const int kPasswdFieldNumber = 2; 38 inline bool has_passwd() const; 39 inline void clear_passwd(); 40 inline const ::std::string& passwd() const; 41 inline void set_passwd(const ::std::string& value); 42 //對于字符串類型字段設置const char*類型的變量值。 43 inline void set_passwd(const char* value); 44 inline void set_passwd(const char* value, size_t size); 45 //可以通過返回值直接給passwd對象賦值。在調用該函數之后has_passwd將返回true。 46 inline ::std::string* mutable_passwd(); 47 //釋放當前對象對passwd字段的所有權,同時返回passwd字段對象指針。調用此函數之后,passwd字段對象 48 //的所有權將移交給調用者。此后再調用has_passwd函數時將返回false。 49 inline ::std::string* release_passwd(); 50 private: 51  ... ... 52 };
復制代碼

????? 下面是讀寫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++代碼和關鍵性注釋。

復制代碼
 1     class LogonRespMessage : public ::google::protobuf::MessageLite {
 2     public:  3  LogonRespMessage();  4 virtual ~LogonRespMessage();  5  6 // implements Message ----------------------------------------------  7 ... ... //這部分函數和之前的例子一樣。  8  9 // required .LoginResult logonResult = 1; 10 //下面的成員函數都是因message中定義的logonResult字段而生成。 11 //這一點和前面的例子基本相同,只是類型換做了枚舉類型LoginResult。 12 static const int kLogonResultFieldNumber = 1; 13 inline bool has_logonresult() const; 14 inline void clear_logonresult(); 15 inline LoginResult logonresult() const; 16 inline void set_logonresult(LoginResult value); 17 18 // required .UserInfo userInfo = 2; 19 //下面的成員函數都是因message中定義的UserInfo字段而生成。 20 //這里只是列出和非消息類型字段差異的部分。 21 static const int kUserInfoFieldNumber = 2; 22 inline bool has_userinfo() const; 23 inline void clear_userinfo(); 24 inline const ::UserInfo& userinfo() const; 25 //可以看到該類并沒有生成用于設置和修改userInfo字段set_userinfo函數,而是將該工作 26 //交給了下面的mutable_userinfo函數。因此每當調用函數之后,Protocol Buffer都會認為 27 //該字段的值已經被設置了,同時has_userinfo函數亦將返回true。在實際編碼中,我們可以 28 //通過該函數返回userInfo字段的內部指針,并基于該指針完成userInfo成員變量的初始化工作。 29 inline ::UserInfo* mutable_userinfo(); 30 inline ::UserInfo* release_userinfo(); 31 private: 32  ... ... 33 }; 
復制代碼

? ? ? 下面是讀寫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字段生成的代碼進行更為詳細的解釋。

復制代碼
 1     class RetrieveBuddiesResp : public ::google::protobuf::MessageLite {
 2     public:  3  RetrieveBuddiesResp();  4 virtual ~RetrieveBuddiesResp();  5  6 ... ... //其余代碼的功能性注釋均可參照前面的例子。  7  8 // repeated .BuddyInfo buddiesInfo = 2;  9 static const int kBuddiesInfoFieldNumber = 2; 10 //返回數組中成員的數量。 11 inline int buddiesinfo_size() const; 12 //清空數組中的所有已初始化成員,調用該函數后,buddiesinfo_size函數將返回0。 13 inline void clear_buddiesinfo(); 14 //返回數組中指定下標所包含元素的引用。 15 inline const ::BuddyInfo& buddiesinfo(int index) const; 16 //返回數組中指定下標所包含元素的指針,通過該方式可直接修改元素的值信息。 17 inline ::BuddyInfo* mutable_buddiesinfo(int index); 18 //像數組中添加一個新元素。返回值即為新增的元素,可直接對其進行初始化。 19 inline ::BuddyInfo* add_buddiesinfo(); 20 //獲取buddiesInfo字段所表示的容器,該函數返回的容器僅用于遍歷并讀取,不能直接修改。 21 inline const ::google::protobuf::RepeatedPtrField< ::BuddyInfo >& 22 buddiesinfo() const; 23 //獲取buddiesInfo字段所表示的容器指針,該函數返回的容器指針可用于遍歷和直接修改。 24 inline ::google::protobuf::RepeatedPtrField< ::BuddyInfo >* 25  mutable_buddiesinfo(); 26 private: 27  ... ... 28 };
復制代碼

? ? ? 下面是讀寫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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457114.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457114.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457114.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

yaml 解決問題

yaml 是什么&#xff1f; 在 github 一些開源項目里經常可以看到 .travis.yml 文件&#xff0c;后來接觸持續集成這個概念時發現很多文件都是 .yml 后綴的文件。我在 阮一峰-YAML 語言教程 里了解了它的作用以及基本寫法&#xff0c;他的那篇文章描述的略微有點啰嗦&#xff0…

2016 7 25 鏈表

1 #include<stdio.h>2 #include<stdlib.h>3 /* 4 usingnamespacestd;5 6 structNode7 {8 int data;//數據域9 struct Node*next;//指針域10 };11 12 /*13 Create14 *函數功能&#xff1a;創建鏈表.15 *輸入&#xff1a;各節點的data16 *返回值&#xff1a;指…

php數值操作,php數值計算num類簡單操作示例

php數值計算num類簡單操作示例,在線,計算器,小數,整數,程序設計php數值計算num類簡單操作示例易采站長站&#xff0c;站長之家為您整理了php數值計算num類簡單操作示例的相關內容。本文實例講述了php數值計算num類簡單操作。分享給大家供大家參考&#xff0c;具體如下&#xff…

YAML-學習筆記!

YAML學習筆記 一、YAML簡介 YAML&#xff0c;即YAML Ain’t Markup Language的縮寫&#xff0c;YAML 是一種簡潔的非標記語言。YAML以數據為中心&#xff0c;使用空白&#xff0c;縮進&#xff0c;分行組織數據&#xff0c;從而使得表示更加簡潔易讀。 二、YAML語法 1、基本規…

CentOS遠程監控

近日&#xff0c;因工作需要&#xff0c;學習了CentOS遠程監控的水平有限&#xff0c;多指教。 遠程訪問CentOS&#xff0c;包括三種方式ssh&#xff0c;telnet&#xff0c;vnc。 本例涉及的是以vnc遠程訪問CentOS。指令在root下操作。注意&#xff1a;vnc的端口為5900&#xf…

aix oracle 10.2.0.1 升級 10.2.0.4,AIX Oracle RAC 升級到10.2.0.4.0要特別注意的問題 - 愛肯的專欄 ......

AIX Oracle RAC 升級到10.2.0.4.0過程有如下報錯(直接升級到10.2.0.4不需要應用Patch:6160398)&#xff1a;rootbwgl_db2:/u01/app/oracle/crs_1/bin# /u01/app/oracle/crs_1/install/root102.shError : Please change the CRS_ORACLE_USER id to have the following OS capabi…

將文件提交到github的兩種方法

方法一 簡要步驟如下&#xff1a; 登陸github&#xff0c;創建git倉庫。記此git倉庫的地址為[github_repository_url]&#xff0c;例如git倉庫的地址&#xff1a;https://github.com/galian123/nodejs_http_server 在本地的工程目錄執行git init&#xff0c;此工程目錄是要提…

Visual Studio 2017新版發布,極大提高開發效率丨附下載

最新的Visual Studio 2017免費下載&#xff1a;https://www.evget.com/product...【包含Professional、Enterprise、Community版本】 為任何開發、應用和平臺提供無與倫比的效率&#xff0c;Visual Studio 2017 候選發布。 提高了效率 代碼導航、IntelliSense、重構、代碼修復和…

BGP路由協議詳解(完整篇)

原文鏈接&#xff1a;http://xuanbo.blog.51cto.com/499334/465596/ 2010-12-27 12:02:45 上個月我寫一篇關于BGP協議的博文&#xff0c;曾許諾過要完善這個文檔&#xff0c;但因最近的工作和授課很忙&#xff0c;所以沒有時間進行完善。為了實現這個承諾&#xff0c;我在去外…

oracle 備份導出,oracle 怎么備份或導入導出表

exp/imp下面介紹的是導入導出的實例&#xff0c;向導入導出看實例基本上就可以完成&#xff0c;因為導入導出很簡單。數據導出&#xff1a;1 將數據庫TEST完全導出,用戶名system 密碼manager 導出到D:\daochu.dmp中exp system/managerTEST filed:\daochu.dmp fully2 將數據庫中…

GitHub+Hexo搭建自己的Blog之-本地環境部署01

前言 之前我的博客沒有綁自己的域名&#xff0c;一直在github上放著&#xff0c;訪問起來比較麻煩&#xff0c;前陣子在阿里云買了這個域名&#xff0c;配置上之后&#xff0c;就可以通過自己的域名訪問了&#xff0c;有些朋友問我這個博客怎么搭的&#xff0c;用的什么主題&am…

appsettings 連接oracle數據庫,ABP .net core集成訪問Oracle數據庫

1.添加包引用&#xff1a;Microsoft.EntityFrameworkCore.RelationalOracle.EntityFrameworkCore2.重寫DbContext OnModelCreating/// /// 判斷如果是Oracle&#xff0c;需要執行Schema/// /// protected override void OnModelCreating(ModelBuilder modelBuilder){//判斷當前…

Day08-函數(3)

import functools #偏函數 def demo(a,b,c,d):print(a,b,c,d)# def partital_demo(a,b): # demo(a,b,3,4) # partital_demo(1,2)#實現偏函數 # #固定前兩個 # demo functools.partial(demo,5,6) # demo(10,50)# #固定后兩個 # demofunctools.partial(demo,c3,d5) # demo(1…

使用VS2010編譯Qt 5.6.1過程記錄

由于Qt官方發布的Qt 5.6.1二進制安裝包沒有對應VS2010版本的&#xff0c;而我的電腦上只安裝了VS2010&#xff0c;因此只能自己編譯。 本文記錄本人的編譯安裝過程&#xff0c;以及其中遇到的一些問題。 本文使用VS2010 32位編譯Qt 5.6.1。 1. 下載Qt源代碼 Qt源代碼下載地址&…

golang 指針

指針1、簡單的定義一個指針package mainimport "fmt"func main(){var p *inti :2p &ifmt.Println("memory address i:",p) } //執行結果&#xff1a; memory address i: 0xc42000a2e02、打印指針的值package mainimport "fmt"func main(){v…

php防錯處理,更好的PHP錯誤處理

錯誤類型PHP 主要有兩種錯誤&#xff1a;觸發錯誤和異常。其中觸發錯誤大概可以分為&#xff1a;編譯錯誤、引擎錯誤和運行時錯誤&#xff0c;其中前兩個是無法捕獲的&#xff1b;異常都是可以捕獲的&#xff0c;當沒有嘗試捕獲時則會中斷代碼。觸發錯誤可以通過 error_get_las…

Windows安裝Apache注冊服務出現(OS 5)拒絕訪問。 : AH00369: Failed to open the Windows service manager,

windows安裝Apache&#xff0c;注冊服務出現“(OS 5)拒絕訪問。 : AH00369: Failed to open the WinNT service manager..."錯誤 在安裝Apache的時候&#xff0c;我下載的是zip格式&#xff0c;不是msi安裝版&#xff0c;需要自己注冊服務&#xff0c;才能在桌面任務欄里有…

算法與數據結構(三) 二叉樹的遍歷及其線索化(Swift版)

前面兩篇博客介紹了線性表的順序存儲與鏈式存儲以及對應的操作&#xff0c;并且還聊了棧與隊列的相關內容。本篇博客我們就繼續聊數據結構的相關東西&#xff0c;并且所涉及的相關Demo依然使用面向對象語言Swift來表示。本篇博客我們就來介紹樹結構的一種&#xff1a;二叉樹。在…

關于android開發時,發生Error infalting classa com.baidu.mapapi.map.MapView的解決辦法

1.問題描述&#xff1a;百度地圖SDK中 Error&#xff1a; infalting classa com.baidu.mapapi.map.MapView 。 2.解決辦法&#xff1a;通過1個多小時的上網搜索&#xff0c;最終發現很多網友之所以出現這方面的問題有以下幾種原因&#xff1a; &#xff08;1&#xff09;.忘…

c++動態綁定的技術實現

1 什么是動態綁定 有一個基類&#xff0c;兩個派生類&#xff0c;基類有一個virtual函數&#xff0c;兩個派生類都覆蓋了這個虛函數。現在有一個基類的指針或者引用&#xff0c;當該基類指針或者引用指向不同的派生類對象時&#xff0c;調用該虛函數&#xff0c;那么最終調用的…