C++ Protobuf 使用

Protocol Buffer Serialization 指的是使用 Protocol Buffer 进行数据序列化的过程。在这个过程中,结构化的数据被转换成 Protocol Buffer 的二进制格式、以便于存储、传输或者在不同的系统之间进行交换。

Protocol Buffer Serialization 的优点包括序列化后数据的紧凑性、高效性以及跨语言、跨平台的兼容性。这使得它成为许多分布式系统和网络通信中的首选序列化方法之一。

1. Proto 编译

syntax = "proto3";

enum Week
{
    Mon = 0;
    Tue = 1;
    Wed = 2;
    Thu = 3;
    Fri = 4;
    Sat = 5;
    Sun = 6;
}

message Custom
{
    int32 attr1 = 1;
    int32 attr2 = 2;
}

message Demo
{
    // 字段存储中文,使用 bytes 类型替换 string 类型
    bytes name = 1;
    int32 numb = 2;
    Week week = 3;
    repeated int32 rept = 4;
    map<string, int32> mpsi = 5;
    Custom cust = 6;
}

当编写完数据定义相关的 .proto 文件,我们还需要使用 protobuf 编译器来生成对应语言的数据类文件。

对于 C++,每个 proto 文件会生成两种不同的文件:

  • foo_pb.h:头文件
  • foo_pb.cc:实现文件
protoc *.proto --cpp_out=.

2. 对象序列化

#include <iostream>
#include <fstream>
using namespace std;

#include "demo.pb.h"

void test01()
{
	Demo demo;

	// 1. 基本字段
	demo.set_name("张三");
	demo.set_numb(18);
	demo.set_week(Week::Sat);
	cout << demo.name() << " " << demo.numb() << " " << demo.week() << endl;

	// 2. repeated 字段
	// 添加
	demo.add_rept(100);
	demo.add_rept(200);  
	demo.add_rept(300);
	// 修改
	demo.set_rept(1, 666);
	// 删除
	demo.mutable_rept()->erase(demo.rept().begin());
	// 访问
	for (auto it = demo.rept().begin(); it != demo.rept().end(); ++it)
		cout << *it << " ";
	cout << endl;
	cout << demo.rept()[0] << endl;


	// 3. map 字段
	// 插入
	demo.mutable_mpsi()->insert({ "aaa", 100 });
	demo.mutable_mpsi()->insert({ "bbb", 200 });
	demo.mutable_mpsi()->insert(pair<string, int>("ccc", 300));
	cout << (int)demo.mutable_mpsi()->size() << endl;
	// 修改
	(*demo.mutable_mpsi())["ccc"] = 888;
	// 删除
	demo.mutable_mpsi()->erase("aaa");
	// 查询
	auto pos = demo.mpsi().find("bbb");
	cout << pos->first << " " << pos->second << endl;
	// 遍历
	for (auto it = demo.mpsi().begin(); it != demo.mpsi().end(); ++it)
		cout << it->first << " " << it->second << endl;

	// 4. 自定义类型
	demo.mutable_cust()->set_attr1(123);
	demo.mutable_cust()->set_attr2(456);
	cout << demo.cust().attr1() << " " << demo.cust().attr2() << endl;
}

void test02()
{
	// 构造函对象
	Demo demo;
	demo.set_name("张三");
	demo.set_numb(100);
	demo.add_rept(10);
	demo.add_rept(20);
	demo.set_week(Week::Thu);
	demo.mutable_mpsi()->insert({"aaa", 111});
	demo.mutable_mpsi()->insert({"bbb", 222});
	demo.mutable_cust()->set_attr1(123);
	demo.mutable_cust()->set_attr2(456);

	// 1. 序列化到数组
	size_t object_size = demo.ByteSizeLong();
	char* data2 = new char[object_size];
	demo.SerializeToArray(data2, (int)object_size);
	ofstream("data2.bin", ios::binary).write(data2, object_size);
	
	// 2. 序列化到字符串
	string data1 = demo.SerializeAsString();
	ofstream("data1.bin", ios::binary).write(data1.data(), data1.size());

	// 3. 序列化到输出流
	ofstream ofs3("data3.bin", ios::binary);
	demo.SerializeToOstream(&ofs3);
}


void print_demo(Demo& demo)
{
	cout << "name:" << demo.name() << endl;
	cout << "numb:" << demo.numb() << endl;
	cout << "week:" << demo.week() << endl;
	cout << "rept:";
	for (auto it = demo.rept().begin(); it != demo.rept().end(); ++it)
		cout << *it << " ";
	cout << endl;

	cout << "mpsi:";
	for (auto it = demo.mpsi().begin(); it != demo.mpsi().begin(); ++it)
		cout << it->first << " " << it->second << endl;
	
	cout << "cust:" << demo.cust().attr1() << " " << demo.cust().attr2() << endl;
}


// 3. 反序列化
void test03()
{
	Demo demo;
	// ifstream ifs("data1.bin", ios::in | ios::binary);
	// ifstream ifs("data2.bin", ios::in | ios::binary);
	ifstream ifs("data3.bin", ios::binary);
	demo.ParseFromIstream(&ifs);
	print_demo(demo);
}


int main()
{
	test01();
	test02();
	test03();
	return 0;
}
未经允许不得转载:一亩三分地 » C++ Protobuf 使用
评论 (0)

5 + 9 =