NO.2|proto3语法|消息类型|通讯录|文件读取|enum类型

  • 不再打印联系⼈的序列化结果,⽽是将通讯录序列化后并写⼊⽂件中。
  • 从⽂件中将通讯录解析出来,并进⾏打印。
  • 新增联系⼈属性,共包括:姓名、年龄、电话信息、地址、其他联系⽅式、备注

字段规则

消息的字段可以⽤下⾯⼏种规则来修饰:

  • singular:消息中可以包含该字段零次或⼀次(不超过⼀次)。proto3语法中,字段默认使⽤该规则。
  • repeated:消息中可以包含该字段任意多次(包括零次),其中重复值的顺序会被保留。可以理解为定义了⼀个数组。

更新contacts.proto, PeopleInfo 消息中新增 phone_numbers 字段,表⽰⼀个联系⼈有多个号码,可将其设置为repeated,写法如下:

c 复制代码
syntax = "proto3";  
package contacts;  

message PeopleInfo {  
	string name = 1;  
	int32 age = 2;  
	repeated string phone_numbers = 3;  
}

消息类型的定义与使⽤

定义

在单个.proto⽂件中可以定义多个消息体,且⽀持定义嵌套类型的消息(任意多层)。每个消息体中的字段编号可以重复。

更新contacts.proto,我们可以将phone_number提取出来,单独成为⼀个消息:

c 复制代码
syntax = "proto3";  
package contacts;  

message PeopleInfo {  
	string name = 1;  
	int32 age = 2;  
	message Phone {  
		string number = 1;  
	}  
}
c 复制代码
syntax = "proto3";  
package contacts;  

message Phone {  
	string number = 1;  
}
message PeopleInfo {  
	string name = 1;  
	int32 age = 2;  
}
使⽤
  • 消息类型可作为字段类型使⽤

    contacts.proto

  • 可导⼊其他.proto⽂件的消息并使⽤

    Phone消息定义在phone.proto⽂件中

contacts.proto中的 PeopleInfo 使⽤ Phone 消息

在proto3⽂件中可以导⼊proto2消息类型并使⽤它们,反之亦然

创建通讯录2.0版本

通讯录2.x的需求是向⽂件中写⼊通讯录列表,以上我们只是定义了⼀个联系⼈的消息,并不能存放通讯录列表,所以还需要在完善⼀下contacts.proto(终版通讯录2.0):

进⾏⼀次编译

c 复制代码
protoc --cpp_out=. contacts.proto

编译后⽣成的 contacts.pb.h contacts.pb.cc 会将之前的⽣成⽂件覆盖掉

contacts.pb.h更新的部分代码

  • 每个字段都有⼀个clear_⽅法,可以将字段重新设置回empty状态。
  • 每个字段都有设置和获取的⽅法,获取⽅法的⽅法名称与⼩写字段名称完全相同。但如果是消息类型的字段,其设置⽅法为mutable_⽅法,返回值为消息类型的指针,这类⽅法会为我们开辟好空间,可以直接对这块空间的内容进⾏修改。
  • 对于使⽤repeated修饰的字段,也就是数组类型,pb为我们提供了add_⽅法来新增⼀个值,并且提供了_size⽅法来判断数组存放元素的个数。
通讯录2.0的写⼊实现
c 复制代码
// GOOGLE_PROTOBUF_VERIFY_VERSION 宏: 验证没有意外链接到与编译的头⽂件不兼容的库版本。如果检测到版本不匹配,程序将中⽌。注意,每个 .pb.cc ⽂件在启动时都会⾃动调⽤此宏。在使⽤ C++ Protocol Buffer 库之前执⾏此宏是⼀种很好的做法,但不是绝对必要的。  
GOOGLE_PROTOBUF_VERIFY_VERSION;

// 在程序结束时调⽤ ShutdownProtobufLibrary(),为了删除 Protocol Buffer 库分配的所有全局对象。对于⼤多数程序来说这是不必要的,因为该过程⽆论如何都要退出,并且操作系统将负责回收其所有内存。但是,如果你使⽤了内存泄漏检查程序,该程序需要释放每个最后对象,或者你正在编写可以由单个进程多次加载和卸载的库,那么你可能希望强制使⽤ Protocol Buffers 来清理所有内容。  
google::protobuf::ShutdownProtobufLibrary();
  • 运⾏write

  • 查看⼆进制⽂件

hexdump工具
通讯录2.0的读取实现
decode

可以⽤ protoc -h 命令来查看ProtoBuf为我们提供的所有命令option。其中ProtoBuf提供⼀个命令选项 --decode ,表⽰从标准输⼊中读取给定类型的⼆进制消息,并将其以⽂本格式写⼊标准输出。消息类型必须在.proto⽂件或导⼊的⽂件中定义

enum类型

定义规则

语法⽀持我们定义枚举类型并使⽤。在.proto⽂件中枚举类型的书写规范为:

  • 枚举类型名称:
    使⽤驼峰命名法,⾸字⺟⼤写。例如: MyEnum
  • 常量值名称:
    全⼤写字⺟,多个字⺟之间⽤ _ 连接。例如: ENUM_CONST = 0;
    定义⼀个名为PhoneType的枚举类型
c 复制代码
enum PhoneType {  

	MP = 0;   // 移动电话 
	TEL = 1;  // 固定电话

}
  1. 0值常量必须存在,且要作为第⼀个元素。这是为了与proto2的语义兼容:第⼀个元素作为默认值,且值为0。
  2. 枚举类型可以在消息外定义,也可以在消息体内定义(嵌套)。
  3. 枚举的常量值在32位整数的范围内。但因负值⽆效因⽽不建议使⽤(与编码规则有关)。
定义时注意

将两个'具有相同枚举值名称'的枚举类型放在单个.proto⽂件下测试时,编译后会报错:某某某常量已经被定义!所以这⾥要注意:

  • 同级(同层)的枚举类型,各个枚举类型中的常量不能重名。
  • 单个.proto⽂件下,最外层枚举类型和嵌套枚举类型,不算同级。
  • 多个.proto⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都未声明package,每个proto⽂件中的枚举类型都在最外层,算同级。
  • 多个.proto⽂件下,若⼀个⽂件引⼊了其他⽂件,且每个⽂件都声明了package,不算同级。
c 复制代码
// ---------------------- 情况1:同级枚举类型包含相同枚举值名称---------------
enum PhoneType {  
	MP = 0;    // 移动电话 
	TEL = 1;   // 固定电话
}  

enum PhoneTypeCopy {  
	MP = 0; // 移动电话 
	// 编译后报错:MP 已经定义  
}  

// ---------------------- 情况2:不同级枚举类型包含相同枚举值名称------------- 
enum PhoneTypeCopy {  
	MP = 0; // 移动电话 
	// ⽤法正确  
}  

message Phone {  
	string number = 1;  // 电话号码
	enum PhoneType {
		MP = 0;  // 移动电话
		TEL = 1; // 固定电话 
	} 
}  

// ---------------------- 情况3:多⽂件下都未声明package-------------------- 
// phone1.proto  
import "phone1.proto"  
enum PhoneType {  
	MP = 0;   // 移动电话 
	TEL = 1;  // 固定电话
	// 编译后报错:MP 已经定义
}  

// phone2.proto  
enum PhoneTypeCopy {  
	MP = 0; // 移动电话  
}

// ---------------------- 情况4:多⽂件下都声明了package--------------------  
// phone1.proto  
import "phone1.proto"  
package phone1;  

enum PhoneType {  
	MP = 0;   // 移动电话
	TEL = 1;  // 固定电话
	// ⽤法正确
}  

// phone2.proto  
package phone2;  
enum PhoneTypeCopy {  
	MP = 0; // 移动电话  
}

同级枚举类型包含相同枚举值名称

多⽂件下都未声明package

多⽂件下都声明了package

升级通讯录⾄2.1版本

更新contacts.proto(通讯录2.1),新增枚举字段并使⽤

  • 编译
c 复制代码
protoc --cpp_out=.contacts.proto

contacts.pb.h更新的部分代码

  • 对于在.proto⽂件中定义的枚举类型,编译⽣成的代码中会含有与之对应的枚举类型、校验枚举值是否有效的⽅法_IsValid、以及获取枚举值名称的⽅法_Name。

  • 对于使⽤了枚举类型的字段,包含设置和获取字段的⽅法,已经清空字段的⽅法clear_。

  • 更新write.cc(通讯录2.1)

  • 更新read.cc(通讯录2.1)

  • 编译后进⾏读写验证

相关推荐
mhkxbq3 分钟前
昆仑G5580、G5680 V2、G2280及泰山鲲鹏200,AI大数据优选服务器
大数据·服务器·人工智能
Lyyaoo.4 分钟前
【JAVA基础面经】线程安全的单例模式
java·安全·单例模式
Hello--_--World8 分钟前
VUE:逻辑复用
前端·javascript·vue.js
_李小白9 分钟前
【OSG学习笔记】Day 39: NodeCallback(帧回调机制)
java·笔记·学习
如来神掌十八式11 分钟前
设计模式之装饰器模式
java·设计模式
艾莉丝努力练剑12 分钟前
【QT】Qt常用控件与布局管理深度解析:从原理到实践的架构思考
linux·运维·服务器·开发语言·网络·qt·架构
cch891818 分钟前
C++、Python与汇编语言终极对比
java·开发语言·jvm
码云数智-园园22 分钟前
Python 列表与元组:从核心区别到实战选型
运维·服务器·windows
陶甜也24 分钟前
3D智慧城市:blender建模、骨骼、动画、VUE、threeJs引入渲染,飞行视角,涟漪、人物行走
前端·3d·vue·blender·threejs·模型
IMPYLH25 分钟前
Linux 的 mv 命令
linux·运维·服务器·bash