代码走读:caffe中protobuf的详细使用过程

【一】proto文件,以caffe.proto中BlobShape为例

syntax = "proto2";    //指明protobuf版本,默认是v2,其它版本:"proto3"package caffe;     // 最终生成c++代码: namespace caffe message BlobShape {  // 最终生成c++代码: class BlobShape : public ::google::protobuf::Message {//下面语句最终生成c++代码: google::protobuf::RepeatedField< ::google::protobuf::int64 > dim_;repeated int64 dim = 1 [packed = true];    //repeated列表字段,等号后面数字表示标识符,[packed=true]保证更高效的编码
}

【二】编译命令 protoc -I=. --cpp_out=. caffe.proto

【三】生成的caffe.pb.h

......
namespace caffe {   //对应caffe.proto中 package caffe;
// protobuf内部实现细节——不要调用这些接口
void  protobuf_AddDesc_caffe_2eproto();
void protobuf_AssignDesc_caffe_2eproto();
void protobuf_ShutdownFile_caffe_2eproto();class BlobShape;
// ===================================================================
class BlobShape : public ::google::protobuf::Message {  //对应caffe.proto中 message BlobShapepublic:BlobShape(); // 构造函数virtual ~BlobShape(); //析构函数BlobShape(const BlobShape& from); // 拷贝构造函数inline BlobShape& operator=(const BlobShape& from) { //赋值构造函数CopyFrom(from);return *this;}// ** UnknownFieldSet用于跟踪解析协议消息但其字段编号或类型无法识别时看到的字段。
// 这种情况最常发生在将新字段添加到消息类型中,然后包含这些字段的消息由添加新类型之前编译的旧软件读取。
// 大多数用户永远不需要使用这个类。比如在caffe中就没有使用到
// 参考网址:https://developers.google.com/protocol-buffers/docs/reference/java/com/google/protobuf/UnknownFieldSetinline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {return _unknown_fields_;}inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {return &_unknown_fields_;}// ** 描述元数据,主要用在反射上,caffe中没有用到
// 参考网址:http://www.blogjava.net/DLevin/archive/2015/04/01/424012.htmlstatic const ::google::protobuf::Descriptor* descriptor();static const BlobShape& default_instance();void Swap(BlobShape* other);   // 交换// implements Message 消息实现----------------------------------------------BlobShape* New() const;    // 新建void CopyFrom(const ::google::protobuf::Message& from);    // 复制void MergeFrom(const ::google::protobuf::Message& from);   // 合并:单数字段会被覆盖、Repeated (类似链表)字段会被连接到一起void CopyFrom(const BlobShape& from);void MergeFrom(const BlobShape& from);void Clear();  // 清除消息的所有字段,并将它们设置为默认值。bool IsInitialized() const;  // 快速检查是否所有必需字段都设置了值。int ByteSize() const;  // 消息的序列化大小bool MergePartialFromCodedStream(::google::protobuf::io::CodedInputStream* input);   //从流中读取协议缓冲区并将其合并到此消息中。void SerializeWithCachedSizes(::google::protobuf::io::CodedOutputStream* output) const;  //序列化消息而不重新计算大小。::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;    //序列化到output中int GetCachedSize() const { return _cached_size_; }    //返回上次调用ByteSize()的结果private:void SharedCtor();void SharedDtor();void SetCachedSize(int size) const;public:::google::protobuf::Metadata GetMetadata() const;    // 获取缓冲大小// nested types message内嵌的类型 ----------------------------------------------------// 如果在proto的mesage中定义/* enum SnapshotFormat {HDF5 = 0;BINARYPROTO = 1;}*/// 则生成如下代码:/*enum SolverParameter_SnapshotFormat {SolverParameter_SnapshotFormat_HDF5 = 0,SolverParameter_SnapshotFormat_BINARYPROTO = 1};bool SolverParameter_SnapshotFormat_IsValid(int value);const SolverParameter_SnapshotFormat SolverParameter_SnapshotFormat_SnapshotFormat_MIN = SolverParameter_SnapshotFormat_HDF5;const SolverParameter_SnapshotFormat SolverParameter_SnapshotFormat_SnapshotFormat_MAX = SolverParameter_SnapshotFormat_BINARYPROTO;const int SolverParameter_SnapshotFormat_SnapshotFormat_ARRAYSIZE = SolverParameter_SnapshotFormat_SnapshotFormat_MAX + 1;typedef SolverParameter_SnapshotFormat SnapshotFormat;static const SnapshotFormat HDF5 = SolverParameter_SnapshotFormat_HDF5;static const SnapshotFormat BINARYPROTO = SolverParameter_SnapshotFormat_BINARYPROTO;static inline bool SnapshotFormat_IsValid(int value) {return SolverParameter_SnapshotFormat_IsValid(value);}static const SnapshotFormat SnapshotFormat_MIN = SolverParameter_SnapshotFormat_SnapshotFormat_MIN;static const SnapshotFormat SnapshotFormat_MAX = SolverParameter_SnapshotFormat_SnapshotFormat_MAX;static const int SnapshotFormat_ARRAYSIZE = SolverParameter_SnapshotFormat_SnapshotFormat_ARRAYSIZE;static inline const ::google::protobuf::EnumDescriptor*SnapshotFormat_descriptor() {return SolverParameter_SnapshotFormat_descriptor();}static inline const ::std::string& SnapshotFormat_Name(SnapshotFormat value) {return SolverParameter_SnapshotFormat_Name(value);}static inline bool SnapshotFormat_Parse(const ::std::string& name, SnapshotFormat* value) {return SolverParameter_SnapshotFormat_Parse(name, value);}*/// accessors -------------------------------------------------------// repeated int64 dim = 1 [packed = true]; // 对 dim 成员的操作inline int dim_size() const;  // 返回dim 列表大小inline void clear_dim();   // 清空static const int kDimFieldNumber = 1; // *FieldNumber 的值对应 dim = 1 标识1inline ::google::protobuf::int64 dim(int index) const; // 获取列表dim中指定索引的值inline void set_dim(int index, ::google::protobuf::int64 value); // 设置列表dim指定索引的值inline void add_dim(::google::protobuf::int64 value); // 添加新元素到列表dim中inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >& dim() const; // 返回dim的const引用,不可修改inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*  mutable_dim(); // 返回dim的指针,可修改// @@protoc_insertion_point(class_scope:caffe.BlobShape)private:::google::protobuf::UnknownFieldSet _unknown_fields_;::google::protobuf::RepeatedField< ::google::protobuf::int64 > dim_;mutable int _dim_cached_byte_size_;mutable int _cached_size_;::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];  // 1表示 message BlobShape中定义的成员个数为1friend void  protobuf_AddDesc_caffe_2eproto();friend void protobuf_AssignDesc_caffe_2eproto();friend void protobuf_ShutdownFile_caffe_2eproto();void InitAsDefaultInstance();static BlobShape* default_instance_;
};
// ===================================================================
// BlobShape对应的操作
// repeated int64 dim = 1 [packed = true];
inline int BlobShape::dim_size() const { //dim是列表形式,因此提供返回大小的接口return dim_.size();
}
inline void BlobShape::clear_dim() { // 清空dim_.Clear();
}
inline ::google::protobuf::int64 BlobShape::dim(int index) const { //获取指定索引的值return dim_.Get(index);
}
inline void BlobShape::set_dim(int index, ::google::protobuf::int64 value) { //设置指定索引的值dim_.Set(index, value);
}
inline void BlobShape::add_dim(::google::protobuf::int64 value) { //增量添加值到列表dim中dim_.Add(value);
}
inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
BlobShape::dim() const { //返回只读dimreturn dim_;
}
inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
BlobShape::mutable_dim() { //返回可读写dimreturn &dim_;
}
......

【四】生成的caffe.pb.cc

【五】官网说明

谷歌官网说明:https://developers.google.com/protocol-buffers/
c++API手册:https://developers.google.com/protocol-buffers/docs/reference/cpp/#google.protobuf

【六】使用

1、将文本格式的 protobuf 读取到 Message 中
1.1 以 caffe 中 ReadProtoFromTextFile 为例

ReadProtoFromTextFile的调用步骤:
train() --> caffe::ReadSolverParamsFromTextFileOrDie(FLAGS_solver, &solver_param); --> ReadProtoFromTextFile(param_file, param)

1.2、ReadProtoFromTextFile 源码详解
bool ReadProtoFromTextFile(const char* filename, Message* proto) {int fd = open(filename, O_RDONLY);CHECK_NE(fd, -1) << "File not found: " << filename;FileInputStream* input = new FileInputStream(fd);bool success = google::protobuf::TextFormat::Parse(input, proto);delete input;close(fd);return success;
}

1.2.1 FileInputStream 详解
官方说明:

FileInputStream 继承自 ZeroCopyInputStream(从文件描述符读取流)

使用方法:(参考构建函数)

explicit FileInputStream(int file_descriptor, int block_size = -1);

参数:

 file_descriptor : Unix 文件描述符block_size:指定 Next() 时读取和返回的大小,不指定时,将使用一个合理的默认值。

1.2.2 google::protobuf::TextFormat::Parse
TextFormat 类:

该类实现 protobuf 文本格式。以文本格式打印和解析协议消息对于调试和人工编辑消息非常有用。
这个类实际上是一个只包含静态方法的命名空间。

静态函数 Parse:

从给定的输入流解析 文本格式协议消息 到给定的 消息对象。这个函数解析Print()编写的格式。
static bool Parse(io::ZeroCopyInputStream* input, Message* output);

其它类似静态函数:

static bool ParseFromString(const string& input, Message* output);  // 直接从字符串解析
static bool Merge(io::ZeroCopyInputStream* input, Message* output); // 从流中解析,并合并到 Message 中,参见 Message::MergeFrom()
static bool MergeFromString(const string& input, Message* output);  // 同上,但是直接从字符串解析//解析单个字段值到指定的 message 的指定 field 字段中
static bool ParseFieldValueFromString(const string& input, const FieldDescriptor* field, Message* message);

对应的反操作:将 Message 打印到流或文本中

static bool Print(const Message& message, io::ZeroCopyOutputStream* output); // 将 message 打印到输出流中
static bool PrintToString(const Message& message, string* output);  // 将 message 打印到字符串中// 将 message 指定的 field 字段,输出到字符串中。字段是列表格式时,指定引索 index;非列表格式时 index 必须指定为 -1
static void PrintFieldValueToString(const Message& message, const FieldDescriptor* field, int index, string* output); // 打印未知数集 UnknownFieldSet 中的字段。它们只按标签号打印。通过尝试解析嵌入的消息,可以直观地识别它们。
static bool PrintUnknownFields(const UnknownFieldSet& unknown_fields, io::ZeroCopyOutputStream* output);
2、将 message 序列化到二进制文件中
2.1 以 caffe 中 WriteProtoToBinaryFile 函数为例
void WriteProtoToBinaryFile(const Message& proto, const char* filename) {fstream output(filename, ios::out | ios::trunc | ios::binary);CHECK(proto.SerializeToOstream(&output)); // 原型bool Message::SerializeToOstream(ostream* output) const; 将 Message 序列化到c++标准输出流中,必须设置所有必需字段。
}
2.2 序列化函数汇总:
// 将 Message 序列化到文件描述符中,必须设置所有必需字段。
bool SerializeToFileDescriptor(int file_descriptor) const;  // 同上,但是允许缺少必需的字段。
bool SerializePartialToFileDescriptor(int file_descriptor) const;// 将 Message 序列化到c++标准输出流中,必须设置所有必需字段。
bool SerializeToOstream(ostream* output) const; // 同上,但是允许缺少必需的字段。
bool SerializePartialToOstream(ostream* output) const;
3、从二进制文件中 反序列化 到 message 中
3.1 以 caffe 中 ReadProtoFromBinaryFile 为例
bool ReadProtoFromBinaryFile(const char* filename, Message* proto) {int fd = open(filename, O_RDONLY);CHECK_NE(fd, -1) << "File not found: " << filename;ZeroCopyInputStream* raw_input = new FileInputStream(fd);CodedInputStream* coded_input = new CodedInputStream(raw_input);coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);bool success = proto->ParseFromCodedStream(coded_input);  // 原型 bool ParseFromCodedStream(io::CodedInputStream* input);delete coded_input;delete raw_input;close(fd);return success;
}
3.2 其它反序列化函数:
bool ParsePartialFromCodedStream(io::CodedInputStream* input);   // 同 ParseFromCodedStream ,但是接受缺少必需字段的消息。
bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);       // 从 io::ZeroCopyInputStream 流中获取
bool ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input); // 同上,但是接受缺少必需字段的消息。
bool ParseFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, int size); // 同上,指定 message 大小必须是 size
bool ParsePartialFromBoundedZeroCopyStream(io::ZeroCopyInputStream* input, , int size); // 同上,但是接受缺少必需字段的消息。

【C++】Google Protocol Buffer(protobuf)详解(二)相关推荐

  1. Protocol Buffer技术详解(语言规范)

     该系列Blog的内容主体主要源自于Protocol Buffer的官方文档,而代码示例则抽取于当前正在开发的一个公司内部项目的Demo.这样做的目的主要在于不仅可以保持Google文档的良好风格 ...

  2. Protocol Buffer技术详解(C++实例)

    原文:http://www.cnblogs.com/stephen-liu74/archive/2013/01/04/2842533.html 这篇Blog仍然是以Google的官方文档为主线,代码实 ...

  3. 【C++】Google Protocol Buffer(protobuf)详解(一)

    1.简介 Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准, Protocol Buffers 是一种轻便高效的结构化数据存储格式 ...

  4. 前端后台以及游戏中使用Google Protocol Buffer详解

    前端后台以及游戏中使用Google Protocol Buffer详解 前端后台以及游戏中使用Google Protocol Buffer详解 0.什么是protoBuf 1.下载protobuf的编 ...

  5. 由Google Protocol Buffer的小例子引起的g++编译问题

    问题 学习 Google Protocol Buffer 的使用和原理时,提供了一个小例子,讲述了protobuf的使用方法. 假如已经有了如下文件: 其中writer.cpp如下: #include ...

  6. windows下Google Protocol Buffer 编译安装(vs)教程

    蒲公英的博客 分享技术带来的快乐 Kuaile.IN 搜索 首页 安卓相关 WordPress 主机相关 Linux相关 网络技术 电脑技术 编程技术 免费资源 当前位置: 首页 > 编程技术 ...

  7. Google Protocol Buffer 的使用和原理

    FROM : https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/ Google Protocol Buffer 的使用和原理 Protocol ...

  8. C#使用Protocol Buffer(ProtoBuf)进行Unity中的Socket通信

    From: http://www.jb51.net/article/82795.htm 这篇文章主要介绍了C#使用Protocol Buffer(ProtoBuf)进行Unity的Socket通信的实 ...

  9. Google Protocol Buffer 的使用和原理(c++)

    简介 什么是 Google Protocol Buffer? 假如您在网上搜索,应该会得到类似这样的文字介绍: Google Protocol Buffer( 简称 Protobuf) 是 Googl ...

  10. PopUpWindow使用详解(二)——进阶及答疑

    相关文章: 1.<PopUpWindow使用详解(一)--基本使用> 2.<PopUpWindow使用详解(二)--进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...

最新文章

  1. Grape和Sinatra结合使用
  2. boost::fusion::insert用法的测试程序
  3. Docker(二)基本操作命令
  4. mysql数据库移植
  5. Oracle 中调用外部C动态库函数
  6. Cookie学习总结
  7. 华为鸿蒙平板界面,华为工作人员曝光:鸿蒙OS正式版,平板手机将采用全新UI界面...
  8. 吴恩达深度学习5.2练习_Sequence Models_Operations on word vectors
  9. Wi-Fi 6这么“6” 原来靠的是这些黑科技!
  10. 1405 mysql 2800_mysql关于任何用户登录以及解决ERROR1405
  11. Android studio3.5读取项目资源文件的图片
  12. SQLServer 2008 r2 安装图解
  13. 听说某琳系统停止更新了,其他的主机管理系统哪个好?
  14. 机器学习原来如此有趣:如何用深度学习进行语音识别 #精选APPLEAR
  15. linux进程3种状态,进程的三种状态及转换
  16. 计算机组成原理试题 一,计算机组成原理试题一
  17. 阿里云无影云桌面服务关联角色设置及删除方法
  18. UNIX环境高级编程(1)——UNIX系统总览
  19. docker导致宿主机重启和Cgroup 驱动程序有关
  20. 计算机未来的发展趋势

热门文章

  1. 【神经网络】(7) 迁移学习(CNN-MobileNetV2),案例:乳腺癌二分类
  2. Miller方法产生、检验素数
  3. 用c语言设计一个菜单界面_用这7个方法设计菜单,让餐厅的利润暴涨
  4. 卷积后feature map尺寸计算公式
  5. 【实用】Angular中如何实现类似Vuex的全局变量状态变化功能?
  6. 判断为空:null、undefined、空字符串、中文空格
  7. 安装vim-go插件之后遇到的gopls警告信息不消失的问题的解决方法
  8. XPath与多线程爬虫
  9. python多版本共存使用pip
  10. js左侧三级菜单导航代码