Proto Buffer

参考资料

proto文件编写指南
官方文档

1.ubuntu安装proto编译器

sudo apt-get install protobuf-compiler

2.Packages

如果 .proto 文件中包含 package 定义,此文件的整个内容将被放置到对应的C++命名空间中。例如,假定 package 声明为:

package foo.bar

则此 .proto 文件中所有声明将都属于命名空间 foo::bar。

3.Massages(相当于类)

比如先定义一个消息声明如下:

massage Person{}

在编译时,protocol buffer编译器自动生成Person类,公开地(publicly)派生自 google::protobuf::Message。

4.字段规则

4.1 required: 必须赋值的字段

4.2 optional: 可有可无的字段,可以使用[default = xxx]配置默认值
如果没有为optional的元素指定默认值,就会使用与特定类型相关的默认值:对string来说,默认值是空字符串。对bool来说,默认值是false。对数值类型来说,默认值是0。对枚举来说,默认值是枚举类型定义中的第一个值。

optional int32 result_per_page = 3 [default = 10]; // 默认值为10

4.3 repeated: 可重复变长字段,类似于C++中的list。

5.示例:addressbook.proto

示例来源
注:Person 内部声明了一个enum和一个message,这类似于C++中的类内声明,Person外部的结构可以用 Person.PhoneType 的方式来使用PhoneType。当使用外部package中的结构时,要使用 pkgName.msgName.typeName 的格式,每两层之间使用’.'来连接,类似C++中的"::"。

// Filename: addressbook.proto
 
 // 使用protobuf的编译器版本为v2
 syntax="proto2";
 
// proto3 已舍弃 required 字段,optional 字段也无法显示使用(因为缺省默认就设置为 optional)
// 声明了一个包名,用来防止不同的消息类型命名冲突,类似于 namespace
 package addressbook;
 
 import "src/help.proto";      //举例用,编译时去掉
 
 message Person {
     required string name = 1;     //字段
     required int32 id = 2;
     optional string email = 3;
 
     enum PhoneType {              //枚举
         MOBILE = 0;
         HOME = 1;
         WORK = 2;
     }
 
     message PhoneNumber {
         required string number = 1;
         optional PhoneType type = 2 [default = HOME];   //为optional指定默认值
     }
 
     repeated PhoneNumber phone = 4;
 }
 
 message AddressBook {
     repeated Person person_info = 1;
}

6.编译

作用:将 .proto 文件转换成对应平台(python、C++、Java)的代码文件。
编译指令如下所示:

# 在终端输入下列命令进行编译
protoc -I=$SRC_DIR --xxx_out=$DST_DIR   $SRC_DIR/addressbook.proto

# 参数说明
# 1. $SRC_DIR:指定需要编译的.proto文件目录 (如没有提供则使用当前目录)
# 2. --xxx_out:xxx根据需要生成代码的类型进行设置
	"""
	对于 Java ,xxx =  java ,即 -- java_out
	对于 C++ ,xxx =  cpp ,即 --cpp_out
	对于 Python,xxx =  python,即 --python_out
	"""

# 3. $DST_DIR :编译后代码生成的目录 (通常设置与$SRC_DIR相同)
# 4. 最后的路径参数:需要编译的.proto 文件的具体路径

# 编译通过后,Protoco Buffer会根据不同平台生成对应的代码文件
# 一般来说,./代表当前目录,若编译后的.h/.cc仍放在当前目录下,可用以下命令。
protoc -I=./ --cpp_out=./ test.proto

7.编译后

参考

7.1 通过查看头文件,可以发现针对每个字段都会大致生成如下几种函数,以number为例:

// required string number = 1;
 inline bool has_number() const;
 inline void clear_number();
 inline const ::std::string& number() const;
 inline void set_number(const ::std::string& value);
 inline void set_number(const char* value);
 inline ::std::string* mutable_number();

可以看出,对于每个字段会生成一个has函数(has_number)、clear清除函数(clear_number)、set函数(set_number)、get函数(number和mutable_number)。这儿解释下get函数中的两个函数的区别,对于原型为const std::string &number() const的get函数而言,返回的是常量字段,不能对其值进行修改。但是在有一些情况下,对字段进行修改是必要的,所以提供了一个mutable版的get函数,通过获取字段变量的指针,从而达到改变其值的目的。

7.2 而对于字段修饰符为repeated的字段生成的函数,则稍微有一些不同,如phone字段,则编译器会为其产生如下的代码:

 // repeated .Person.PhoneNumber phone = 4;
 inline int phone_size() const;
 inline void clear_phone();
 inline const ::google::protobuf::RepeatedPtrField< ::Person_PhoneNumber >& phone() const;
 inline ::google::protobuf::RepeatedPtrField< ::Person_PhoneNumber >* mutable_phone();
 inline const ::Person_PhoneNumber& phone(int index) const;
 inline ::Person_PhoneNumber* mutable_phone(int index);
 inline ::Person_PhoneNumber* add_phone();

可以看出,set函数变成了add函数,这个其实很好理解。上面也说过,repeated修饰的字段在高级语言中的实现可能是个数组或动态数组,所以当然通过添加的方式来加入新的字段值。而且get函数也变化很大,这个也不用多说了。

其他参考资料

参考资料1
参考资料2
参考资料3
参考资料4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值