protobuf安装和使用

一、概述

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中。

 


 

猜你喜欢

转载自blog.csdn.net/Cxiazaiyu/article/details/100574475
今日推荐