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

本文介绍了Protobuf的enum类型,包括如何定义枚举、语法规范、重定义问题,以及相关函数的使用。在Contacts项目中,enum用于表示PhoneType,修改后增加了对PhoneType的处理,包括设置和解析。同时,文章强调了枚举类型的默认值和命名规则,并提供了代码示例展示如何在write.cc和read.cc中应用这些概念。
摘要由CSDN通过智能技术生成

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 <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();
}

image-20230608102039649

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

image-20230608102102029

🎯[说明]:

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

六、总结

在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罅隙`

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值