以前玩 C,Json、XML 什麽的看多了,現在開始玩 C++,才發現我了解的世界太小了——原來 C++ 屆還有 Google Protocol Buffers 這麽好的東西。果然在 PC 上做開發真是好,不用考慮可執行程序的 size,可以放心放肆地用 C++。
Reference
Protocol Buffer Basics: C++
Google Protocol Buffers
Google Protocol Buffer 的使用和原理 - IBM
這是一份很有誠意的 Protocol Buffer 語法詳解
簡介
Protocol Buffers 又簡稱為 Protobuf、PB。是 Google 推出的一種數據交換格式。註意,這還是二進制的交換數據。
Protobuf 有自己的編譯器,在 linux 中叫做 protoc
,可以解釋 .proto
文件並且聲稱對應語言的源文件。目前 Google 提供了三種語言:Java, C++, python。後面我們就以 C++ 來說明,其他語言蕾絲。
總結一下:我們所說的 Protobuf,其實可以說是包含以下幾部分:
一種數據交換格式,可以將 C++ 中定義的存儲類的內容 與 二進制序列串 相互轉換,主要用於數據傳輸或保存
定義了一種源文件,擴展名為
.proto
,使用這種源文件,可以定義存儲類的內容Google 提供了一個編譯器
protoc
,可以編譯.proto
編譯成.cc
文件,使之成為一個可以在 C++ 工程中直接使用的類。類的功能非常完善,後文輝具體說明。
.proto 的簡單語法
我們來定義一個可以覆蓋大多數使用情況的例子,定義一個高中的班級和班上學生的信息
// --------------------------------------
// File: School.HighSchool.proto
//
package School.HighSchool; // 註意各種分號
message Person
{
optional int32 id = 0;
optional int32 age = 0;
optional string first_name;
optional string last_name;
optional bool is_female;
};
message Class
{
optional int32 grade_num;
optional int32 class_num;
optional Person head_teacher;
repeated Person students;
};
文件的建議命名為 “包名.消息名.proto
”。對於 C++ 而言,就是 “命名空間.數據類.proto
”。
上面這段的語義,我直接用 C++ 的概念來說明吧:
定義包的分類(命名空間)是 School 類別下的 HighSchool 子類別。
定義一個 個人 數據類,包含學生的 ID、姓、名、性別等等信息。
定義一個 班級 數據類,包含年級號和班級號、班主任信息、所有學生的信息。
這裏有必要說明 optional
和 repeated
。前者表示這個數據類型是可選的,也就是說有可能不存在這樣的一個數據信息。後者表示這個數據類型是多個的,可以理解為一個堆,或者說一個 set、一個集合,總之就是多個同類數據,類似於 C++ 中的 vector
。對應於 JSON 中的 array。Repeated 類型的數據有可能是空的(成員為 0)。
與 optional
相對應的是 required
類型,表示這個數據類型是必須的。但是,大部分資料都建議不要用這種類型。
生成 C++ 類
上面的源文件,可以使用以下命令進行編譯:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR School.HighSchool.proto
編譯完成後,生成兩個文件:School.HighSchool.pb.cc
和 School.HighSchool.pb.h
。
在類裏面,大體結構是這樣的:
namespace School {
namespace HighSchool {
class Person public ::google::protobuf::Message {
...
} // end of class Student
class Class public ::google::protobuf::Message {
...
} // end of class public
}} // end of namespaces
對象函數方法
對象方法
一般而言生成的類,都有對應整個類的操作方法,常用的幾個方法如下:
operator= (...)
CopyFrom (...)
MergeFrom (...)
ByteSize () const
Swap (...)
而最重要的兩個方法則是:
bool SerializeToString (string *output) const;
bool ParseFromString (const string &data);
作用分別是序列化和反序列化,也就是
將 PB 的內容序列化(二進制化)到指定的
string
對象中。從
string
類型中解析出 PB 對象
get / set 方法
對於具體的數據成員,則給出具體的 get / set
方法。比如 Person 類的 id
成員,C++ 類會提供以下方法:
inline bool has_id() const;
inline void clear_id();
static const int kInt32IdNumber = 0;
inline ::google::protobuf::int32 id() const;
inline void set_id(::google::protobuf::int32 value);
所有的方法都按照字面意思就可以讀懂,非常好理解。
而對於 repeated
屬性的成員,比如 students,則比較復雜,使用了 STL:
inline int students_size() const;
inline void clear_students_size();
inline const ::School::HighSchool::Person &students(int index) const;
inline ::School::HighSchool::Person *mutable_students(int index);
inline ::School::HighSchool::Person *add_students();
inline const ::google::protobuf::RepeatedPtrField <::School::HighSchool::Person> &students() const;
inline ::google::protobuf::RepeatedPtrField <::School::HighSchool::Person> *mutable_students();
看起來比較復雜,其實還是很好理解的。請讀者結合 C++ 的叠代器理解就好了。
支持的基本數據類型
Protobuf 中常用的基本數據類型及必要的說明如下:
double
float
int32
,int64
:負數的編碼效率低於sintXX
系列。當有負數,但是出現頻率不高時使用uint32
,uint64
sint32
,sint64
:當負數出現的頻率比較高時,比int32
的效率高fixed32
,fixed64
:註意,這不是 “定點數” 的意思,而是表示定長 4 字節的整形數據。如果數字長期大於 228 時,比int32
效率更高sfixed32
,sfixed64
bool
string
:ASCII / UTF-8 字符串bytes
:二進制序列
Tags: C++ 可以 Protocol Google 定義 proto
文章來源: