ProtoBuf3 C++使用篇

protobuf 是用于结构化数据串行化的灵活、高效、自动化的解决方案。又如 XML,不过它更小、更快、也更简单。你只需要按照你想要的数据存储格式编写一个.proto,然后使用生成器生成的代码来读写这个数据结构。更重要的是,你甚至可以在无需重新部署程序的情况下更新数据结构。

在项目中使用protocol buffers需要经历如下三步:

(1).定义消息格式文件,最好以proto作为后缀名

(2).使用Google提供的protocol buffers编译器来生成代码文件,一般为.h和.cc文件,主要是对消息格式以特定的语言方式描述

(3).使用protocol buffers库提供的API来编写应用程序

1.先来定义一个

本消息将以客户端请求登录和服务端返回为列

syntax = "proto3";
package pt;
option optimize_for = LITE_RUNTIME;

message req_login
{
    string username = 1;
    string password = 2;
}

message obj_user_info
{
    string nickname = 1;      //昵称
    string icon        = 2;    //头像
    int64  coin        = 3;    //金币
    string location    = 4;    //所属地
}

//游戏数据统计
message obj_user_game_record
{
    string time = 1;
    int32 kill  = 2;        //击杀数
    int32 dead  = 3;        //死亡数
    int32 assist= 4;        //助攻数
}

message rsp_login
{
    enum RET {
        SUCCESS         = 0;
        ACCOUNT_NULL    = 1;    //账号不存在
        ACCOUNT_LOCK    = 2;    //账号锁定
        PASSWORD_ERROR  = 3;    //密码错误
        ERROR           = 10;
    }
    int32 ret = 1;
    obj_user_info user_info = 2;
    repeated obj_user_game_record record = 3;
}

2.生成目标语言代码
下面的命令帮助我们将game.proto文件中定义的Protocol Buffer格式的消息编译成C++代码文件。

//SRC_DIR   .proto文件存放目录
//--cpp_out  指示编译器生成C++代码,DST_DIR为生成文件存放目录
//game.proto 待编译的协议文件
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/game.proto

由于我们在game.proto文件中定义选项optimize_for=LITE_RUNTIME,因此由该文件内生成的所有C++类的父类均为::google::protobuf::MessageLite,而非::google::protobuf::Message。MessageLite类是Message的父类,MessageLite中缺少对反射的支持,而此类功能均在Message类中提供了具体的实现。
对于我们的项目而言,整个系统相对比较封闭,不会和外部程序进行交互,与此同时,我们的客户端部分又是运行在Android平台,有鉴于此,我们考虑使用LITE版本的Protocol Buffer。这样不仅可以得到更高编码效率,而且生成代码编译后所占用的资源也会更少,至于反射所能带来的灵活性和极易扩展性,对于该项目而言完全可以忽略。

3.protobuf自动生成的API

class rsp_login : public ::google::protobuf::MessageLite
{
public:
  //每一个message类都包含以下方法供你检测或操作
  void CopyFrom(const rsp_login& from);
  void MergeFrom(const rsp_login& from);
  void Clear();                              //清除所有字段内容,并置为空状态
  bool IsInitialized() const;                //检测所有required 是否初始化
int ByteSize() const; //类所占字节数
//整形变量只提供获取、修改、清除 void clear_ret(); ::google::protobuf::int32 ret() const; void set_ret(::google::protobuf::int32 value); //自定义类类型user_info bool has_user_info() const; void clear_user_info(); const ::pt::obj_user_info& user_info() const; //自定义类型,并没提供set方法,而是通过mutable_接口返回user_info的指针,可根据此指针进行赋值操作 ::pt::obj_user_info* mutable_user_info(); //返回user_info字段指针,将所有权移交给此指针,并将user_info字段置为empty状态 ::pt::obj_user_info* release_user_info(); //使用set_allocated要小心,传入的参数需要显示allocate,设置后函数内部维护此指针 void set_allocated_user_info(::pt::obj_user_info* user_info); //record为repeated的类数组类型 int record_size() const; void clear_record(); //根据id索引,返回记录的引用,const不可修改内容 const ::pt::obj_user_game_record& record(int index) const; //根据id索引,返回记录的指针,以供查看、修改 ::pt::obj_user_game_record* mutable_record(int index); //repeated类型提供add接口增加一条记录,并返回此记录的指针,以便对其赋值 ::pt::obj_user_game_record* add_record(); //提供mutable接口,并返回record字段的容器指针,可根据此指针遍历、修改 ::google::protobuf::RepeatedPtrField< ::pt::obj_user_game_record >* mutable_record(); //返回record字段的容器引用,const不可修改内容 const ::google::protobuf::RepeatedPtrField< ::pt::obj_user_game_record >& record() const; }

4.protobuf读和写

#include 
#include <string>
#include "game.pb.h"

int main()
{
    pt::rsp_login rsp{};
    rsp.set_ret(pt::rsp_login_RET_SUCCESS);
    auto user_info = rsp.mutable_user_info();
    user_info->set_nickname("dsw");
    user_info->set_icon("345DS55GF34D774S");
    user_info->set_coin(2000);
    user_info->set_location("zh");

    for (int i = 0; i < 5; i++) {
        auto record = rsp.add_record();
        record->set_time("2017/4/13 12:22:11");
        record->set_kill(i * 4);
        record->set_dead(i * 2);
        record->set_assist(i * 5);
    }

    std::string buff{};
    rsp.SerializeToString(&buff);
    //------------------解析----------------------
    pt::rsp_login rsp2{};
    if (!rsp2.ParseFromString(buff)) {
        std::cout << "parse error\n";
    }

    auto temp_user_info = rsp2.user_info();
    std::cout << "nickname:" << temp_user_info.nickname() << std::endl;
    std::cout << "coin:" << temp_user_info.coin() << std::endl;
    for (int m = 0; m < rsp2.record_size(); m++) {
        auto temp_record = rsp2.record(m);
        std::cout << "time:" << temp_record.time() << " kill:" << temp_record.kill() << " dead:" << temp_record.dead() << " assist:" << temp_record.assist() << std::endl;
    }
}

输出如下:

ProtoBuf3 C++使用篇
nickname:dsw
coin:2000
time:2017/4/13 12:22:11 kill:0 dead:0 assist:0
time:2017/4/13 12:22:11 kill:4 dead:2 assist:5
time:2017/4/13 12:22:11 kill:8 dead:4 assist:10
time:2017/4/13 12:22:11 kill:12 dead:6 assist:15
time:2017/4/13 12:22:11 kill:16 dead:8 assist:20

View Code

4.扩展protoobuf
在你发布了使用Protocol-Buffers的代码之后, 你必定会想”改进”message的定义. 如果你想让你的新message向后兼容, 并且旧的message能够向前兼容。你一定希望如此,那么你在新的Person 中就要遵守其他的一些规则了:
(1).对已存在的任何字段, 你都不能更改其标识tag号。
(2).你绝对不能添加或删除任何required的字段。
(3).你可以添加新的optional或repeated的字段, 但是你必须使用新的标识tag号(此message中从未使用过的标识号,甚至连已经被删除过标识号也不行)。
(4).有一些例外情况, 但是它们很少使用。
如果你遵守这些规则, 老代码将能很好地解析新的消息, 并忽略掉任何新的字段. 对老代码来说, 已经被删除的optional字段将被赋予默认值. 已被删除的repeated字段将是空的. 新的代码也能够透明地读取旧的消息. 但是请牢记心中: 新的optional字段将不会出现在旧的消息中, 如果没有为一个optional项指定默认值, 那么就会使用与特定类型相关的默认值: 对string(null)、boolean(false)、数值类型(0)。
还要注意: 如果你添加了一个新的repeated字段, 你的新代码将无法告诉你它是否被留空了(被新代码), 或者是否从未被置值(被旧代码), 这是因为它没有has_标志.

Original: https://www.cnblogs.com/DswCnblog/p/6700660.html
Author: 滴水瓦
Title: ProtoBuf3 C++使用篇

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536185/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Delphi XE8,C++ Builder XE8,RAD Studio XE8 官方 ISO 文件下载,附激活工具

    RAD Studio XE8 v22.0.19027.8951 官方ISO下载(6.72G):http://altd.embarcadero.com/download/radstu…

    C++ 2023年5月29日
    077
  • 【C++】第1章 在VS2015中用C++编写控制台应用程序

    分类:C++、VS2015 创建日期:2016-06-12 看到不少人至今还在用VC 6.0开发工具学习C++,其实VC 6.0开发工具早就被淘汰了。这里仅介绍学习C++时推荐使用…

    C++ 2023年5月29日
    067
  • NDK自带的c/c++库

    1.官方文档 https://developer.android.google.cn/ndk/guides/stable_apis https://developer.androi…

    C++ 2023年5月29日
    066
  • 当C++遇到iOS应用开发—Dict集合

    在Object-c中,字典(KEY/VALUE)使用NSDictionary 和NSMutableDictionary(可变长)。使用语法如下: NSDictionary *dic…

    C++ 2023年5月29日
    065
  • 【转】C++的赋值构造函数(赋值运算符重载)

    当一个类的对象向该类的另一个对象赋值时,就会用到该类的赋值构造函数。 当没有重载赋值构造函数(赋值运算符)时,通过默认赋值构造函数来进行赋值操作 注意:这里a,b对象是已经存在的,…

    C++ 2023年5月29日
    052
  • 【转】C++知识点总结

    其他路径: 微信公众号:程序喵星人 更多资源和视频教程,QQ:1902686547 这篇文章是对C++的知识点做了一些简单的总结,基本包含了所有的C++基础知识点。以下提到的知识点…

    C++ 2023年5月29日
    052
  • C++ STL std::copy 详解

    std::copy(start, end, std::back_inserter(container)); 这里,start和end是输入序列(假设有N个元素)的迭代器(itera…

    C++ 2023年5月29日
    062
  • C++应该更多使用堆还是栈?

    栈,是不需要涉及内存分配的,你可以把它看成一个很长的连续内存,用来执行函数。自动以先进后出的方式使用。具体的进出在C++里你可以假设是不能操纵这个栈的,实际上它存在。 _main函…

    C++ 2023年5月29日
    056
  • C++ 资源大全

    http://www.uml.org.cn/c++/201411145.asp http://ezlippi.com/blog/2014/12/c-open-project.htm…

    C++ 2023年5月29日
    050
  • 解决JAVA调用C++ DLL文件Unable to load library的问题

    JAVA项目开发中,有时候会遇到调用C++编写的动态链接库的场景(比如调用第三方的动态链接库、软件中关键部分用C++编码提供给外部调用)。我们知道JAVA调用动态链接库(C/C++…

    C++ 2023年5月29日
    091
  • Using WebAssembly threads from C, C++ and Rust

    Learn how to bring multithreaded applications written in other languages to WebAssembly. J…

    C++ 2023年5月29日
    068
  • c++ 书籍

    c++ 书籍 入门 c++0x G:\doc\bianchengsuixiang\BUPSDXFA3TP7KCMLHALRHLIX2FEJEUJFEIT(信息技术)\IT\软件开发…

    C++ 2023年5月29日
    065
  • libnode 0.4.0 发布,C++ 语言版的 Node.js

    libnode 0.4.0 支持 Windows ,提升了性能,libuv 更新到 0.10.17 版本,libj 更新到 0.8.2 版本。 libnode 是 C++ 语言版的…

    C++ 2023年5月29日
    070
  • 对指针和引用的理解(c++)

    1.指针 typedef说明一种新类型名,来代替已有类型名。 a.案例:typedef char String_t和#define String_d char 这两句在使用上的区别…

    C++ 2023年5月29日
    058
  • 【C++自绘控件】如何用GDI+来显示图片

    在我们制作一个应用软件的时候往往需要在窗口或控件中添加背景图。而图片不仅有BMP格式的,还有JPEG、PNG、TIFF、GIF等其它的格式。那么如何用jpg格式的图片来当背景呢? …

    C++ 2023年5月29日
    064
  • C++ std::Recursive_mutex 支持 “对同一互斥量进行嵌套加锁”

    使用场景:一个类的不同成员函数之间,存在相互调用的情况, 如果这样的成员函数作为线程的入口函数时,就会出现在成员函数 func1()中对某个互斥量上锁,并且, func1()中调用…

    C++ 2023年5月29日
    046
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球