[C++][ProtoBuf][Proto3语法][三]详细讲解

目录

  • 1.默认值
  • 2.更新消息
    • 1.更新规则
    • 2.保留字段reserved
  • 3.未知字段
    • 1.是什么?
    • 2.未知字段从哪获取
  • 4.前后兼容性
  • 5.选项option
    • 1.选项分类
    • 2.常用选项列举
    • 3.设置自定义选项


1.默认值

  • 反序列化消息时,如果被反序列化的⼆进制序列中不包含某个字段,反序列化对象中相应字段时,就会设置为该字段的默认值
  • 不同的类型对应的默认值不同
    • 字符串:默认值为空字符串
    • 字节:默认为空字节
    • 布尔值:默认值为false
    • 数值类型
      • 整数默认为0
      • 浮点数默认为0.0
    • 枚举:默认值是第一个定义的枚举值,必须为0
    • 消息字段:未设置该字段,它的取值依赖于语言
    • 对于设置了repeated的字段的默认值是空的
      • 通常是相应语言的一个空列表
  • 对于messageoneofany字段,C++和Java中都有has_方法来检测当前字段是否被设置
  • 对于标量数据类型,在proto3语法下,没有生成has_语法

2.更新消息

1.更新规则

  • 如果现有的消息类型已经不再满⾜需求,例如需要扩展⼀个字段,在不破坏任何现有代码的情况下更新消息类型⾮常简单。遵循如下规则即可
  • 新增:不要和老字段冲突即可
  • 修改
    • 禁⽌修改任何已有字段的字段编号
    • int32uint32int64uint64bool是完全兼容的。可以从这些类型中的⼀个改为另⼀个, ⽽不破坏前后兼容性
      • 若解析出来的数值与相应的类型不匹配,会采⽤与C++⼀致的处理⽅案
      • 例如:若将64位整数当做32位进⾏读取,它将被截断为32位
    • sint32sint64相互兼容但不与其他的整型兼容
    • stringbytes在合法UTF-8字节前提下也是兼容的
    • bytes包含消息编码版本的情况下,嵌套消息与bytes也是兼容的
    • fixed32sfixed32兼容,fixed64sfixed64兼容
    • enumint32uint32int64uint64兼容(注意若值不匹配会被截断)
      • 但要注意当反序列化消息时会根据语⾔采⽤不同的处理⽅案
      • 例如
        • 未识别的proto3枚举类型会被保存在消息中
        • 但是当消息反序列化时如何表⽰是依赖于编程语⾔的,整型字段总是会保持其的值
    • oneof
      • 将⼀个单独的值更改为新oneof类型成员之⼀是安全和⼆进制兼容的
      • 若确定现有代码没有⼀次性设置多个值那么将多个字段移⼊⼀个新oneof类型也是可⾏的
      • 将任何字段移⼊已存在的oneof类型是不安全的
  • 删除:若是移除⽼字段,要保证不再使⽤移除字段的字段编号
    • 正确的做法是保留字段编号(reserved),以确保该编号将不能被重复使⽤
    • 不建议直接删除或注释掉字段

2.保留字段reserved

  • 如果通过删除或注释掉字段来更新消息类型,未来的⽤⼾在添加新字段时,有可能会使⽤以前已经存在,但已经被删除或注释掉的字段编号
    • 将来使⽤该.proto的旧版本时的程序会引发很多问题:数据损坏、隐私错误等等
  • 确保不会发⽣这种情况的⼀种⽅法是:使⽤reserved将指定字段的编号或名称设置为保留项
    • 当再使⽤这些编号或名称时,Protocol Buffer的编译器将会警告这些编号或名称不可⽤
  • 示例
    message Message 
    {// 设置保留项reserved 100, 101, 200 to 299;reserved "field3", "field4";// 注意:不要在⼀⾏ reserved 声明中同时声明字段编号和名称// reserved 102, "field5"; // ERROR// 设置保留项之后,下⾯代码会告警int32 field1 = 100; //告警:Field 'field1' uses reserved number 100int32 field2 = 101; //告警:Field 'field2' uses reserved number 101int32 field3 = 102; //告警:Field name 'field3' is reservedint32 field4 = 103; //告警:Field name 'field4' is reserved
    }
    
  • 总结
    • 若是移除⽼字段,要保证不再使⽤移除字段的字段编号,不建议直接删除或注释掉字段
    • 正确的做法是保留字段编号(reserved),以确保该编号将不能被重复使⽤

3.未知字段

1.是什么?

  • 未知字段:解析结构良好的Protocol Buffer已序列化数据中的未识别字段的表⽰⽅式
    • 例如:当旧程序解析带有新字段的数据时,这些新字段就会成为旧程序的未知字段
  • 本来proto3在解析消息时总是会丢弃未知字段,但在3.5版本中重新引⼊了对未知字段的保留制
    • 在3.5或更⾼版本中,未知字段在反序列化时会被保留,同时也会包含在序列化的结果中

2.未知字段从哪获取

  • 类图
    请添加图片描述

  • MessageLite类介绍

    • MessageLite从名字看是轻量级的message,仅仅提供序列化、反序列化功能
    • 类定义在Google提供的message_lite.h
  • Message类介绍

    • 用户⾃定义的message类,都是继承⾃Message
    • Message最重要的两个接⼝GetDescriptor/GetReflection,可以获取该类型对应的Descriptor对象指针和Reflection对象指针
    • 类定义在Google提供的message.h
      //google::protobuf::Message 部分代码展⽰
      const Descriptor* GetDescriptor() const;
      const Reflection* GetReflection() const;
      
  • Descriptor类介绍

    • Descriptor:是对message类型定义的描述,包括message的名字、所有字段的描述、原始的.proto⽂件内容等
    • 类定义在Google提供的descriptor.h
      // 部分代码展⽰
      class PROTOBUF_EXPORT Descriptor : private internal::SymbolBase 
      {string& name () constint field_count() const;const FieldDescriptor* field(int index) const;const FieldDescriptor* FindFieldByNumber(int number) const;const FieldDescriptor* FindFieldByName(const std::string& name) const;const FieldDescriptor* FindFieldByLowercaseName(const std::string& lowercase_name) const;const FieldDescriptor* FindFieldByCamelcaseName(const std::string& camelcase_name) const;int enum_type_count() const;const EnumDescriptor* enum_type(int index) const;const EnumDescriptor* FindEnumTypeByName(const std::string& name) const;const EnumValueDescriptor* FindEnumValueByName(const std::string& name) const;
      }
      
  • Reflection类介绍

    • Reflection接⼝类,主要提供了动态读写消息字段的接⼝,对消息对象的⾃动读写主要通过该类完成
    • 提供⽅法来动态访问/修改message中的字段,对每种类型,Reflection都提供了⼀个单独的接⼝⽤于读写字段对应的值
      • 针对所有不同的field类型FieldDescriptor::TYPE_*,需要使⽤不同的Get*()/Set* ()/Add*()接⼝
      • repeated类型需要使⽤GetRepeated*()/SetRepeated*()接⼝,不可以和⾮repeated类型接⼝混⽤
      • message对象只可以被由它⾃⾝的Reflection(message.GetReflection())来操作
    • 类中还包含了访问/修改未知字段的⽅法
    • 类定义在Google提供的message.h
  • UnknownFieldSet类介绍(重要)

    • UnknownFieldSet包含在分析消息时遇到但未由其类型定义的所有字段
    • 若要将UnknownFieldSet附加到任何消息,请调⽤Reflection::GetUnknownFields()
    • 类定义在unknown_field_set.h
      class PROTOBUF_EXPORT UnknownFieldSet 
      {inline void Clear();void ClearAndFreeMemory();inline bool empty() const;inline int field_count() const;inline const UnknownField& field(int index) const;inline UnknownField* mutable_field(int index);// Adding fields ---------------------------------------------------void AddVarint(int number, uint64_t value);void AddFixed32(int number, uint32_t value);void AddFixed64(int number, uint64_t value);void AddLengthDelimited(int number, const std::string& value);std::string* AddLengthDelimited(int number);UnknownFieldSet* AddGroup(int number);// Parsing helpers -------------------------------------------------// These work exactly like the similarly-named methods of Message.bool MergeFromCodedStream(io::CodedInputStream* input);bool ParseFromCodedStream(io::CodedInputStream* input);bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input);bool ParseFromArray(const void* data, int size);inline bool ParseFromString(const std::string& data) {return ParseFromArray(data.data(), static_cast<int>(data.size()));}// Serialization.bool SerializeToString(std::string* output) const;bool SerializeToCodedStream(io::CodedOutputStream* output) const;static const UnknownFieldSet& default_instance();
      };
      
  • UnknownField类介绍(重要)

    • 表⽰未知字段集中的⼀个字段
    • 类定义在unknown_field_set.h
      class PROTOBUF_EXPORT UnknownField 
      {
      public:enum Type {TYPE_VARINT,TYPE_FIXED32,TYPE_FIXED64,TYPE_LENGTH_DELIMITED,TYPE_GROUP};inline int number() const;inline Type type() const;// Accessors----------------------------------------------------------// Each method works only for UnknownFields of the corresponding type.inline uint64_t varint() const;inline uint32_t fixed32() const;inline uint64_t fixed64() const;inline const std::string& length_delimited() const;inline const UnknownFieldSet& group() const;inline void set_varint(uint64_t value);inline void set_fixed32(uint32_t value);inline void set_fixed64(uint64_t value);inline void set_length_delimited(const std::string& value);inline std::string* mutable_length_delimited();inline UnknownFieldSet* mutable_group();
      };
      

4.前后兼容性

  • Protobuf是具有前后兼容性的,为了叙述⽅便,把增加了新属性的Service称为“新模块”,未做变动的Client称为“⽼模块”
    • 向前兼容⽼模块能够正确识别新模块⽣成或发出的协议
      • 这时新增加的属性会被当作未知字段(3.5版本及之后)
    • 向后兼容新模块也能够正确识别⽼模块⽣成或发出的协议
  • 前后兼容的作⽤:当维护⼀个很庞⼤的分布式系统时,由于你⽆法同时升级所有模块,为了保证 在升级过程中,整个系统能够尽可能不受影响,就需要尽量保证通讯协议的“向后兼容”或“向前兼容”

5.选项option

  • .proto文件中可以声明许多选项,使⽤option标注,选项能影响proto编译器的某些处理⽅式

1.选项分类

  • 选项的完整列表在google/protobuf/descriptor.proto中定义,部分代码:
    syntax = "proto2"; // descriptor.proto 使⽤ proto2 语法版本
    message FileOptions { ... } // ⽂件选项 定义在 FileOptions 消息中
    message MessageOptions { ... } // 消息类型选项 定义在 MessageOptions 消息中
    message FieldOptions { ... } // 消息字段选项 定义在 FieldOptions 消息中
    message OneofOptions { ... } // oneof字段选项 定义在 OneofOptions 消息中
    message EnumOptions { ... } // 枚举类型选项 定义在 EnumOptions 消息中
    message EnumValueOptions { .. } // 枚举值选项 定义在 EnumValueOptions 消息中
    message ServiceOptions { ... } // 服务选项 定义在 ServiceOptions 消息中
    message MethodOptions { ... } // 服务⽅法选项 定义在 MethodOptions 消息中
    ...
    
  • 选项分为⽂件级、消息级、字段级等等, 但并没有⼀种选项能作⽤于所有的类型

2.常用选项列举

  • optimize_for:该选项为⽂件选项,可以设置protoc编译器的优化级别,设置不同的优化级别,编译.proto⽂件后⽣成的代码内容不同
    • SPEED
      • protoc编译器将⽣成的代码是⾼度优化的,代码运⾏效率⾼,但是由此⽣成的代码编译后会占⽤更多的空间
      • SPEED默认选项
    • CODE_SIZE
      • proto编译器将⽣成最少的类,会占⽤更少的空间,是依赖基于反射的代码来 实现序列化、反序列化和各种其他操作
      • SPEED恰恰相反,它的代码运⾏效率较低
      • 这种⽅式适合⽤在包含⼤量的.proto⽂件,但并不盲⽬追求速度的应⽤中
    • LITE_RUNTIME
      • ⽣成的代码执⾏效率⾼,同时⽣成代码编译后的所占⽤的空间也是⾮常 少
      • 这是以牺牲Protocol Buffer提供的反射功能为代价的,仅仅提供encoding+序列化功能
      • 所以在链接PB库时仅需链接libprotobuf-lite,⽽⾮libprotobuf
      • 这种模式通常⽤于资源有限的平台,例如移动⼿机平台中
  • allow_alias
    • 允许将相同的常量值分配给不同的枚举常量,⽤来定义别名
    • 该选项为枚举选项,例⼦
      enum PhoneType 
      {option allow_alias = true;MP = 0;TEL = 1;LANDLINE = 1; // 若不加 option allow_alias = true; 这⼀⾏会编译报错
      }
      

3.设置自定义选项

  • ProtoBuf允许⾃定义选项并使⽤,该功能⼤部分场景⽤不到,在这⾥不拓展讲解
  • 参考资料

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

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

相关文章

解读网络安全公司F5:助企业高效简化多云和应用部署

伴随企业加速数字化转型工作、扩展到新的基础设施环境并采用微服务架构&#xff0c;企业正拥抱混合和多云基础设施所带来的灵活性。Ernst & Young调查数据显示&#xff0c;84%的企业正处于向现有网络安全解决方案套件添加多种新技术的早期阶段。企业同样意识到&#xff0c;…

JavaWeb—js(3)

Bom dom: document object model(文档对象模型), 是处理html、xml的标准编写接口。 节点和元素 整个页面也就是整个文档我们称之为文档节点; 文档节点使用document来表示; 页面中的所有标签我们称之为元素&#xff0c;使用element来表示; 如此处的文本、属性、注释等&…

AirPods Pro新功能前瞻:iOS 18的五大创新亮点

随着科技的不断进步&#xff0c;苹果公司一直在探索如何通过创新提升用户体验。iOS 18的推出&#xff0c;不仅仅是iPhone的一次系统更新&#xff0c;更是苹果生态链中重要一环——AirPods Pro的一次重大升级。 据悉&#xff0c;iOS 18将为AirPods Pro带来五项新功能&#xff0…

Mysql练习题目【7月10日更新】

七、Mysql练习题目 https://zhuanlan.zhihu.com/p/38354000 1. 创建表 创建学生表 mysql> create table if not exists student(-> student_id varchar(255) not null,-> student_name varchar(255) not null,-> birthday date not null,-> gender varchar(…

C++ | Leetcode C++题解之第226题翻转二叉树

题目&#xff1a; 题解&#xff1a; class Solution { public:TreeNode* invertTree(TreeNode* root) {if (root nullptr) {return nullptr;}TreeNode* left invertTree(root->left);TreeNode* right invertTree(root->right);root->left right;root->right …

硬件:CPU和GPU

一、CPU与GPU 二、提升CPU利用率&#xff1a;计组学过的 1、超线程一般是给不一样的任务的计算使用&#xff0c;而非在计算密集型工作中 2、Cpu一次可以计算一个线程&#xff0c;而gpu有多少个绿点一次就能计算多少个线程&#xff0c;Gpu比cpu快是因为gpu它的核多&#xff0c;…

看影视学英语(假如第一季第一集)

in the hour也代表一小时吗&#xff1f;等同于in an hour&#xff1f;

文件操作和IO流

前言&#x1f440;~ 上一章我们介绍了多线程进阶的相关内容&#xff0c;今天来介绍使用java代码对文件的一些操作 文件&#xff08;file&#xff09; 文件路径&#xff08;Path&#xff09; 文件类型 文件操作 文件系统操作&#xff08;File类&#xff09; 文件内容的读…

c++ learn third day

1.津津的储蓄计划 参考&#xff1a;http://t.csdnimg.cn/XI1HV 记得最后加上num&#xff01;&#xff01;&#xff01; #include<stdio.h> int main() {int arr[13]{0};int num0,i0,j;double sum0;for(j1;j<12;j){scanf("%d",&arr[j]);}for(i1;i<…

(图文详解)小程序AppID申请以及在Hbuilderx中运行

今天小编给大家带来了如何去申请APPID&#xff0c;如果你是小程序的开发者&#xff0c;就必须要这个id。 申请步骤 到小程序注册页面&#xff0c;注册一个小程序账号 微信公众平台 填完信息后提交注册 会在邮箱收到 链接激活账号 确认。邮箱打开链接后&#xff0c;会输入实…

谷粒商城学习笔记-22-分布式组件-SpringCloud-OpenFeign测试远程调用

文章目录 一&#xff0c;OpenFeign的简介二&#xff0c;OpenFeign的使用步骤1&#xff0c;场景说明2&#xff0c;引入依赖2&#xff0c;开启OpenFeign3&#xff0c;编写Feign接口4&#xff0c;使用feign调用远程接口5&#xff0c;验证 错误记录 上一节学习了注册中心&#xff0…

Linux——多线程(四)

前言 这是之前基于阻塞队列的生产消费模型中Enqueue的代码 void Enqueue(const T &in) // 生产者用的接口{pthread_mutex_lock(&_mutex);while(IsFull())//判断队列是否已经满了{pthread_cond_wait(&_product_cond, &_mutex); //满的时候就在此情况下等待// 1.…

泛微开发修炼之旅--36通过js控制明细表中同一列中多个浏览框的显示控制逻辑(明细表列中多字段显示逻辑控制)

文章链接&#xff1a;36通过js控制明细表中同一列中多个浏览框的显示控制逻辑&#xff08;明细表列中多字段显示逻辑控制&#xff09;

【基于R语言群体遗传学】-13-群体差异量化-Fst

在前几篇博客中&#xff0c;我们深度学习讨论了适应性进化的问题&#xff0c;从本篇博客开始&#xff0c;我们关注群体差异的问题&#xff0c;建议大家可以先看之前的博客&#xff1a;群体遗传学_tRNA做科研的博客-CSDN博客 一些新名词 Meta-population:An interconnected gro…

2024年06月CCF-GESP编程能力等级认证Python编程四级真题解析

本文收录于专栏《Python等级认证CCF-GESP真题解析》,专栏总目录:点这里,订阅后可阅读专栏内所有文章。 一、单选题(每题 2 分,共 30 分) 第 1 题 小杨父母带他到某培训机构给他报名参加CCF组织的GESP认证考试的第1级,那他可以选择的认证语言有几种?( ) A. 1 B. 2 C…

昇思MindSpore学习笔记6-02计算机视觉--ResNet50迁移学习

摘要&#xff1a; 记录MindSpore AI框架使用ResNet50迁移学习方法对ImageNet狼狗图片分类的过程、步骤。包括环境准备、下载数据集、数据集加载、构建模型、固定特征训练、训练评估和模型预测等。 一、概念 迁移学习的方法 在大数据集上训练得到预训练模型 初始化网络权重参数…

通用个人客户关系管理系统设计

设计一个通用个人客户关系管理系统&#xff08;Personal CRM&#xff09;&#xff0c;旨在帮助个人用户管理他们的社交网络、职业联系人、个人项目和日常沟通&#xff0c;需要关注以下几个核心设计原则和功能模块&#xff1a; 核心设计原则 易用性&#xff1a;界面简洁直观&a…

Hospital Management Startup 1.0 SQL 注入漏洞(CVE-2022-23366)

前言 CVE-2022-23366是一个影响HMS v1.0的SQL注入漏洞。该漏洞存在于patientlogin.php文件中&#xff0c;允许攻击者通过特定的SQL注入来获取或修改数据库中的敏感信息。 具体来说&#xff0c;攻击者可以通过向patientlogin.php发送恶意构造的SQL语句来绕过身份验证&#xff…

Java系列-valitile

背景 volatile这个关键字可以说是面试过程中出现频率最高的一个知识点了&#xff0c;面试官的问题也是五花八门&#xff0c;各种刁钻的角度。之前也是简单背过几道八股文&#xff0c;什么可见性&#xff0c;防止指令重拍等&#xff0c;但面试官一句&#xff1a;volatile原理是什…

Echarts实现github提交记录图

最近改个人博客&#xff0c;看了github的提交记录&#xff0c;是真觉得好看。可以移植到自己的博客上做文章统计 效果如下 代码如下 <!DOCTYPE html> <html lang"en" style"height: 100%"><head><meta charset"utf-8"> …