在 C++ 里使用 Protocol Buffer,要先定义消息结构,接着生成 C++ 代码,最后在程序里使用这些生成的代码。
定义消息结构
首先要创建一个.proto文件,在其中定义消息类型和字段。
// person.proto
syntax = "proto3"; // 指定语法版本
package tutorial; // 定义包名,防止命名冲突
// 定义Person消息类型
message Person {
string name = 1; // 字段名、类型和唯一编号
int32 id = 2; // 编号1-15在编码时更节省空间
string email = 3; // 字符串类型字段
// 定义枚举类型
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
// 定义嵌套消息类型
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
// 重复字段相当于C++里的vector
repeated PhoneNumber phones = 4;
}
// 定义AddressBook消息类型
message AddressBook {
repeated Person people = 1;
}
生成 C++ 代码
通过 Protocol Buffer 编译器(protoc)来生成 C++ 代码:
protoc --cpp_out=. person.proto
执行该命令后,会生成两个文件:person.pb.h(声明类)和person.pb.cc(实现类)。
在 C++ 代码中使用生成的类
下面是一个简单的示例,展示了如何在 C++ 中使用生成的类:
#include <iostream>
#include <fstream>
#include <string>
#include "person.pb.h"
using namespace std;
// 填充Person消息
void PromptForAddress(tutorial::Person* person) {
cout << "输入ID: ";
int id;
cin >> id;
person->set_id(id);
cin.ignore(256, '\n'); // 清除输入缓冲区
cout << "输入姓名: ";
getline(cin, *person->mutable_name());
cout << "输入邮箱: ";
getline(cin, *person->mutable_email());
while (true) {
cout << "输入电话号码(输入空行结束): ";
string number;
getline(cin, number);
if (number.empty()) break;
tutorial::Person::PhoneNumber* phone = person->add_phones();
phone->set_number(number);
cout << "输入电话类型 (0 - 移动, 1 - 家庭, 2 - 工作): ";
int type;
cin >> type;
cin.ignore(256, '\n');
phone->set_type(static_cast<tutorial::Person::PhoneType>(type));
}
}
int main(int argc, char* argv[]) {
// 验证库版本与编译时使用的版本是否兼容
GOOGLE_PROTOBUF_VERIFY_VERSION;
tutorial::AddressBook address_book;
// 从文件中读取现有地址簿
fstream input("address_book.bin", ios::in | ios::binary);
if (input) {
if (!address_book.ParseFromIstream(&input)) {
cerr << "Failed to parse address book." << endl;
return -1;
}
}
// 添加新联系人
PromptForAddress(address_book.add_people());
// 将修改后的地址簿写入文件
fstream output("address_book.bin", ios::out | ios::trunc | ios::binary);
if (!address_book.SerializeToIstream(&output)) {
cerr << "Failed to write address book." << endl;
return -1;
}
// 释放Protobuf分配的所有全局对象
google::protobuf::ShutdownProtobufLibrary();
return 0;
}
编译并运行程序
编译时需要链接 Protocol Buffer 库:
g++ -o addressbook_example addressbook_example.cc person.pb.cc -lprotobuf
关键操作说明
创建消息对象:
tutorial::Person person;
person.set_id(1234);
person.set_name("John Doe");
person.set_email("jdoe@example.com");
tutorial::Person::PhoneNumber* phone_number = person.add_phones();
phone_number->set_number("555-4321");
phone_number->set_type(tutorial::Person::HOME);
序列化消息:
string serialized_data;
person.SerializeToString(&serialized_data);
// 或者写入流
person.SerializeToOstream(&output_file);
解析消息:
tutorial::Person person;
person.ParseFromString(serialized_data);
// 或者从流中读取
person.ParseFromIstream(&input_file);
访问字段:
cout << "Name: " << person.name() << endl;
cout << "ID: " << person.id() << endl;
cout << "Email: " << person.email() << endl;
for (int i = 0; i < person.phones_size(); i++) {
const tutorial::Person::PhoneNumber& phone = person.phones(i);
cout << "Phone: " << phone.number();
cout << " (type: " << phone.type() << ")" << endl;
}
注意事项
- 字段编号(如name = 1)在消息的生命周期内不能改变。
- 编译时要确保链接了正确的 Protocol Buffer 库版本。
- 使用完 Protocol Buffer 后,建议调用google::protobuf::ShutdownProtobufLibrary()来释放资源。