Protocol Buffer C++实践
2016-07-22 16:06
411 查看
Protocol Buffer C++实践
Protocol Buffer C实践说明
Protocol Buffer 技术详解
安装 Google Protocol Buffer
Protocol Buffer C基础
第一步在proto文件中定义消息格式
第二步编译 proto 文件
单字型数值字段 proto2
单字符串字段 proto2
重复型数字字段数组型数字字段proto2
重复型字符串字段 数组型字符串字段 proto2
第三步编写 writer 和 Reader
说明
Protocol Buffer的C++实践。本文侧重于实践,先自行学习推荐的资料学习基本的Protocol Buffer知识。Protocol Buffer 技术详解
本文侧重于实践,基本的理论只要看下面推荐的blog就可以了,推荐一些Protocol Buffer 技术详解的极好的资料:中文资料:
Protocol Buffer技术详解(语言规范)
Protocol Buffer技术详解(C++实例)
Protocol Buffer技术详解(数据编码)
英文资料:
毫无疑问最佳资料是Protocol Buffer官网
安装 Google Protocol Buffer
点击下面链接下载最新版本protobuf-2.6.1.tar.gz
上面下载太慢就点我
下载完毕,需要解压
tar -xzf protobuf-2.6.1.tar.gz
然后,编译安装便可以使用它了
cd protobuf-2.6.1 sh ./configure --prefix=$INSTALL_DIR make make check make install
安装完毕之后,可以查看版本信息
protoc --version
Protocol Buffer C++基础
Protocol Buffer C++实践,将包含以下三个方面:在.proto文件中定义消息格式
使用protocol buffer 编译程序进行编译
使用protocol buffer的接口来写和读消息
下面将以一个例子对上面三个方面进行说明。
本文使用的源代码下载
第一步,在.proto文件中定义消息格式
下面定义的是Caffe深度学习框架的Datum,命名为caffe.proto//caffe.proto package caffe; //Specifies the Datum message Datum { optional int32 channels = 1; optional int32 height = 2; optional int32 width = 3; // the actual image data, in bytes optional bytes data = 4; optional int32 label = 5; // Optionally, the datum could also hold float data. repeated float float_data = 6; // If true data contains an encoded image that need to be decoded optional bool encoded = 7 [default = false]; }
具体的细节,学习了推荐的资料,基本上没有任何问题的。
第二步,编译 .proto 文件
将.proto格式的文件用 Protobuf 编译器编译成目标语言。关键的命令是:
protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/caffe.proto
.proto 文件存放在 $SRC_DIR 下面,生成的文件在$DST_DIR目录下。
这里需要的C++类,使用cpp_out选项,类似的选择可以提供其他支持的语言。
命令将生成两个文件:
caffe.pb.h 定义了 C++ 类的头文件
caffe.pb.cc C++ 类的实现文件
这里,在和caffe.proto同一目录下写了一个脚本compile.sh,将输出文件也设置在这一目录下:
//compile.sh #! /bin/sh SRC_DIR=./ DST_DIR=./ protoc -I=$SRC_DIR --cpp_out=$DST_DIR $SRC_DIR/caffe.proto
所以,只要在终端运行
sh compile.sh命令即可。
这里介绍protocol buffer 编译器产生的代码。
对于给出一个简单的消息声明:
message Datum {...}
protocol buffer 编译器生成一个称为Datum的类,公有继承与 google::protobuf::Message类,见编译生成的caffe.pb.h文件中:
class Datum : public ::google::protobuf::Message { public: Datum();//默认构造函数。 virtual ~Datum();//默认析构函数。 Datum(const Datum& from);//复制构造函数。 //赋值操作符 inline Datum& operator=(const Datum& from) { CopyFrom(from); return *this; } //与另一个消息交换内容。 void Swap(Datum* other); ... }
除了上面中描述的方法外,对在消息的原始文件中定义的每个字段(field ),protocol buffer编译器会生成一系列的访问方法。
单字型数值字段 (proto2)
对于这些字段定义中的任何一个:optional int32 foo = 1; required int32 foo = 1;
编译器将生成以下的访问方法:
bool has_foo() const: 检查字段是否被设置,如果字段被设置,则会设置为true
int32 foo() const: 返回当前字段的值
void set_foo(int32 value): 设置字段的值
void clear_foo(): 清除字段的值,这时has_foo() 返回false 而foo() 返回默认值
可以查看之前定义的optional int32 channels = 1;
// optional int32 channels = 1; //has_channels()检查字段是否被设置 inline bool has_channels() const; inline void clear_channels(); static const int kChannelsFieldNumber = 1; inline ::google::protobuf::int32 channels() const; //set_channels 设置字段的值 inline void set_channels(::google::protobuf::int32 value);
单字符串字段 (proto2)
对于单字符串字段 (proto2)optional string foo = 1; required string foo = 1; optional bytes foo = 1; required bytes foo = 1;
也会有类似上面的has_foo()等函数,这里只介绍编译器生成以下的访问方法:
// 使用C风格的NULL终止字符串设置字段的值。 //has_foo()将返回true,foo()将返回值的copy。 void set_foo(const char* value); //像上面一样,但字符串大小显式给出, //而不是通过寻找一个空终止符字节来确定。 void set_foo(const char* value, int size);
重复型数字字段(数组型数字字段proto2)
对于这个字段定义:repeated int32 foo = 1;
这里介绍编译器将生成的和设置数值相关的访问方法:
//返回当前字段中的元素的数量。 int foo_size() const //返回给定索引index对应的元素,index是基于零的。 int32 foo(int index) const //设置给定索引index对应的元素的值value void set_foo(int index, int32 value) //添加一个新的元素的值到字段。 void add_foo(int32 value) //移除字段中的所有元素。之后,foo_size()将返回零。 void clear_foo() //返回潜在repeatedfield,它存储字段的元素。 //这个容器类提供类似STL的迭代器和其他方法。 const RepeatedField<int32>& foo() const //类似上面,返回的是一个指针,可修改repeatedfield RepeatedField<int32>* mutable_foo()
重复型字符串字段 (数组型字符串字段 proto2)
对于这些字段定义中的任何一个:optional string foo = 1; required string foo = 1; optional bytes foo = 1; required bytes foo = 1;
基本的函数功能是类似上面重复的数字字段访问函数的,这里只介绍编译器生成以下的访问方法:
//返回给定的基于零的索引index的元素。 const string& foo(int index) const //在给定的基于零的索引index处设置元素的值 //注意是使用C风格的零终止字符串 void set_foo(int index, const char* value) //像上面一样,但字符串大小显式给出, //而不是通过寻找一个空终止符字节来确定。 void set_foo(int index, const char* value, int size)
第三步,编写 writer 和 Reader
使用下面命令创建writer.cpp和reader.cpp。touch writer.cpp touch reader.cpp
Writer 将把一个结构化数据写入磁盘,以便其他人来读取。
使用 Protobuf,Writer 的工作将变得很简单,需要处理的结构化数据由 .proto 文件描述,经过上一节中的编译过程后,该数据化结构对应了一个 C++ 的类,并定义在 caffe.pb.h 中。对于本例,类名为 Caffe::Datum。
Writer 需要 include 该头文件,然后便可以使用这个类了。
现在,在 Writer 代码中,将要存入磁盘的结构化数据由一个 Caffe::Datum 类的对象表示,它提供了一系列的 get/set 函数用来修改和读取结构化数据中的数据成员,或者叫 field。
当我们需要将该结构化数据保存到磁盘上时,类Caffe::Datum 已经提供相应的方法来把一个复杂的数据变成一个字节序列,我们可以将这个字节序列写入磁盘。
对于想要读取这个数据的程序来说,也只需要使用类 Caffe::Datum 的相应反序列化方法来将这个字节序列重新转换会结构化数据。
相关文章推荐
- C#使用Protocol Buffer(ProtoBuf)进行Unity中的Socket通信
- python如何通过protobuf实现rpc
- 我的Protobuf消息设计原则
- 化繁为简--google protobuf
- c++ java中关于protobuf反序列化对象实体和实体处理(函数)关系 (一)
- c++ java中关于protobuf反序列化对象实体和实体处理(函数)关系(二)
- Google 的开源技术protobuf 简介与例子
- Some Notes of Protocol Buffer C++
- rabbitmq学习
- Google Protocol Buffer 的使用和原理
- 最常用的两种C++序列化方案的使用心得
- protobuf通过反射来赋值
- TDEngine数据包的详细说明
- FlatBuffers与protobuf性能比较
- gogoprotobuf使用(上)
- gogoprotobuf使用(下)
- vs2012编译protobuf
- protocol buffer 编解码
- IOS之rpc实现