Google Protobuf 使用

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_DLLPROTOBUF_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
未经允许不得转载:一亩三分地 » Google Protobuf 使用
评论 (0)

1 + 6 =