Protobuf 中的类型会对应到 CPP 中的对象,我们需要了解如何操作这些对象,以及如何序列化和反序列化这些对象。
1. 定义数据
创建 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=.
2. 创建项目
在 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 默认假设使用的是静态库。
3. 操作数据
3.1 对象的操作
#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 对象序列化
#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