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

news2024/12/23 14:14:27

文章目录

  • 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 <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

六、总结

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/622854.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【IMX6ULL驱动开发学习】02.IMX6ULL烧写Linux系统

由于我买的是正点原子的IMX6ULL阿尔法开发板&#xff0c;但是我是看韦东山老师视频学习的驱动 所以这里我烧录的方法是按照韦东山老师的课程来的 这里给出烧写Linux系统用到的工具 链接&#xff1a;https://pan.baidu.com/s/1bD-xxn3K8xQAVkJSaJmTzQ 提取码&#xff1a;af6w …

操作系统1-操作系统的基本特征和主要功能

目录 1、操作系统的目标和作用 &#xff08;1&#xff09;操作系统的目标 &#xff08;2&#xff09;操作系统的作用 2、操作系统的发展过程 &#xff08;1&#xff09;未配置操作系统的计算机系统 &#xff08;2&#xff09;单道批处理系统(Simple Batch Processing Sys…

企企通受邀出席多场高质量数智化活动,推进行业数字化升级转型

当前&#xff0c;数字经济已成为引领全球经济社会变革、推动我国经济高质量发展的重要引擎。Chat GPT的横空出世&#xff0c;宣告虚实无缝结合的跨场景体验时代即将到来。在云计算、大数据、人工智能、物联网等技术助推下&#xff0c;经过大量实践证明&#xff0c;数字化、智能…

工业RFID读写器选择指南

工业RFID读写器在工业领域上可提升自动化、现代化工业生产的效率&#xff0c;那么企业在选择工业读写器的时候&#xff0c;需要注重哪些方面&#xff0c;如何选择呢?以下是ANDEWELL给大家准备的工业RFID读写器选择指南! 1、根据应用场景选择 根据不同的应用场景&#xff0c;要…

【Docker安装部署Neo4j保姆级教程】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

大数据架构师岗位的主要职责概述(合集)

大数据架构师岗位的主要职责概述1 职责&#xff1a; 1.参与公司数据平台系统规划和架构工作&#xff0c;主导系统的架构设计和项目实施&#xff0c;确保项目质量和关键性能指标达成; 2.统筹和推进制造工厂内部数据系统的构建&#xff0c;搭建不同来源数据之间的逻辑关系&#x…

10.无监督学习

10.1 无监督学习的定义 监督学习&#xff1a;我们有一些列标签&#xff0c;然后用假设函数去拟合它 无监督学习&#xff1a;给出的数据不带任何标签。对于无监督学习来说&#xff0c;需要做的就是将数据输入到算法中&#xff0c;让算法找到一些隐含在数据中的结构&#xff0c;通…

IDEA代码提示设置

1. 打开File -> setting -> Editor -> Live Templates 2. 点击中间框框中的右侧""号,选择 Template Group, 命名为MyGroup(随便起名字) 3. 选中 MyGroup 点击右侧""号,选择Live Template Abbreviation 快捷提示 Description 描述 Template tex…

探究核心技术最佳实践,云原生OLAP论坛火热开启!

2023/06/11&#xff0c;09:00-12:30&#xff0c;在DataFunSummit 2023&#xff1a;OLAP引擎架构峰会上&#xff0c;由阿里云资深技术专家&#xff0c;实时数仓 Hologres 研发负责人姜伟华老师出品的云原生OLAP论坛讲邀请来自阿里云、亚马逊云科技、三七互娱、聚水潭、诺亚财富的…

相机成像模型(二)

相机内参/外参 将上述公式整合到一起,得到世界坐标系中点(Xw,Yw,Zw)映射到像素坐标系中点(u,v)的计算过程,同时给出相机内参、外参的定义如下。 相机外参:描述世界坐标系与相机坐标系之间的变换关系,可将世界坐标系中的点变换至相机坐标系;依赖世界坐标系的定义。 相机内参…

ubuntu18.04LTS编译openssl库进行rsa加密解密

一、下载openssl库源码&#xff0c;下载地址&#xff1a;https://ftp.openssl.org/source/ 我下载的版本&#xff1a; 二、编译源码 1、解压源码&#xff1a; tar zxvf openssl-1.1.1u.tar.gz 2、进入到解压后的文件夹中&#xff1a; cd openssl-1.1.1u/3、执行配置&#x…

6.数组、排序和查找|Java学习笔记

文章目录 数组数组使用注意事项和细节数组赋值机制 排序查找相关链接 数组 数组可以存放多个同一类型的数据。数组也是一种数据类型&#xff0c;是引用类型。 数据类型 数组名[] new 数据类型[大小];int a[] 或者 int[] a 都可以。 int[] a {1,2,3};// 二维数组的声明方式有…

ThreeJS教程:山脉地形高度可视化

推荐&#xff1a;将 NSDT场景编辑器加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 山脉地形高度可视化 一个山脉地形的高度可视化&#xff0c;具体说就是地形不同的高度设置不同的颜色值。有多种方式&#xff0c;下面就举一个设置顶点颜色.attributes.color的例子 …

魏副业而战:夸克网盘拉新如何引流推广?3个方法,让你多赚5W+

我是魏哥&#xff0c;与其在家躺平&#xff0c;不如魏副业而战&#xff01; 夸克网盘拉新项目&#xff0c;相信大家都知道吧。 提到拉新&#xff0c;很多人不屑一顾&#xff0c;认为拉人头的项目&#xff0c;没什么前途。 这个想法&#xff0c;魏哥是反对的。 任何项目的存…

阿里巴巴 2023 版(Java 岗)面试突击手册,Github 已标星42K

程序员作为一个自带“高薪多金”标签的职业&#xff0c;收入要高于市场的平均薪资&#xff0c;即便是在 2023 年,程序员的薪资依然保持居高不下。 据拉勾发布的《2022 程序员群体职场洞察报告》显示计算机专业的应届本科生起薪普遍高于其他职业的平均薪资水平。77%的本科毕业生…

[数据库]数据库事务,管理等级,并发锁

1.数据库事务 数据库事务(transaction)是数据库的特性之一,在mysql数据库管理系统中,事务的管理是由引擎innordb实现的. 数据库事务可以理解为一个阶段中的活动,对于每一个窗口都有一个日志,日志中记录着本次事务中进行的改动(注意只是改动,查询不算).其中的两个指令commit,以…

ffplay设置延时

目录 理论 实验 不带参数 带参数 理论 使用ffplay播放视频源时&#xff0c;rtsp/rtmp等&#xff0c;会有一定的延时&#xff0c;这里我们可以通过设置ffplay播放参数将延时控制到最小。 ffplay.exe -i rtmp://xxxxxxx -fflags nobuffer 减少缓冲 也可以减少分析码流的时间…

chatgpt赋能python:Python就业岗位分析:机遇和挑战并存

Python就业岗位分析&#xff1a;机遇和挑战并存 Python是一种高级编程语言&#xff0c;它具有易于学习、易于阅读和维护的特点。自问世以来&#xff0c;Python一直是最受欢迎的编程语言之一&#xff0c;用于各种领域的开发、数据分析和科学计算等方面&#xff0c;因此Python岗…

【AI工具】 一款多SOTA模型集成的高精度自动标注工具(直接安装使用,附源码)

目录 高精度自动标注工具简介及其特性标注工具的安装开启自动标注 简介 X-AnyLabeling 是一款全新的交互式自动标注工具&#xff0c;其基于AnyLabeling进行构建和二次开发&#xff0c;在此基础上扩展并支持了许多的模型和功能&#xff0c;并借助Segment Anything和YOLO等主流…

Abp Vnext 搭建 ELK日志记录

ELK是三个开源软件的缩写&#xff0c;分别表示&#xff1a;Elasticsearch , Logstash, Kibana 安装 Elasticsearch Kibana 的方法我前面文章有写 Abp中加入Logstash Serilog.AspNetCore Serilog.Formatting.Elasticsearch Serilog.Sinks.Async Serilog.Sinks.Elasticsearchp…