亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

Protocol Buffer技術深入理解(C++實例)

2020-01-26 16:22:39
字體:
來源:轉載
供稿:網友
這篇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++類的部分聲明,以及常用方法的說明性注釋。
復制代碼 代碼如下:

class LogonReqMessage : public ::google::protobuf::MessageLite {
public:
LogonReqMessage();
virtual ~LogonReqMessage();
// implements Message ----------------------------------------------
//下面的成員函數均實現自MessageLite中的虛函數。
//創建一個新的LogonReqMessage對象,等同于clone。
LogonReqMessage* New() const;
//用另外一個LogonReqMessage對象初始化當前對象,等同于賦值操作符重載(operator=)
void CopyFrom(const LogonReqMessage& from);
//清空當前對象中的所有數據,既將所有成員變量置為未初始化狀態。
void Clear();
//判斷當前狀態是否已經初始化。
bool IsInitialized() const;
//在給當前對象的所有變量賦值之后,獲取該對象序列化后所需要的字節數。
int ByteSize() const;
//獲取當前對象的類型名稱。
::std::string GetTypeName() const;
// required int64 acctID = 1;
//下面的成員函數都是因message中定義的acctID字段而生成。
//這個靜態成員表示AcctID的標簽值。命名規則是k + FieldName(駝峰規則) + FieldNumber。
static const int kAcctIDFieldNumber = 1;
//如果acctID字段已經被設置返回true,否則false。
inline bool has_acctid() const;
//執行該函數后has_acctid函數將返回false,而下面的acctid函數則返回acctID的缺省值。
inline void clear_acctid();
//返回acctid字段的當前值,如果沒有設置則返回int64類型的缺省值。
inline ::google::protobuf::int64 acctid() const;
//為acctid字段設置新值,調用該函數后has_acctid函數將返回true。
inline void set_acctid(::google::protobuf::int64 value);
// required string passwd = 2;
//下面的成員函數都是因message中定義的passwd字段而生成。這里生成的函數和上面acctid
//生成的那組函數基本相似。因此這里只是列出差異部分。
static const int kPasswdFieldNumber = 2;
inline bool has_passwd() const;
inline void clear_passwd();
inline const ::std::string& passwd() const;
inline void set_passwd(const ::std::string& value);
//對于字符串類型字段設置const char*類型的變量值。
inline void set_passwd(const char* value);
inline void set_passwd(const char* value, size_t size);
//可以通過返回值直接給passwd對象賦值。在調用該函數之后has_passwd將返回true。
inline ::std::string* mutable_passwd();
//釋放當前對象對passwd字段的所有權,同時返回passwd字段對象指針。調用此函數之后,passwd字段對象
//的所有權將移交給調用者。此后再調用has_passwd函數時將返回false。
inline ::std::string* release_passwd();
private:
... ...
};

下面是讀寫LogonReqMessage對象的C++測試代碼和說明性注釋。
復制代碼 代碼如下:

void testSimpleMessage()
{
printf("==================This is simple message.================/n");
//序列化LogonReqMessage對象到指定的內存區域。
LogonReqMessage logonReq;
logonReq.set_acctid(20);
logonReq.set_passwd("Hello World");
//提前獲取對象序列化所占用的空間并進行一次性分配,從而避免多次分配
//而造成的性能開銷。通過該種方式,還可以將序列化后的數據進行加密。
//之后再進行持久化,或是發送到遠端。
int length = logonReq.ByteSize();
char* buf = new char[length];
logonReq.SerializeToArray(buf,length);
//從內存中讀取并反序列化LogonReqMessage對象,同時將結果打印出來。
LogonReqMessage logonReq2;
logonReq2.ParseFromArray(buf,length);
printf("acctID = %I64d, password = %s/n",logonReq2.acctid(),logonReq2.passwd().c_str());
delete [] buf;
}

三、嵌套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++代碼和關鍵性注釋。
復制代碼 代碼如下:

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

下面是讀寫LogonRespMessage對象的C++測試代碼和說明性注釋。
復制代碼 代碼如下:

void testNestedMessage()
{
printf("==================This is nested message.================/n");
LogonRespMessage logonResp;
logonResp.set_logonresult(LOGON_RESULT_SUCCESS);
//如上所述,通過mutable_userinfo函數返回userInfo字段的指針,之后再初始化該對象指針。
UserInfo* userInfo = logonResp.mutable_userinfo();
userInfo->set_acctid(200);
userInfo->set_name("Tester");
userInfo->set_status(OFFLINE);
int length = logonResp.ByteSize();
char* buf = new char[length];
logonResp.SerializeToArray(buf,length);
LogonRespMessage logonResp2;
logonResp2.ParseFromArray(buf,length);
printf("LogonResult = %d, UserInfo->acctID = %I64d, UserInfo->name = %s, UserInfo->status = %d/n"
,logonResp2.logonresult(),logonResp2.userinfo().acctid(),logonResp2.userinfo().name().c_str(),logonResp2.userinfo().status());
delete [] buf;
}

四、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字段生成的代碼進行更為詳細的解釋。
復制代碼 代碼如下:

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

下面是讀寫RetrieveBuddiesResp對象的C++測試代碼和說明性注釋。
復制代碼 代碼如下:

void testRepeatedMessage()
{
printf("==================This is repeated message.================/n");
RetrieveBuddiesResp retrieveResp;
retrieveResp.set_buddiescnt(2);
BuddyInfo* buddyInfo = retrieveResp.add_buddiesinfo();
buddyInfo->set_groupid(20);
UserInfo* userInfo = buddyInfo->mutable_userinfo();
userInfo->set_acctid(200);
userInfo->set_name("user1");
userInfo->set_status(OFFLINE);
buddyInfo = retrieveResp.add_buddiesinfo();
buddyInfo->set_groupid(21);
userInfo = buddyInfo->mutable_userinfo();
userInfo->set_acctid(201);
userInfo->set_name("user2");
userInfo->set_status(ONLINE);
int length = retrieveResp.ByteSize();
char* buf = new char[length];
retrieveResp.SerializeToArray(buf,length);
RetrieveBuddiesResp retrieveResp2;
retrieveResp2.ParseFromArray(buf,length);
printf("BuddiesCount = %d/n",retrieveResp2.buddiescnt());
printf("Repeated Size = %d/n",retrieveResp2.buddiesinfo_size());
//這里僅提供了通過容器迭代器的方式遍歷數組元素的測試代碼。
//事實上,通過buddiesinfo_size和buddiesinfo函數亦可循環遍歷。
RepeatedPtrField<BuddyInfo>* buddiesInfo = retrieveResp2.mutable_buddiesinfo();
RepeatedPtrField<BuddyInfo>::iterator it = buddiesInfo->begin();
for (; it != buddiesInfo->end(); ++it) {
printf("BuddyInfo->groupID = %d/n", it->groupid());
printf("UserInfo->acctID = %I64d, UserInfo->name = %s, UserInfo->status = %d/n"
, it->userinfo().acctid(), it->userinfo().name().c_str(),it->userinfo().status());
}
delete [] buf;
}

最后需要說明的是,Protocol Buffer仍然提供了很多其它非常有用的功能,特別是針對序列化的目的地,比如文件流和網絡流等。與此同時,也提供了完整的官方文檔和規范的命名規則,在很多情況下,可以直接通過函數的名字便可獲悉函數所完成的工作。

本打算將該Blog中使用的示例代碼以附件的方式上傳,但是沒有發現此功能,望諒解。
上一篇:c++ int轉string方法

下一篇:返回列表

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
中文字幕亚洲精品| 一本色道久久综合狠狠躁篇的优点| 久久精品视频在线观看| 午夜精品免费视频| 欧美国产日韩xxxxx| 日韩av理论片| 欧美激情日韩图片| 日本道色综合久久影院| 8090成年在线看片午夜| 日韩一区在线视频| 精品日韩美女的视频高清| 国产视频精品va久久久久久| 亚洲国产一区二区三区在线观看| 国产精品大片wwwwww| 国产精品久久二区| 在线色欧美三级视频| 中文字幕欧美专区| 精品国产乱码久久久久久婷婷| 91精品啪在线观看麻豆免费| 亚洲欧美在线播放| 久久久久国产视频| 精品国产一区二区三区四区在线观看| 欧美黑人一级爽快片淫片高清| 日本精品在线视频| 不卡av在线网站| 日韩小视频在线| 久久久久久久影院| 国产精品免费福利| 精品女同一区二区三区在线播放| 欧美成人精品激情在线观看| 青青青国产精品一区二区| 亚洲成人av片在线观看| 色综合久久久888| 欧美一级大片视频| 中文字幕亚洲一区在线观看| 日韩av影院在线观看| 精品偷拍一区二区三区在线看| 欧美精品日韩www.p站| 一区二区成人av| 国产高清在线不卡| 国产精品99免视看9| 国产精彩精品视频| 国产精品美女av| 欧美极品少妇xxxxⅹ免费视频| 自拍偷拍亚洲欧美| 一区二区三区国产视频| www.日韩.com| 亚洲最大成人网色| 国产色综合天天综合网| 久久久久久久激情视频| 久久精品国产精品亚洲| 成人黄色免费在线观看| 日韩乱码在线视频| 国产欧美久久一区二区| 亚洲人成人99网站| 国产精品成人av在线| 97在线视频免费播放| 亚洲国产精品va在线看黑人动漫| 成人黄色免费片| 欧美另类69精品久久久久9999| 高跟丝袜一区二区三区| 国产精品稀缺呦系列在线| xxxx性欧美| 亚洲欧美国产制服动漫| 亚洲欧洲在线免费| 毛片精品免费在线观看| 久久成人18免费网站| 亚洲黄色www| 97在线观看免费| 免费不卡欧美自拍视频| 欧美一级bbbbb性bbbb喷潮片| 亚洲欧洲在线观看| 国产精品成人观看视频国产奇米| 欧美综合国产精品久久丁香| 亚洲福利视频免费观看| 欧美激情在线播放| 国产精品中文久久久久久久| 91国自产精品中文字幕亚洲| 国内精品久久影院| 久久国产精品偷| 国产精品网址在线| 欧美—级a级欧美特级ar全黄| 国产香蕉一区二区三区在线视频| 欧美高清一级大片| 亚洲香蕉成视频在线观看| 国产成人综合av| 欧美一级黑人aaaaaaa做受| 国产精品最新在线观看| 精品亚洲aⅴ在线观看| 日韩中文综合网| 欧美激情视频在线免费观看 欧美视频免费一| 国产亚洲精品成人av久久ww| 亚洲美女精品久久| 韩剧1988在线观看免费完整版| 久久精品影视伊人网| 海角国产乱辈乱精品视频| 日韩av电影国产| 伊人精品在线观看| 欧美一区二区三区四区在线| 日韩在线激情视频| 欧美亚洲第一页| 国产精品黄页免费高清在线观看| 国产一区视频在线播放| 国内精品久久久久久影视8| 亚洲成人a级网| 中文字幕亚洲欧美在线| 欧美色欧美亚洲高清在线视频| 国产91九色视频| 激情av一区二区| 国产午夜精品理论片a级探花| 国产精品v片在线观看不卡| 色偷偷噜噜噜亚洲男人| 97久久久免费福利网址| 欧美日韩性视频| 国内自拍欧美激情| 日韩高清有码在线| 亚洲精品美女在线观看| 久久99精品国产99久久6尤物| 中文字幕视频在线免费欧美日韩综合在线看| 欧美午夜丰满在线18影院| 精品国内亚洲在观看18黄| 国产视频精品一区二区三区| 欧美高清第一页| 狠狠躁夜夜躁人人躁婷婷91| 性色av香蕉一区二区| 亚洲视频精品在线| 97免费中文视频在线观看| 午夜精品久久久久久久99热浪潮| 欧美性黄网官网| 久久久国产精彩视频美女艺术照福利| 欧美激情一区二区三区在线视频观看| 国产精品99蜜臀久久不卡二区| 欧美日韩精品在线播放| 在线激情影院一区| 欧美亚洲第一区| 欧美亚洲在线播放| 57pao成人国产永久免费| 欧美视频中文字幕在线| 亚洲图片欧美午夜| 最近2019年日本中文免费字幕| 欧美夫妻性视频| 日韩美女视频免费在线观看| 国产日韩av在线| 亚洲在线第一页| 国产a∨精品一区二区三区不卡| 国产精品久久久久福利| 国产精品亚洲精品| 欧美性20hd另类| 欧美激情精品久久久久久变态| 亚洲护士老师的毛茸茸最新章节| 欧美精品18videosex性欧美| 亚洲精品久久久久久下一站| 国产精品日韩专区| 热门国产精品亚洲第一区在线| 国产精品99久久久久久久久久久久| 成人激情视频在线播放| 久久影院模特热| 性欧美激情精品| 在线性视频日韩欧美| 亚洲国产黄色片| 亚洲理论片在线观看| 成人国产亚洲精品a区天堂华泰| 日产精品久久久一区二区福利| 欧美精品videofree1080p|