一、概述
Protocol Buffers (a.k.a., protobuf) are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data for use in communications protocols, data storage, and more.
参考官网:https://developers.google.com/protocol-buffers/
二、安装
2.1 系统默认protobuf
Ubuntu 16.04安装了protobuf 2.6.1。
查看protobuf的版本的命令:
protoc --version
查看protoc路径:
which protoc
系统默认安装路径/usr/bin/protoc
2.2 安装指定版本的protobuf
各版本protobuf下载地址: https://github.com/protocolbuffers/protobuf/releases/ ,根据需要选择版本分支,以3.6.1为例。
生成configure:
./autogen.sh
PS: 如果出现如下错误 ./autogen.sh: 37: ./autogen.sh: autoreconf: not found或者autoreconf: /usr/bin/autoconf failed with exit status: 1错误,需要先安装autoconf,automake和libtool
sudo apt-get update #autoconf和automake是用来发布C程序 sudo apt-get install autoconf sudo apt-get install automake sudo apt-get install libtool
从makefile中读取指令,安装到指定的位置
./configure --prefix=/home/yly/Software/protobuf-3.6.1/local #configure用来生成makefile
make -j12 #编译
make install #从makefile中读取指令,安装到指定位置(即使用configure生成makefile时指定的--prefix对应的地址)
参考:https://blog.csdn.net/qq_16775293/article/details/81119375
三、使用
3.1 生成头文件方法
编写message文件,msg.proto,里面存储了数据结构。例如:
package test;
message Pack{
int64 height=1;
repeated Point edge=2; //repeated相当于std::vector
Entity entity=3;
}
通过protoc编译,每个message自动生成一个类,包含了自动由protoc定义的方法。具体操作:
./protoc --cpp_out=/OUT_DIR -I=/PATH/OF/msg.proto msg.proto
#SYNOPSIS: protoc [--cpp_out=OUT_DIR] [-IPATH=PATH] PROTO_FILE
生成msg.pb.h和msg.pb.cc文件。
其中,.h是头文件,为类的声明;.cc中定义了类。
3.2 生成的头文件说明
每一个proto定义的message都在pb.h和pb.cc中生成了一个类。类中包含常用对象的构造、拷贝、赋值等方法,以及对类的对象成员操作的方法。这里重点将一下自动生成的对类的对象成员的操作方法,以上文中的Pack类为例,proto文件定以的message Pack中有三类数据成员:a)内置类型int64, b) repeated类型(相当于vector), 3) 自定义类型Entity (写在同一个proto的另一个message中,或者写在另一个proto文件的message中并被import进该proto文件,该message也会在pb.h文件中生成另一个类)。根据类型的不同,生成的方法也不同(关注下文 // nested types 后的代码):
class Pack : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:test.Pack) */ {
public:
Pack();
virtual ~Pack();
Pack(const Pack& from);
inline Pack& operator=(const Pack& from) {
CopyFrom(from);
return *this;
}
#if LANG_CXX11
Pack(Pack&& from) noexcept
: Pack() {
*this = ::std::move(from);
}
inline Pack& operator=(Pack&& from) noexcept {
if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {
if (this != &from) InternalSwap(&from);
} else {
CopyFrom(from);
}
return *this;
}
#endif
static const ::google::protobuf::Descriptor* descriptor();
static const Pack& default_instance();
static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY
static inline const Pack* internal_default_instance() {
return reinterpret_cast<const Pack*>(
&_Pack_default_instance_);
}
static constexpr int kIndexInFileMessages =
17;
void Swap(Pack* other);
friend void swap(Pack& a, Pack& b) {
a.Swap(&b);
}
// implements Message ----------------------------------------------
inline Pack* New() const final {
return CreateMaybeMessage<Pack>(NULL);
}
Pack* New(::google::protobuf::Arena* arena) const final {
return CreateMaybeMessage<Pack>(arena);
}
void CopyFrom(const ::google::protobuf::Message& from) final;
void MergeFrom(const ::google::protobuf::Message& from) final;
void CopyFrom(const Pack& from);
void MergeFrom(const Pack& from);
void Clear() final;
bool IsInitialized() const final;
size_t ByteSizeLong() const final;
bool MergePartialFromCodedStream(
::google::protobuf::io::CodedInputStream* input) final;
void SerializeWithCachedSizes(
::google::protobuf::io::CodedOutputStream* output) const final;
::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray(
bool deterministic, ::google::protobuf::uint8* target) const final;
int GetCachedSize() const final { return _cached_size_.Get(); }
private:
void SharedCtor();
void SharedDtor();
void SetCachedSize(int size) const final;
void InternalSwap(Pack* other);
private:
inline ::google::protobuf::Arena* GetArenaNoVirtual() const {
return NULL;
}
inline void* MaybeArenaPtr() const {
return NULL;
}
public:
::google::protobuf::Metadata GetMetadata() const final;
// nested types ----------------------------------------------------
// accessors -------------------------------------------------------
// repeated .test.Point edge = 2;
int edge_size() const; //查看edge的数量(因为repeated相当于vector,可以有多个edge)
void clear_edge();
static const int kedgeFieldNumber = 2; //与proto中message内序号对应
::google::protobuf::Point& edge(int index) const; //去序号为index的edge
void set_edge(int index, ::google::protobuf::int64 value); //修改index的edge值
void add_edge(::google::protobuf::int64 value); //增加edge
const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
edge() const;
::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
mutable_edge();
// .test.Entity entity = 3;
bool has_entity() const; //检测是否存在entity的数据;
void clear_entity();
static const int kEntityFieldNumber = 3; //与Pack中指定的entity的number一致
private:
const ::proto::Entity& _internal_entity() const;
public:
const ::proto::Entity& entity() const;
::proto::Entity* release_entity();
::proto::Entity* mutable_entity();
void set_allocated_entity(::proto::Entity* entity);
// int64 height = 1;
void clear_height();
static const int kHeightFieldNumber = 1;
::google::protobuf::int64 height() const;
void set_height(::google::protobuf::int64 value);
};
3.3 Serialize & Deserialize
假设msg.proto中定义了message Car{***},则生成的msg.pb.h中会定义Car的类。Car类可以使用SerializeToArray和ParseFromArray方法分别负责向二进制文件中写入和从二进制文件中读取。代码解析:
SerializeToArray和ParseFromArray定义在/protobuf/include/google/protobuf/message_lite.h中(message.h中include了message_lite.h,所以自动生成的msg.pb.h文件中#include <google/protobuf/message.h>,也就自动包含了序列化和反序列化的方法)。
生成的msg.ph.h文件中的类Car继承了Message,message.h文件中代码:class Car : public ::google::protobuf::Message{}
并且,Message类继承了MessageLite类,message_lite.h文件中代码:class LIBPROTOBUF_EXPORT Message : public MessageLite{}
所以自定义生成的类Car中包含SerializeToArray和ParseFromArray方法。
两个方法定义如下:
bool SerializeToArray(void* data, int size) const;
bool ParseFromArray(const void* data, int size);
其实现被封装在lib中了。
使用范例:
Car car;
//序列化:将数据序列化
char *write_buf = nullptr;
int write_size=car.ByteSize();
write_buffer = new char[write_size];
car.SerializeToArray(write_buffer, write_size); //将write_size大小的数据从car中序列化并写入protocal_buffer;
//反序列化:从二进制文件中读取数据并解析
std::fstream test_stream;
test_stream.open(your_file_name, std::ios::binary | std::ios::in);
char *read_buffer = nullptr;
test_stream.read(reinterpret_cast<char *>(&read_size), sizeof(int)); //最开始的int写的是单个car数据的大小,紧接着是car的具体信息;随后,是下一帧中car的大小,之后跟随car的具体信息,以此类推。
test_stream.read(read_buf, read_size);
car.ParseFromArray(read_buffer, read_size); //从read_buffer中将read_size大小的数据写入car中。