Protobuf 中的类型会对应到 CPP 中的对象,我们需要了解如何操作这些对象,以及如何序列化和反序列化这些对象。
- 定义数据 {#title-0} ==================
创建 sample.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
{
string name = 1;
int32 numb = 2;
Week week = 3;
repeated int32 rept = 4;
map<string, int32> mpsi = 5;
Custom cust = 6;
}
接下来,使用下面命令生成 demo_pb.h 头文件和 demo_pb.cc 实现文件:
protoc sample.proto --cpp_out=.
- 创建项目 {#title-1} ==================
在 VS 中创建项目使用,进行如下配置:
- 头文件:项目属性 -> 配置属性 -> VC++目录 -> 包含目录,添加 include 目录
- lib 文件:项目属性 -> 链接器 -> 常规 -> 附加库目录,添加 lib 目录
- lib 文件:项目属性 -> 链接器 -> 输入 -> 附加依赖项,添加 abseil_dll.lib、libprotobuf.lib 两个库名
- dll 文件:将 bin 目录下的 dll 文件拷贝到项目中
注意:
- 由于库编译的是 Release 版本,创建的项目也应设置为 Release 模式
- 项目属性 -> C/C++ -> 代码生成 -> 运行库 -> 多线程DLL(/MD)
- 项目属性 -> C/C++ -> 预处理器 -> 预处理器定义,增加 ABSL_CONSUME_DLL、PROTOBUF_USE_DLLS
如果没有定义 ABSL_CONSUME_DLL
、PROTOBUF_USE_DLLS
,则 absl、protobuf 默认假设使用的是静态库。
- 操作数据 {#title-2} ==================
3.1 对象的操作 {#title-3}
#if 1
#include <iostream>
#include <fstream>
#include <algorithm>
using namespace std;
#include "sample.pb.h"
// 1. 基本类型
void test01()
{
Demo demo;
demo.set_name("张三");
demo.set_numb(18);
demo.set_week(Week::Sat);
cout << demo.name() << " " << demo.numb() << " " << demo.week() << endl;
}
// 2. repeated 类型
void test02()
{
Demo demo;
// 基本方法
demo.add_rept(100);
demo.add_rept(200);
demo.add_rept(300);
demo.set_rept(1, 666);
cout << demo.rept_size() << endl;
// demo.clear_rept();
// 可读可写
demo.mutable_rept()->erase(demo.rept().begin());
demo.mutable_rept()->at(0) = 10000;
for_each(demo.mutable_rept()->begin(), demo.mutable_rept()->end(), [](int& val) { val += 100; });
// 只读访问
cout << demo.rept().at(0) << " " << demo.rept()[0] << endl;
for_each(demo.rept().begin(), demo.rept().end(), [](const int& val) {cout << val << endl; });
}
// 3. map 类型
void test03()
{
Demo demo;
// 基本方法
cout << demo.mpsi_size() << endl;
// 可读可写
demo.mutable_mpsi()->insert({ "aaa", 100 });
demo.mutable_mpsi()->insert({ "bbb", 200 });
demo.mutable_mpsi()->insert(pair<string, int>("ccc", 300));
demo.mutable_mpsi()->at("bbb") = 1000;
cout << demo.mutable_mpsi()->contains("ccc") << endl;
auto pos1 = demo.mutable_mpsi()->find("aaa");
demo.mutable_mpsi()->erase("aaa");
for (auto it = demo.mutable_mpsi()->begin(); it != demo.mutable_mpsi()->end(); ++it)
cout << it->first << " " << it->second << endl;
// 只读访问
cout << demo.mpsi().at("aaa") << endl;
auto pos2 = demo.mpsi().find("aaa");
cout << demo.mpsi().contains("ccc") << endl;
}
// 4. 自定义类型
void test04()
{
Demo demo;
// 可读可写
demo.mutable_cust()->set_attr1(123);
demo.mutable_cust()->set_attr2(456);
// 只读访问
cout << demo.cust().attr1() << " " << demo.cust().attr2() << endl;
}
int main()
{
// test01();
// test02();
// test03();
// test04();
return 0;
}
#endif
3.2 对象序列化 {#title-4}
#if 1
#include <iostream>
#include <fstream>
#include "sample.pb.h"
using namespace std;
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;
cout << "---------------------" << endl;
}
void test()
{
// 实例化数据对象
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. 序列化到数组/从数组反序列化
int object_size = demo.ByteSizeLong();
char* data2 = new char[object_size];
demo.SerializeToArray(data2, object_size);
Demo new_demo1;
new_demo1.ParseFromArray(data2, object_size);
print_demo(new_demo1);
// 2. 序列化到字符串/从字符串反序列化
string data;
demo.SerializeToString(&data);
Demo new_demo2;
new_demo2.ParseFromString(data);
print_demo(new_demo2);
// 3. 序列化到输出流
ofstream ofs("demo.bin", ios::binary);
demo.SerializeToOstream(&ofs);
ofs.close();
Demo new_demo3;
ifstream ifs("demo.bin", ios::binary);
new_demo3.ParseFromIstream(&ifs);
ifs.close();
print_demo(new_demo3);
}
int main()
{
test();
return 0;
}
#endif