【Protobuf速成指南】enum类型的使用

文章目录

  • 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文件下测试时,编译后会报错:某某某常量已经被定义。我们来总结这个问题

  1. 同级的枚举常量名不能重复

    //【错误】:同级的MOBILE名发生重复
    enum PhoneType{
    	MOBILE = 0;
    }
    enum PhoneTypeCopy{
    	MOBILE = 0;           
    }
    
  2. 处于非同级的枚举常量名可以重复

    // 【正确】: 处于不同级的枚举常量名可以重复
    enum PhoneType{
    	MOBILE = 0;
    }
    
    message PeopleInfo{
       enum PhoneTypeCopy{
    		MOBILE = 0;           
    	}
    }
    
  3. 引入另一个.proto文件时,同级的枚举常量名不能重复

    // 【错误】:同级的MOBILE名发生重复
    // ----------------- phone.proto -------------------
    enum PhoneType{
    	MOBILE = 0;
    }
    
    // ---------------- contact.prot -----------------
    import phone.proto
    enum PhoneType{
       	MOBILE = 0;
    }
    
  4. 引入另一个.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,
  // ……
};

 对枚举类型 typePhoneType 类型新生成的部分函数(注意枚举常量名和枚举常量值表达意思的不同)

// 检验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 
#include 
#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();
}

【Protobuf速成指南】enum类型的使用_第1张图片

read.cc修改

#include 
#include 
#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;
}

【Protobuf速成指南】enum类型的使用_第2张图片

[说明]:

​  "张三"是我们上次插入的数据,我们并没有对其type类型进行赋值,但是根据proto的语法,对于没有赋值的枚举类型,默认设置枚举常量值为0

六、总结

【Protobuf速成指南】enum类型的使用_第3张图片

你可能感兴趣的:(【Protobuf速成教程】,Protobuf,C++)