当前位置:首页 > c++ > c++如何编译和使用probuf

c++如何编译和使用probuf

xuwenyan2个月前 (08-10)c++470

关于protobuf的介绍,请阅读文章关于protobuf的介绍和优缺点,这里不做过多介绍,直接开始使用教程。

编译protobuf

1:安装cmake

cmake用于生成protobuf的VisualStudio的工程,需要通过VisualStudio编译生成libprotobuf.lib和protoc.exe文件。

2:下载protobuf代码

打开https://github.com/protocolbuffers/protobuf/releases/tag/v21.5,c++使用选择下载文件 protobuf-cpp-xxx.tar.gz 或 protobuf-cpp-xxx.zip文件。

image.png

下载成功后解压文件。

3:通过cmake生成vs工程

  1. 打开cmake,填入protobuf目录下的cmake文件夹到Where is the source code编辑框

  2. 随便填入一个生成目录到Where to build the binaries编辑框,比如protobuf目录下的build文件夹

  3. 点击Configure,选择生成需要的vs版本,点击确定

  4. Configure完成后点击Generate生成vs工程

image.png

打开生成目录,找到protobuf.sln文件,打开并编译 libprotobuf-lite(l ibprotobuf-lite 没有描述符和反射等某些功能,但体积会小一个数量级,如果需要可以选择编译libprotobuf) 和 protoc 工程,会生成 libprotobuf-lite.lib(debug模式会在文件名末尾加d) 和 protoc.exe文件

image.png

image.png

配置工程依赖

1:复制文件所需文件到工程对应目录下

  • 把 protobuf/src/ 目录下的 google 文件夹拷贝到工程目录下

  • 把 protobuf输出目录下的 libprotobuf-lite.lib 和 protoc.exe 分 Debug 和 Release环境分别拷贝到工程的输出目录下

image.png

image.png

image.png

2:配置附加包含目录和附加库目录

打开工程解决方案,右击工程选择属性,C/C++ -> 常规 -> 附加包含目录,填入当前目录 ./

image.png

右击工程选择属性,连接器 -> 常规 -> 附加库目录,填入 libproto-lite.lib 所在目录, 我选择的是输出目录,可以使用变量 $(OutDir)

image.png

3:依赖静态库

可以选择在 链接器 -> 输入 -> 附加依赖项 中填入 libproto-lite.lib文件,也可以选择在代码中链接,看个人习惯,我喜欢在代码中链接

创建links.cpp文件并打开

分 Debug 和 Release 分别链接 libprotobuf,代码如下:

#ifdef _DEBUG
#pragma comment(lib, "libprotobuf-lited.lib")
#else
#pragma comment(lib, "libprotobuf-lite.lib")
#endif // DEBUG

proto文件的编写和配置

1:创建.proto文件

首先在工程目录下创建一个文件夹用于专门存储.proto文件,比如我创建的是 protobuf 文件夹

image.png

打开文件夹,创建一个后缀为 .proto 的文件,比如 message.proto

image.png

2:编写.proto文件

打开 message.proto 文件,配置需要的字段,比如:

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

message Person {
  optional string name = 1;
  optional int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    optional string number = 1;
    optional PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;
}

每个字段都必须使用optional,repeated,required标签修饰,注意:proto3已经不支持required标签,并且proto3不支持设置默认值。

  • optional: 该字段可以设置也可以不设置。如果未设置可选字段值,则使用默认值。对于简单类型,您可以指定自己的默认值,就像我们在示例中为电话号码所做的那样type否则,使用系统默认值:数字类型为零,字符串为空字符串,布尔值为 false。对于嵌入式消息,默认值始终是消息的“默认实例”或“原型”,没有设置任何字段。调用访问器以获取未显式设置的可选(或必需)字段的值始终返回该字段的默认值。

  • repeated:该字段可以重复任意次数(包括零次)。重复值的顺序将保存在协议缓冲区中。将重复字段视为动态大小的数组。

  • required:必须提供该字段的值,否则该消息将被视为“未初始化”。如果libprotobuf在调试模式下编译,序列化未初始化的消息将导致断言失败。在优化的构建中,会跳过检查并且无论如何都会写入消息。但是,解析未初始化的消息总是会失败(通过false从 parse 方法返回)。除此之外,必填字段的行为与可选字段完全相同。

syntax 为显示的指定proto的版本。

package 指定包名,避免与其它 .proto文件的字段冲突,在C++中,package其实就是生成了一个名字控件(namespace)。

message 会生成一个类,类里面包含的就是每个字段的变量和一些操作方法。

每个字段后面都必须有一个字段编号“=数字;”它可以不是递增的,但每个message作用域内它必须是唯一的。

如果链接的是 libproto-lite.lib 库,那么必须在 .proto 文件开头加上 “option optimize_for = LITE_RUNTIME”;

关于required被在proto3移除的原因大概是因为:

您应该非常小心将字段标记为required如果在某些时候您希望停止编写或发送必填字段,则将该字段更改为可选字段将会有问题——老读者会认为没有此字段的消息不完整,可能会无意中拒绝或丢弃它们。您应该考虑为您的缓冲区编写特定于应用程序的自定义验证例程。在 Google 内部,required字段是非常不受欢迎的;proto2 语法中定义的大多数消息optional仅使用repeated(Proto3 根本不支持required字段。)

更多关于protobuf的详细介绍可以参略谷歌文档:https://developers.google.com/protocol-buffers?hl=cn

3:配置.proto文件自定义生成

  • 右击工程,选择 添加 -> 新建筛选器,创建一个筛选器,比如protobuf

  • 右击创建的筛选器,添加 -> 现有项,找到刚才创建的 message.proto 文件,选择添加即可

image.png

  • 右击 message.pro 文件,配置属性 -> 常规 -> 项类型,选择 “自定义生成工具”

  • 右击 message.pro 文件,配置属性 -> 自定义生成工具 -> 命令行,填入 “$(OutDir)protoc.exe --proto_path=$(ProjectDir)protobuf --cpp_out=$(ProjectDir) %(FullPath)”

    这是使用protoc.exe编译 message.proto 文件,生成 message.pb.cc 和 message.pb.h 文件,这里填入的就是执行protoc.exe的命令行

protoc --proto_path=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/xxx.proto

    $SRC_DIR是.proto所在的目录,$DST_DIR是需要输出的目录

  • 右击 message.pro 文件,配置属性 -> 自定义生成工具 -> 输出,填入 “$(ProjectDir)%(Filename).pb.cc;$(ProjectDir)%(Filename).pb.h;%(Outputs)”

    protoc.exe即将生成的.cc和.h文件配置到输出,否则protoc.exe无法正常运行并生成所需的文件。

image.png

demo代码

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

int main() {
  GOOGLE_PROTOBUF_VERIFY_VERSION;

  tutorial::Person john;
  john.set_name("hello word");
  john.set_id(998);
  tutorial::Person_PhoneNumber* ph1 = john.add_phones();
  ph1->set_number("111111");
  ph1->set_type(tutorial::Person_PhoneType_HOME);
  tutorial::Person_PhoneNumber* ph2 = john.add_phones();
  ph2->set_number("222222");
  ph2->set_type(tutorial::Person_PhoneType_MOBILE);

  std::string serialize;
  bool ret = john.SerializePartialToString(&serialize);

  tutorial::Person john2;
  ret = john2.ParseFromString(serialize);

  std::string name = john2.name();
  int32_t id = john2.id();
  for (auto& iter : john2.phones()) {
    std::string phone = iter.number();
    tutorial::Person_PhoneType type = iter.type();
    int a = 1;
  }

  std::string debug = john.DebugString();
  google::protobuf::ShutdownProtobufLibrary();
}
    文章作者:xuwenyan
    版权声明:本文为本站原创文章,转载请注明出处,非常感谢,如版权漏申明或您觉得任何有异议的地方欢迎与本站取得联系。

    相关文章

    7z的简介和使用

    7z的简介和使用

    7z是一个支持多种压缩格式的开源项目,由Igor Pavlov开发,源码下载位置:https://www.7-zip.org/download.html源码结构项目源码目录结构是如下图:Asm包含主要...

    C++指针*为什么靠后会比较好?

    C++指针*为什么靠后会比较好?

    大多数书中和大神的代码里,往往指针的*都是靠变量而不是靠类型的,这主要是为了不造成我们第一眼对变量类型的误解和对指针类型的误解,比如: int* p1,p2 我们一眼看上去是不是通常会觉...

    使用GDI、MFC_GDI、GDI+绘制数组RGBA序列

    使用GDI、MFC_GDI、GDI+绘制数组RGBA序列

    学习ffmpeg时遇到一个问题,ffmpeg解码出RGB颜色后怎么绘制到屏幕上,于是将GDI、MFC_GDI、GDI+等方式都记录一下 1:注意按windows的要求,R、G、B、A顺序要调整...

    VC的ATL工程向导同时生成一个PS工程是做什么的?可以不要吗?

    VC的ATL工程向导同时生成一个PS工程是做什么的?可以不要吗?

    例如,我用VC2015的工程向导新建一个ATL的工程名字叫myAtl,那么VC会同时给我生成一个叫做myAtlPS的工程。这个myAtlPS工程是做什么的?什么情况下可以不需要它?什么情况下它又是必...

    ATL实现windows右键菜单扩展(ContextMenu)

    ATL实现windows右键菜单扩展(ContextMenu)

    右键菜单,即用户右击shell对象时弹出的上下文菜单(context menu)。本文记录了如何创建右键菜单的基本过程,跟着步骤一步一步来,即可创建出一个右键菜单工程。第一步,新建一个ATL工程Vis...

    uafxcwd.lib(afxmem.obj) : error LNK2005:

    uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)"解决办法

    如果在编译MFC程序的时候出现下列及类似的错误: 1˃uafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new...

    发表评论

    访客

    ◎欢迎参与讨论,请在这里发表您的看法和观点。