文章目录
- 2.1枚举类型
- 一、如何定义枚举类型?
- 二、语法规范
- 三、重定义问题
- 四、enum类型相关函数
- 五、Contact 2.1 改写
- 六、总结
2.1枚举类型
本系列文章将通过对通讯录项目的不断完善,带大家由浅入深的学习Protobuf的使用。这是Contacts的2.1版本,在这篇文章中将带大家学习Protobuf的enum类型语法,并将其用到我们的项目中
一、如何定义枚举类型?
.proto文件中可以定义枚举类型。如下我们可以定义了一个PhoneType枚举类型:
enum PhoneType{MOBILE = 0;FIXED = 1;
}
🎯[书写规范]:
- 枚举类型名建议采用驼峰命名,开头大写
- enum 类型成员被定义为具名常量。常量命名建议全部使用大写,多个字母之间使用
_
连接,例如:ENUM_COUNT = 0
二、语法规范
-
0值常量必须存在,且要作为第一个元素。这是为了与proto2的语义兼容:第一个元素作为默认值,且值为0。
// 错误示范1: 0值常量必须存在 enum PhoneType{MOBILE = 1;FIXED = 2; } // 错误示范2:0值常量必须作为第一个元素 enum PhoneType{FIXED = 1;MOBILE = 0; }
-
枚举类型除了可以在消息外定义,也可以在消息体内定义(嵌套)
message PeopleInfo{// ……enum PhoneType{FIXED = 0;MOBILE = 1;} }
-
枚举的常量值在32位整数的范围内。但因负值无效因而不建议使用(与编码规则有关)
三、重定义问题
将两个具有相同枚举值名称的枚举类型放在单个.proto文件下测试时,编译后会报错:某某某常量已经被定义。我们来总结这个问题
-
同级的枚举常量名不能重复
//【错误】:同级的MOBILE名发生重复 enum PhoneType{MOBILE = 0; } enum PhoneTypeCopy{MOBILE = 0; }
-
处于非同级的枚举常量名可以重复
// 【正确】: 处于不同级的枚举常量名可以重复 enum PhoneType{MOBILE = 0; }message PeopleInfo{enum PhoneTypeCopy{MOBILE = 0; } }
-
引入另一个.proto文件时,同级的枚举常量名不能重复
// 【错误】:同级的MOBILE名发生重复 // ----------------- phone.proto ------------------- enum PhoneType{MOBILE = 0; }// ---------------- contact.prot ----------------- import phone.proto enum PhoneType{MOBILE = 0; }
-
引入另一个.proto文件时,如果两个文件在不同的package下,则枚举常量名可以重复。package起到了隔离的作用,避免了冲突的发生
// 【正确】:因为处于不同的package下 // ----------------- phone.proto ------------------- package phone enum PhoneType{MOBILE = 0; }// ---------------- contact.prot ----------------- import phone.proto enum PhoneType{MOBILE = 0; }
🎯[总结]:
- 同级的枚举常量名不能重复
- 处于不同package下的常量名可以重复
四、enum类型相关函数
// 原始版本
message PeopleInfo{string name = 1;int32 age = 2;message Phone{ string number = 1;string type = 2;}repeated Phone phone = 3;
}
将Phone的type类型改为枚举类型:
// 修改后的版本
message PeopleInfo{string name = 1;int32 age = 2;message Phone{ string number = 1;enum PhoneType{MOBILE = 0;FIXED = 1;}PhoneType type = 2;}repeated Phone phone = 3;
}
对上面修改后的 .proto 文件进行编译后,观察生成的 .h
的变化
// 新生成了枚举类
enum PeopleInfo_Phone_PhoneType : int {PeopleInfo_Phone_PhoneType_MOBILE = 0,PeopleInfo_Phone_PhoneType_FIXED = 1,// ……
};
对枚举类型 type 和 PhoneType 类型新生成的部分函数(注意枚举常量名和枚举常量值表达意思的不同)
// 检验value是不是有效的枚举常量值
static inline bool PhoneType_IsValid(int value); // 返回枚举常量值对应的枚举常量名
static inline const std::string& PhoneType_Name(T enum_t_value);// 将枚举常量名解析为对应的枚举常量值
static inline bool PhoneType_Parse(const std::string& name, PhoneType* value) // -----------------------------------------------------------------// 获取type的枚举常量值
::contacts::PeopleInfo_Phone_PhoneType type() const;// 设置为默认值,即值为0的枚举值
void clear_type();// 设置枚举类型值
void set_type(::contacts::PeopleInfo_Phone_PhoneType value);
// value 为上一代码块提到的枚举类,例如“PeopleInfo_Phone_PhoneType_MOBILE”
五、Contact 2.1 改写
write.cc修改
#include <iostream>
#include <fstream>
#include "contact.pb.h"using namespace std;void AddPeopleInfo(contact2::PeopleInfo* p){cout << "----------新增联系人----------" << endl;cout << "请输入联系人姓名: ";string name;getline(cin, name);p->set_name(name);cout << "请输入联系人年龄: ";int age;cin >> age;p->set_age(age);// 一直清空,直到读到 \n(\n也会被清除),或者清空,直到清空了256个字符cin.ignore(256, '\n'); // 说明:cin输入后,换行符还会被留在缓冲区中,而getline读到\n就停了// 所以需要使用 cin.ignore 清空缓冲区从而避免对之后的输入产生影响for(int i = 0;; i++){cout << "请输入联系人电话" << i + 1 << "(只输入回车则结束): ";string number;getline(cin, number);if(number.empty()) break;contact2::PeopleInfo_Phone* phone = p->add_phone();phone->set_number(number);// ------------------修改部分-----------------------cout << "请选择电话类型: (0:MOBIle 1:FIXED)";int type;cin >> type;cin.ignore(256, '\n');switch(type){case 0:phone->set_type(contact2::PeopleInfo_Phone_PhoneType_MOBILE);break;case 1:phone->set_type(contact2::PeopleInfo_Phone_PhoneType_FIXED);break;default:cout << "非法选择, 将采用默认值" << endl;}// ---------------------修改部分---------------------}cout << "-----------添加成功-----------" << endl;
}int main(int argc, char* argv[]){if(argc != 2){cerr << "use: ./write filename" << endl;exit(1);}contact2::Contact contact;// 1. 读取通讯录中的原始数据fstream input(argv[1], ios::in | ios::binary); // 二进制的方式读取if(!input){cerr << argv[1] << " file not find. Creating a new file" << endl;} else if(!contact.ParseFromIstream(&input)){ // 从输出流中反序列化cerr << "parser original file error" << endl;exit(2); }// 2. 新增联系人AddPeopleInfo(contact.add_contact());// 3. 写入文件中fstream output(argv[1], ios::out | ios::trunc | ios::binary);if(!output || !contact.SerializePartialToOstream(&output)){cerr << "write error" << endl;exit(3);}cout << "write success" << endl;input.close();output.close();
}
read.cc修改
#include <iostream>
#include <fstream>
#include "contact.pb.h"using namespace std;void PrintContact(contact2::Contact& contact){for(int i = 0; i < contact.contact_size(); i++){ const contact2::PeopleInfo people = contact.contact(i);cout << "-------------联系人" << i + 1 << "-------------" << endl;cout << "name: " << people.name() << endl;cout << "age: " << people.age() << endl;;int j = 1;// ---------------------修改部分---------------------------// phone.type()枚举常量值// PhoneType_Name根据枚举常量值来返回对应的枚举常量名for(const auto& phone : people.phone()){cout << "number" << j++ << ": " << phone.number()<< " type: " << phone.PhoneType_Name(phone.type()) << endl;// ---------------------修改部分---------------------------}}
}int main(int argc, char* argv[])
{if(argc != 2){cerr << "use: ./read file" << endl;exit(1);}contact2::Contact contact;// 1. 读取通讯录中的原始数据fstream input(argv[1], ios::in | ios::binary); // 二进制的方式读取if(!input){cerr << argv[1] << " file not find. exit" << endl;exit(2);} else if(!contact.ParseFromIstream(&input)){ // 从输出流中反序列化cerr << "parser original file error" << endl;exit(2); }PrintContact(contact);input.close();return 0;
}
🎯[说明]:
"张三"是我们上次插入的数据,我们并没有对其type类型进行赋值,但是根据proto的语法,对于没有赋值的枚举类型,默认设置枚举常量值为0