本文参考自官方文档以及大佬们博客,实测完成。
参考:官方安装文档
sudo apt-get install autoconf automake libtool curl make g++ unzip
在线下载网址:https://github.com/protocolbuffers/protobuf/releases/,然后解压并安装。
也可以通过git安装:
//1. 下载最新版本
git clone https://github.com/protocolbuffers/protobuf.git
cd protobuf
git submodule update --init --recursive
./autogen.sh
//2. build 并 install
./configure
make
make check
sudo make install
sudo ldconfig # refresh shared library cache.
//3. 验证
protoc --version
输出:libprotoc 3.6.1
安装位置
默认安装位置在/usr/local
,然而大部分平台中usr/local/lib
并没有添加在环境变量LD_LIBRARY_PATH
中。简单起见,可以直接选择在环境变量中的路径,比如/usr
进行安装:
./configure --prefix=/usr
config 出错则使用命令make clean
进行清除配置。
pkg-config --cflags protobuf # print compiler flags
pkg-config --libs protobuf # print linker flags
pkg-config --cflags --libs protobuf # print both
比如后面我们编译代码,便利用了其中的lib选项:
g++ -std=c++11 list_person.cpp Person.pro.pb.cc -o list_people `pkg-config --cflags --libs protobuf`
参考官网实验(proto2):https://developers.google.com/protocol-buffers/docs/cpptutorial
结合这位博主已经用proto3修改:https://blog.csdn.net/hailong0715/article/details/52057873?utm_source=blogxgwz5
定义一个ProtoBuf源文件: Person.pro
syntax="proto3";
package tutorial;
message Person
{
string name = 1;
int32 id = 2;
string email = 3;
enum PhoneType
{
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber
{
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;
}
message AddressBook
{
repeated Person person =1;
}
可以看到该文件中,有一点类似C语言结构体的风格,自定义数据结构类型并且可以嵌套。Person类型中包含了三个基本类型:name
,id
,email
,同时又包含一个复合类型PhoneNumber
。
编译该文件:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/Person.proto
//这里在当前路径下编译即可
protoc -I=./ --cpp_out=./ ./Person.pro
编译后生成Person.proto,pb.h
和Person.pro.pb.cc
文件,这两个文件包含了Person以及AddressBook的类,以及对应操作类的方法,在你使用者这些类必须包含头文件,最终可执行文件也必须将cc文件也编译进去。
add_person.cpp 展示了如何设置一个联系人的各个字段的信息,并且最终序列化信息到文件中。
//add_person.cpp
#include "Person.pro.pb.h"
#include
#include
using namespace std;
void PromptForAddress(tutorial::Person*);
int main(int argc, char* argv[])
{
GOOGLE_PROTOBUF_VERIFY_VERSION;
if(2 != argc)
{
//必须指定电话本名称才执行程序
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
tutorial::AddressBook address_book;
fstream in("ADDRESS_BOOK_FILE", ios::binary | ios::in);
if(!in)
{
cerr << "open file ADDRESS_BOOK_FILE failed!\n";
return -1;
}
if(!address_book.ParseFromIstream(&in))
{
cerr << "Parse File ADDRESS_BOOK_FILE failed!\n";
return -1;
}
in.close();
//增加一个Person,可通过多次调用该接口增加联系人
//具有repeated的属性可通过add_fieldname方法增加一个属性
PromptForAddress(address_book.add_person());
fstream out("ADDRESS_BOOK_FILE", ios::binary | ios::out | ios::trunc);
if(!address_book.SerializeToOstream(&out))
{
cerr << "Failed to Write Address Book!\n";
return -1;
}
//可选的,回收所有ProtoBuf分配的对象
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
void PromptForAddress(tutorial::Person* person)
{
cout<<"Enter a Person ID number: ";
int id;
cin >> id;
person->set_id(id);
/*忽略CIN的前256个字符,或者忽略CIN的换行符之前的字符,包括换行符
这样的话不会将换行符之前的其他类型的数据保留在输入缓冲中
*/
cin.ignore(256, '\n');
cout<<"Enter name: ";
getline(cin, *person->mutable_name());
cout<< "Enter email address (blank for none): ";
string email;
getline(cin,email);
if(!email.empty())
person->set_email(email);
while(true)
{
cout<<"Enter a phone number (or leave blank to finish): ";
string number;
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? ";
string type;
getline(cin, type);
if(type == "mobile")
phone_number->set_type(tutorial::Person::MOBILE);
else if( type == "home")
phone_number->set_type(tutorial::Person::HOME);
else if (type == "work")
phone_number->set_type(tutorial::Person::WORK);
else
{
cout << "Unknown phone type. Using default." << endl;
phone_number->set_type(tutorial::Person::HOME);
}
}
}
list_person.cpp展示了如何从文件中读取ProtoBuf序列化的信息
//list_person.cpp
#include "Person.pro.pb.h"
#include
#include
using namespace std;
void ListPeople(const tutorial::AddressBook& address_book);
int main(int argc, char * argv[])
{
if(2!=argc)
{
cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
return -1;
}
fstream in("ADDRESS_BOOK_FILE", ios::in | ios::binary);
tutorial::AddressBook address_book;
if(!address_book.ParseFromIstream(&in))
{
cerr << "Parse Input File failed!"<< endl;
return -1;
}
ListPeople(address_book);
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
void ListPeople(const tutorial::AddressBook& address_book)
{
//fieldName_size方法返回具有repeated属性的个数
for(int i=0; i< address_book.person_size();i++)
{
const tutorial::Person& person = address_book.person(i);
cout<<"Person ID: "<<person.id();
cout<<"Name: "<<person.name();
cout<<"Email: "<<person.email();
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;
}
}
}
Makefile
test:add_person list_person
add_person: add_person.cpp protoMid
g++ -std=c++11 list_person.cpp Person.pro.pb.cc -o add_person `pkg-config --cflags --libs protobuf`
list_person: list_person.cpp protoMid
g++ -std=c++11 add_person.cpp Person.pro.pb.cc -o list_person `pkg-config --cflags --libs protobuf`
protoMid: Person.pro
protoc -I=./ --cpp_out=./ ./Person.pro
clean:
rm -rf add_person list_person protoMid
rm -rf Person.pro.pb.cc Person.pro.pb.h
其中,protoMid
则利用了protoc
将Person.pro文件生成对应的.cpp和.h文件
执行make后,生成add_people 和list_people两个可执行文件,可以先执行add_people文件增加联系人信息,再执行list_people显示所有联系人信息
goole官网proto3文档
goole官网proto2实验
Protocol Buffer技术详解(语言规范)
Protocol Buffer技术详解(C++实例)
官方译文:Protobuf3语言指南