下载ProtoBuff
- 从github下载:
https://github.com/protocolbuffers/protobuf
说明:
直接clone下来的代码,没有子模块(引用的其他第三方模块的代码),比如third_party/googletest;
- 建议直接下载release最新版:
https://github.com/protocolbuffers/protobuf/releases
编译ProtoBuff
- 解压protobuf-3.13.0后,命令行进入protobuf-3.13.0目录
cd D:\work\program\open_source\protobuf-all-3.13.0\protobuf-3.13.0
- 新建一个编译输出路径
mkdir build_1 && cd build_1
- 执行cmake, 生成VS2017工程文件;
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" ../cmake -G "Visual Studio 15 2017 Win64"
说明:
-G "Visual Studio 15 2017 Win64" 表示生成VS2017 X64工程;
../cmake 表示CMakeLists.txt文件的路径
生成include文件
extract_includes.bat
打开build_1/protobuf.sln,进行编译
这样,就得到了我们需要的头文件、protoc.exe、libprotobufd.lib(PS:libprotobuf只提供了静态库),在如下两个目录:
protobuf-3.13.0/build_1/Debug
protobuf-3.13.0/build_1/include
注意:
Protobuf的所有工程默认运行库是MT/MTd,建议都改成MD/MDd;
如果不修改,那么你的工程也必须设置成MT/MTd才能编译通过,这样如果在使用其他动态库时,很容易导致一些堆错误,因为动态库相对应用有独立heap;
image.png
使用ProtoBuff
-
先把生成的Debug和include拷贝到工程目录
image.png
PS:D:\work\program\Serialization\Serialization是我的测试工程目录*
- 添加一个测试proto文件
Serialization\protobuff_files\AddressBook.proto
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 phones = 4;
}
message AddressBook {
repeated Person people = 1;
}
- 编译proto文件
进入测试工程目录
cd D:\work\program\Serialization\Serialization
使用编译生成的protoc.exe编译proto文件
protobuf-3.13.0\x64\Debug\protoc.exe -I=Serialization\protobuff_files --cpp_out=Serialization\protobuff_files AddressBook.proto
说明:
protoc编译.proto文件命令格式:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR xxx1.proto xxx2.proto
$SRC_DIR:包含相关的所有.proto文件,一般为工程目录;
$DST_DIR:生成的源文件的目录,可以与$SRC_DIR相同;
xxx1.proto:proto文件在$SRC_DIR中的相对路径,也可以是绝对路径;
编译proto文件后,会生成对应的.pb.cc和.pb.h文件:
- 应用工程设置
PS:老手请跳过此步;
-
包含include目录:$(ProjectDir)..\protobuf-3.13.0\x64\include;
image.png -
添加库目录:$(ProjectDir)..\protobuf-3.13.0\x64\Debug;
image.png -
添加附加依赖项:libprotobufd.lib;
image.png -
添加编译proto生成的AddressBook.pb.cc和AddressBook.pb.h到工程:
image.png
- 使用ProtoBuff示例
代码可以直接拷贝运行
/************************************************************************
* 测试ProtoBuff
*
*
* 从didi_AddressBook.txt中读取通讯录,然后添加一条记录,最后再写入文件;
* 如果didi_AddressBook.txt不存在,第一次添加记录后,会创建文件didi_AddressBook.txt
*
************************************************************************/
#include
#include
#include
#include "protobuff_files/AddressBook.pb.h"
using namespace std;
// 打印AddressBook
void ListPeople(const tutorial::AddressBook& address_book) {
for (int i = 0; i < address_book.people_size(); i++) {
const tutorial::Person& person = address_book.people(i);
cout << "Person ID: " << person.id() << endl;
cout << " Name: " << person.name() << endl;
cout << " E-mail address: " << person.email() << endl;
for (int j = 0; j < person.phones_size(); j++) {
const tutorial::Person::PhoneNumber& phone_number = person.phones(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;
}
}
}
// 通讯录增加一个人
void PromptForAddress(tutorial::Person* person) {
cout << "Enter person ID number: ";
int id;
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): ";
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_phones();
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;
}
}
}
// 从didi_AddressBook.txt中读取通讯录,然后添加一条记录,最后再写入文件;
// 如果didi_AddressBook.txt不存在,第一次添加记录后,会创建文件didi_AddressBook.txt
int main(int argc1, char* argv1[]) {
// 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;
char file_name[] = "didi_AddressBook.txt";
tutorial::AddressBook address_book;
{
// Read the existing address book.
fstream input(file_name, ios::in | ios::binary);
if (!input) {
cout << file_name << ": File not found. Creating a new file." << endl;
}
else if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
//打印address_book
{
int people_size = address_book.people_size();
cout << "print address_book by ListPeople(): " << people_size << endl;
ListPeople(address_book);
//也可以使用基类Message中提供的方法:
string debug_string = address_book.DebugString();
cout << "print address_book by PrintDebugString(): " << endl;
address_book.PrintDebugString();
address_book.Clear();
cout << "after Clear, print address_book by PrintDebugString(): " << endl;
address_book.PrintDebugString();
cout << endl;
}
// Add an address.
cout << "begin add a address:" << endl;
PromptForAddress(address_book.add_people());
{
// Write the new address book back to disk.
fstream output(file_name, ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToOstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
}
// Optional: Delete all global objects allocated by libprotobuf.
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
可以看到ProtoBuff序列化后的内容如下:
image.png