首页 > 代码库 > Ggoogle Protocol Buffer的使用 (基于C++语言)
Ggoogle Protocol Buffer的使用 (基于C++语言)
首先说明的是Protocol Buffle是灵活高效的.它的一个很好的优点(很重要的,我认为)就是后向兼容性——当我们扩展了了.proto文件后,我们照样可以用它来读取之前生成的文件。
下面的例子,为了实现我们的一个应用 :通讯录,它包含一个人的名字,ID,电子邮件,联系电话,并实现存储与读取。
第一步,定义一个 .proto 文件
/* 首先是一个 package名称的声明,它的作用就是防止出现名字的冲突; 当生成C++代码的时候,对应的pacage名称会生成一个相同名字的命名空间。 */ package tutorial; message Person { required string name = 1; required int32 id = 2; optional string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { required string number = 1; optional PhoneType type = 2 [default = HOME]; } repeated PhoneNumber phone = 4; } message AddressBook { repeated Person person = 1; }
第二步:用生成protobuf 编译器编译写的.proto文件。
命令为:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/addressbook.proto其中,-I=$SRC_DIR,表示指明源文件的目录,也可以用 –proto_path, 它们是等价的。当我们有import .proto文件时,它也会从这里指定的目录去搜索。
-cpp_out=$DST_DIR, –cpp_out,表示我们输出的是C++类型的代码,$DST_DIR表示输出的目录。
后面的$SRC_DIR/addressbook.proto表示我们经编译的源文件,它在-I指定的目录下的哦;
运行完命令后,会输出.pb.cc文件与.pb.h文件。
例如,我的.proto文件名为:tutorial.addressbook.proto, 那么我生成的文件为: tutorial.addressbook.pb.h和tutorial.addressbook.pb.cc文件。
第三步,在我们写的程序中调用Protocol Buffe的C++的API来使用我们定义的AddressBook类。
在我们想知道怎么使用它的时候,不防先看看生成的文件里面是什么,让我们打开 tutorial.addressbook.bh.h文件,比如Person类开定义,我们会发现如下的代码:
239 // accessors ------------------------------------------------------- 240 241 // required string name = 1; 242 inline bool has_name() const; 243 inline void clear_name(); 244 static const int kNameFieldNumber = 1; 245 inline const ::std::string& name() const; 246 inline void set_name(const ::std::string& value); 247 inline void set_name(const char* value); 248 inline void set_name(const char* value, size_t size); 249 inline ::std::string* mutable_name(); 250 inline ::std::string* release_name(); 251 inline void set_allocated_name(::std::string* name); 252 253 // required int32 id = 2; 254 inline bool has_id() const; 255 inline void clear_id(); 256 static const int kIdFieldNumber = 2; 257 inline ::google::protobuf::int32 id() const; 258 inline void set_id(::google::protobuf::int32 value); 259 260 // optional string email = 3; 261 inline bool has_email() const; 262 inline void clear_email(); 263 static const int kEmailFieldNumber = 3; 264 inline const ::std::string& email() const; 265 inline void set_email(const ::std::string& value); 266 inline void set_email(const char* value); 267 inline void set_email(const char* value, size_t size); 268 inline ::std::string* mutable_email(); 269 inline ::std::string* release_email(); 270 inline void set_allocated_email(::std::string* email); 271 272 // repeated .tutorial.Person.PhoneNumber phone = 4; 273 inline int phone_size() const; 274 inline void clear_phone(); 275 static const int kPhoneFieldNumber = 4; 276 inline const ::tutorial::Person_PhoneNumber& phone(int index) const; 277 inline ::tutorial::Person_PhoneNumber* mutable_phone(int index); 278 inline ::tutorial::Person_PhoneNumber* add_phone(); 279 inline const ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >& 280 phone() const; 281 inline ::google::protobuf::RepeatedPtrField< ::tutorial::Person_PhoneNumber >* 282 mutable_phone();上面它就是定义了访问Person类成员函数的方法。如,可以通过name()方法得到它的值。可以通过set_name()方法设置它的值。 通过has_name()判断是否有这个值,通过clear_name()清空它的值。
另外,string类型也numeic类型相比较,还多出了几项,如通过mutable_name()可以得到指针的。
还有,对于repeated的关键字的来说 ,又有一些不同。如,可以通过name_size()得到有多个项(就是它因为可以重复嘛,重复了多少次呢),也可以通过 index 得到特定的第几个的值,等等。
如果想要详细看的话,可以https://developers.google.com/protocol-buffers/docs/reference/cpp-generated看这里哈。
它,也提供了可以能message进行全局操作的标准message 方法, 如:
bool IsInitialized() const; 用于检查是不是所有的required 字段已经初始化、
string DebugString() const; 返回一个人可以读懂的representation of the message,在debug时,很有用的。
void CopyFrom(const Person& from), 用另一个类为一个类赋值;
void Clear() 把类中的所有元素的值变为空状态(unset)
想了解更多,可以看https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message。
解析与序列化(就是读与写啦)
如:bool SerializeToString(string* output) const ,序列化我们的Message,并把得到的bytes放到string类型中。(注意,这里的bytes仍然是二进制文件,我们只不过使用string作为一个容器来装我们的bytes.)
bool ParseFromString(const string& data); 解析;
bool SerializeToOstream(ostream* output) const;
bool ParseFromIstream(istream* input);
更多见:https://developers.google.com/protocol-buffers/docs/reference/cpp/google.protobuf.message.html#Message
编写我们自己的程序:
写文件的程序:
1 #include<iostream> 2 #include<fstream> 3 #include<string> 4 $include "tutorial.addressbook.pb.h" 5 using namespace std; 6 7 //定义一个函数,用于设置person对象的值; 8 //参数为: 一个person 对象的指针; 返回为空; 9 void PromptForAddress(tutorial::Person* person) { 10 cout << "Enter person ID number :"; 11 int id; 12 cin >> id; 13 person->set_id(id); 14 cin.ignore(256, ‘\n‘) //作用:用于清除缓冲区的内容,防止对下次输入产生影响. 15 16 cout << "Enter name: "; 17 getline(cin, *person->mutable_name()); //mutable_name()返回一个指针,再加上*,要干什么?看来要补补getline()函数了; 18 19 cout<< "Enter email address(blank for none): "; 20 string email; 21 getline(cin, email); 22 if (!email.empty()) { 23 person->set_email(email); 24 } 25 26 while (true) { 27 cout << "Enter a phone number (or leave blank to finish): "; 28 string number; 29 getline(cin, number); 30 if (number.empty()) { 31 break; 32 } 33 34 35 tutorial::Person::PhoneNumber* phone_number = person->add_phone(); 36 phone_number->set_number(number); 37 38 cout <<"Is this a mobile, home, or work phone? "; 39 string type; 40 getline(cin, type); 41 if (type == "mobile") { 42 phone_number->set_type(tutorial::Person::MOBILE); 43 } else if (type == "home") { 44 phone_number->set_type(tutorial::Person::HOME); 45 } else if (type =="work") { 46 phone_number->set_type(tutorial::Person::WORK); 47 } else { 48 cout << "Unknown phone type. Using default." <<endl; 49 } 50 } 51 } 52 53 54 int main(int argc, char* argv[]) { 55 GOOGLE_PROTOBUF_VERIFY_VERSION; //这是一个宏命令,用于检查看看用的protobuf的库是否兼容;最好加上它; 56 57 if (argc !=2) { 58 cerr << "Usage: " << argv[0] << "ADDRESS_BOOK_FILE" << endl; 59 return -1; 60 } 61 62 tutorial::AddressBook address_book; 63 64 //读文件,如果不存在,就创建; 65 fstream input(argv[1], ios::in | ios::binary); 66 if (!input) { 67 cout << argv[1] << ":File not found, Creating a new file." <<endl; 68 } else if (!address_book.ParseFromeIstream(&input)) { 69 cerr << "Failed to parse address book." << endl; 70 return -1; 71 } 72 73 //增加address; 74 PromptForAddress(address_book.add_person()); 75 76 //写放盘中; 77 fstream output(argv[1], ios::out | ios::trunc | ios::binary); 78 if (!address_book.SerializeToOstream(&output)) { 79 cerr << "Failed to write address book." << endl; 80 return -1; 81 } 82 83 //用于把全局的由libprotobuf申请的对象空间; 84 google::protobuf::ShutdownProtobufLibrary(); 85 86 return 0; 87 }
GOOGLE_PROTOBUF_VERIFY_VERSION是一个宏命令,我们最好加上它,因为它的作用可以用于检查我们的library是否不兼的问题。如果 你不想加,也没有办法哦;
ShutdownProtobufLibrary() 它的作用为:delete any global objects that were allocated by the Protocol Buffer library.通常或许我们不用加它,因为我们运行完我们的程序,我们就退出了,然后操作系统会回收内存的。但是如果不加的话,当我们用内存泄漏检查器检查程序时,它会说内存泄漏的。
读取文件的程序:
#include <iostream> #include <fstream> #include <string> #include "addressbook.pb.h" using namespace std; // Iterates though all people in the AddressBook and prints info about them. void ListPeople(const tutorial::AddressBook& address_book) { for (int i = 0; i < address_book.person_size(); i++) { const tutorial::Person& person = address_book.person(i); cout << "Person ID: " << person.id() << endl; cout << " Name: " << person.name() << endl; if (person.has_email()) { cout << " E-mail address: " << person.email() << endl; } for (int j = 0; j < person.phone_size(); j++) { const tutorial::Person::PhoneNumber& phone_number = person.phone(j); switch (phone_number.type()) { case tutorial::Person::MOBILE: cout << " Mobile phone #: "; break; case tutorial::Person::HOME: cout << " Home phone #: "; break; case tutorial::Person::WORK: cout << " Work phone #: "; break; } cout << phone_number.number() << endl; } } } // Main function: Reads the entire address book from a file and prints all // the information inside. int main(int argc, char* argv[]) { // Verify that the version of the library that we linked against is // compatible with the version of the headers we compiled against. GOOGLE_PROTOBUF_VERIFY_VERSION; if (argc != 2) { cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; return -1; } tutorial::AddressBook address_book; { // Read the existing address book. fstream input(argv[1], ios::in | ios::binary); if (!address_book.ParseFromIstream(&input)) { cerr << "Failed to parse address book." << endl; return -1; } } ListPeople(address_book); // Optional: Delete all global objects allocated by libprotobuf. google::protobuf::ShutdownProtobufLibrary(); return 0; }
很简单吧;
另外,说一点代码风格:
1. 消息名字:大写字母开头的驼峰表示法表示消息名称,如:SongServerRequest。
2. 字段名: 使用小写字母+下划线分隔表示字段名,如:song_name。
message SongServerRequest { required string song_name = 1; }
3. 枚举类型:大写字母开头的驼峰表示法表示枚举名称,使用大写字母+下划线表示值
enum Foo { FIRST_VALUE = 1; SECOND_VALUE = 2; }
参考:
https://developers.google.com/protocol-buffers/docs/cpptutorial
https://my.oschina.net/macwe/blog/157862
最后,如果你觉得以上内容对你提供了实质的帮助, 如果你有钱的话,如果你愿意的话,可以打赏一下我这个穷学生哦,资助我买几本书.
钱也不用太多哦, 您可以选择:2毛,5毛、1元。(靠知识创造价值, 靠知识改变生活 ——爱你们的杰克船长,么么哒)
Ggoogle Protocol Buffer的使用 (基于C++语言)