转载

Google protobuf(C++) 学习笔记

本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循 署名-非商业用途-保持一致 的创作共用协议.

Google Protocol Buffers 简称 Protobuf, 是 Google 公司内部的混合语言数据标准. 它提供一种轻量, 高效的结构化数据存储结构.

简介

为什么学习protobuf?

想通过protobuf的序列化来做一个C++的微型RPC框架.本文主要是学习protobuf的使用, 大量参考官方文档.

为什么要使用protobuf?

  1. 因为 protobuf 是谷歌出的, 性能不过, 重要是的我是 谷歌脑残粉 (逃
  2. 官方文档中提到一些protobuf的优点, protobuf灵活高效的结构化数据存储格式. 方便用于序列化, 适合做RPC的数据交换.
  3. 相比 XML , protobuf 比 XML 更小、更快、更简单. 仅需要写一个 *.proto 文件描述需要的数据结构, protobuf会帮助你实现相关类和方法(自动化多好!).
  4. 目前提供 C++, Java, Python, Go, C#等多种语言 的API

安装

神奇Mac版homebrew帮你解决一切问题

# 安装最新的protobuf $ brew install protobuf  # 验证protobuf是否安装成功 $ protoc --version # 在我电脑上的输出 libprotoc 3.0.0 

学习笔记

通过一个简单的官方例子来学习protobuf

我们首先定义一个 addressbook.proto , 通过 message 来定义需要序列化的数据结构, message内包含许多key-value对

# addressbook.proto packagetutorial;  messagePerson{ requiredstringname =1; requiredint32id =2; optionalstringemail =3;  enumPhoneType{  MOBILE=0;  HOME=1;  WORK=2;  }  messagePhoneNumber{ requiredstringnumber =1; optionalPhoneType type =2[default = HOME];  }  repeatedPhoneNumber phone =4; }  messageAddressBook{ repeatedPerson person =1; } 

ps:

  1. message 中有多种类型, bool, int32, float, double, ,string and enum 以及内嵌的 message
  2. message 内可以定义optional、required、repeated字段

定义好*.proto文件后, 我们通过protobuf提供的protoc命令行工具来自动生成相关数据结构源码

// 编译.proto文件 $ protoc -I=$SRC_DIR--cpp_out=$DST_DIR$SRC_DIR/addressbook.proto  // 范例, 源目录为当前文件, 输出路径为当前文件 $ protoc -I=./ --cpp_out=./ ./addressbook.proto 
  • -I 指明源文件所在目录
  • --cpp_out 指向想要输出的文件路径
  • 最后一个参数为 .proto 所在的路径

使用protobuf

生成我们自定义的类后, 我们开始尝试使用生成的文件. add_person.cpp 用于从用户的输入中, 创建一个Person并初始化一个Person对象, 并将该对象通过二进制的形式写入给定文件中.

// 代码出自官方文档 // add_person.cpp  // // Created by Andrew_liu on 16/4/17. // #include<iostream> #include<fstream> #include<string> #include"addressbook.pb.h" usingnamespacestd;  // 基于用户输出填充Person message voidPromptForAddress(tutorial::Person* person) { cout<<"Enter person ID number: "; intid; cin>> id;  person->set_id(id); cin.ignore(256,'/n');  cout<<"Enter name: ";  getline(cin, *person->mutable_name());  cout<<"Enter email address (blank for none): "; stringemail;  getline(cin, email); if(!email.empty()) {  person->set_email(email);  }  while(true) { cout<<"Enter a phone number: "; stringnumber;  getline(cin, number); if(number.empty()) { break;  }  tutorial::Person::PhoneNumber* phone_number = person->add_phone();  phone_number->set_number(number);  cout<<"Is this a mobile, home, or work phone(type: mobile/home/work): "; stringtype;  getline(cin, type); if(type =="mobile") {  phone_number->set_type(tutorial::Person::MOBILE);  } elseif(type =="home") {  phone_number->set_type(tutorial::Person::HOME);  } elseif(type =="work") {  phone_number->set_type(tutorial::Person::WORK);  } else{ cout<<"Unknown phone type. Using default."<< endl;  }  } }  intmain(intargc,char* argv[]) {  GOOGLE_PROTOBUF_VERIFY_VERSION; if(argc !=2) { cerr<<"Usage: "<< argv[0] <<" ADDRESS_BOOK_FILE"<< endl; return-1;  }  tutorial::AddressBook address_book;   {  fstream input(argv[1], ios::in | ios::binary); if(!input) { cout<< argv[1] <<": File not found. Creating a new file. "<< endl;  } elseif(!address_book.ParseFromIstream(&input)) { cerr<<"Failed to parse address book."<< endl; return-1;  }  }  // add an address  PromptForAddress(address_book.add_person());   { // write new address book back to disk  fstream output(argv[1], ios::out | ios::trunc | ios::binary); if(!address_book.SerializeToOstream(&output)) { cerr<<"Failed to write address book."<< endl; return-1;  }  }  // delete all global object allocated by libprotobuf  google::protobuf::ShutdownProtobufLibrary(); return0; } 

list_person.cpp 通过给定的文件名读取Person对象, 并将每个Person对象输出.

// list_person.cpp // Created by Andrew_liu on 16/4/17. // #include<iostream> #include<fstream> #include<string> #include"addressbook.pb.h" usingnamespacestd;  voidListPeople(consttutorial::AddressBook& address_book) { for(inti =0; i < address_book.person_size(); i++) { consttutorial::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(intj =0; j < person.phone_size(); ++j) { consttutorial::Person::PhoneNumber& phone_number = person.phone(j);  switch(phone_number.type()) { casetutorial::Person::MOBILE: cout<<"Mobile phone #: "; break; casetutorial::Person::HOME: cout<<"Home phone #: "; break; casetutorial::Person::WORK: cout<<"Work phone #: ";  } cout<< phone_number.number() << endl;  }  } }  intmain(intargc,char* argv[]) {  GOOGLE_PROTOBUF_VERIFY_VERSION;  if(argc !=2) { cerr<<"Usage: "<< argv[0] <<" ADDRESS_BOOK_FILE"<< endl; return-1;  }   tutorial::AddressBook 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);  google::protobuf::ShutdownProtobufLibrary();  return0; } 

完成后, 我们分别编译两个源文件, 编译命令如下:

#编译add_person.cpp,生成writer可执行文件 $g++-owriteradd_person.cppaddressbook.pb.cc`pkg-config--cflags--libsprotobuf`  #编译list_person.cpp,生成reader可执行文件 g++-oreaderlist_person.cppaddressbook.pb.cc`pkg-config--cflags--libsprotobuf` 

图片为执行 writerreader 的结果

Google protobuf(C++) 学习笔记

实现RPC

package news; message NewsRequest { requiredstringmessage =1; }; message NewsResponse { requiredstringresponse =1; }; serviceNewsService{  rpc News(NewsRequest) returns (NewsResponse); }; 

参考链接

  • C++ Installation and Compile
  • Protocol Buffer Basics: C++
原文  http://andrewliu.in/2016/06/05/Google-protobuf-C-学习笔记/
正文到此结束
Loading...