代码走读:caffe中protobuf的详细使用过程
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]保证更高效的编码
}
......
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 BlobShape
public:
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/UnknownFieldSet
inline 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.html
static 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 标识1
inline ::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中定义的成员个数为1
friend 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 { //返回只读dim
return dim_;
}
inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
BlobShape::mutable_dim() { //返回可读写dim
return &dim_;
}
......
略
谷歌官网说明:https://developers.google.com/protocol-buffers/
c++API手册:https://developers.google.com/protocol-buffers/docs/reference/cpp/#google.protobuf
ReadProtoFromTextFile的调用步骤:
train() --> caffe::ReadSolverParamsFromTextFileOrDie(FLAGS_solver, &solver_param); --> ReadProtoFromTextFile(param_file, param)
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);
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++标准输出流中,必须设置所有必需字段。
}
// 将 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;
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;
}
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); // 同上,但是接受缺少必需字段的消息。