[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://www.mzph.cn/bicheng/43994.shtml

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

相关文章

基于Spring Boot的旅游信息推荐信息系统设计与实现(源码+lw+部署+讲解)

技术指标 开发语言&#xff1a;Java 框架&#xff1a;Spring BootJSP JDK版本&#xff1a;JDK1.8 数据库&#xff1a;MySQL5.7 数据库工具&#xff1a;Navicat16 开发软件&#xff1a;IDEA Maven包&#xff1a;Maven3.6.3 浏览器&#xff1a;IE浏览器 功能描述 旅游信…

Hadoop-19 Flume Agent批量采集数据到HDFS集群 监听Hive的日志 操作则把记录写入到HDFS 方便后续分析

章节内容 上一节我们完成了内容&#xff1a; Flume 启动测试Flume Conf编写Flume 测试发送和接收数据 背景介绍 这里是三台公网云服务器&#xff0c;每台 2C4G&#xff0c;搭建一个Hadoop的学习环境&#xff0c;供我学习。 之前已经在 VM 虚拟机上搭建过一次&#xff0c;但…

深入探索大语言模型

深入探索大语言模型 引言 大语言模型&#xff08;LLM&#xff09;是现代人工智能领域中最为重要的突破之一。这些模型在自然语言处理&#xff08;NLP&#xff09;任务中展示了惊人的能力&#xff0c;从文本生成到问答系统&#xff0c;无所不包。本文将从多个角度全面介绍大语…

AGE agtype 简介

AGE 使用一种名为 agtype 的自定义数据类型&#xff0c;这是 AGE 返回的唯一数据类型。agtype 是 Json 的超集&#xff0c;也是 JsonB 的自定义实现。 简单数据类型 Null 在Cypher中&#xff0c;null用于表示缺失或未定义的值。概念上&#xff0c;null表示“缺失的未知值”&…

路径规划 | 基于蚁群算法的三维无人机航迹规划(Matlab)

目录 效果一览基本介绍程序设计参考文献 效果一览 基本介绍 基于蚁群算法的三维无人机航迹规划&#xff08;Matlab&#xff09;。 蚁群算法&#xff08;Ant Colony Optimization&#xff0c;ACO&#xff09;是一种模拟蚂蚁觅食行为的启发式算法。该算法通过模拟蚂蚁在寻找食物时…

【安全设备】Web应用防火墙

一、什么是Web应用防火墙 Web应用程序防火墙&#xff08;Web Application Firewall&#xff09;的缩写是WAF&#xff0c;用于保护Web应用程序免受各种恶意攻击和漏洞利用。WAF通过监控和过滤进出Web应用程序的HTTP/HTTPS流量来工作。它位于Web应用程序和用户之间&#xff0c;分…

【总线】AXI第九课时:介绍AXI响应信号 (Response Signaling):RRESP和 BRESP

大家好,欢迎来到今天的总线学习时间!如果你对电子设计、特别是FPGA和SoC设计感兴趣&#xff0c;那你绝对不能错过我们今天的主角——AXI4总线。作为ARM公司AMBA总线家族中的佼佼者&#xff0c;AXI4以其高性能和高度可扩展性&#xff0c;成为了现代电子系统中不可或缺的通信桥梁…

STM32F103RB多通道ADC转换功能实现(DMA)

目录 概述 1 硬件 1.1 硬件实物介绍 1.2 nucleo-f103rb 1.3 软件版本 2 软件实现 2.1 STM32Cube配置参数 2.2 项目代码 3 功能代码实现 3.1 ADC功能函数 3.2 函数调用 4 测试 4.1 DMA配置data width&#xff1a;byte 4.2 DMA配置data width&#xff1a;Half wor…

java如何实现一个死锁 ?

死锁(Deadlock)是指在并发系统中,两个或多个线程(或进程)因争夺资源而互相等待,导致它们都无法继续执行的一种状态。 一、简易代码 public class DeadlockExample {private static final Object lock1 = new Object();private

如何在 ASP.NET MVC 项目中使用身份验证器应用程序实现多因素身份验证?

介绍 增强安全性对于任何应用程序都至关重要&#xff0c;而多因素身份验证 (MFA) 是实现此目标的有效方法。在本文中&#xff0c;我们将介绍在 ASP.NET MVC 项目中使用身份验证器应用程序集成 MFA 的过程。无论您是从头开始还是将 MFA 添加到现有项目&#xff0c;本指南都将提…

生物素标记降钙素Biotin-α-CGRP, rat 中间体

生物素标记降钙素Biotin-α-CGRP, rat 中间体是一种特定的生物化学试剂&#xff0c;主要用于科学研究领域。以下是对该产品的详细介绍&#xff1a; 一、基本信息 产品名称&#xff1a;生物素标记降钙素Biotin-α-CGRP, rat 中间体 英文名称&#xff1a;Biotin-α-CGRP, rat 纯度…

Qt 线程同步机制 互斥锁 信号量 条件变量 读写锁

qt线程同步 Qt提供了丰富的线程同步机制来帮助开发者更高效和安全地进行多线程编程。其主要包括: QMutex:为共享数据提供互斥访问能力,避免同时写入导致的数据冲突。利用lock()/unlock()方法实现锁定和解锁。 QReadWriteLock:读写锁,允许多个读线程同时访问,但写操作需要独占…

springboot社区物资交易互助平台+lw+源码+调试+讲解

第3章 系统分析 用户的需求以及与本系统相似的在市场上存在的其它系统可以作为系统分析中参考的资料&#xff0c;分析人员可以根据这些信息确定出本系统具备的功能&#xff0c;分析出本系统具备的性能等内容。 3.1可行性分析 尽管系统是根据用户的要求进行制作&#xff0c;但…

windows USB 设备驱动开发-USB带宽

本文讨论如何仔细管理 USB 带宽的指导。 每个 USB 客户端驱动程序都有责任最大程度地减少其使用的 USB 带宽&#xff0c;并尽快将未使用的带宽返回到可用带宽池。 在这里&#xff0c;我们认为USB 2.0 的速度是480Mbps、12Mbps、1.5Mbps&#xff0c;这分别对应高速、全速、低速…

Python面试宝典第9题:买卖股票

题目 给定一个整型数组&#xff0c;它的第i个元素是一支给定股票第i天的价格。如果最多只允许完成一笔交易&#xff08;即买入和卖出一支股票一次&#xff09;&#xff0c;设计一个算法来计算你所能获取的最大利润。注意&#xff1a;你不能在买入股票前卖出股票。 示例 1&#…

LabVIEW平台从离散光子到连续光子的光子计数技术

光子计数技术用于将输入光子数转换为离散脉冲。常见的光子计数器假设光子是离散到达的&#xff0c;记录到来的每一个光子。但是&#xff0c;当两个或多个光子同时到达时&#xff0c;计数器会将其记录为单个脉冲&#xff0c;从而只计数一次。当连续光子到达时&#xff0c;离散光…

python学习-容器类型

列表 列表&#xff08;list&#xff09;是一种有序容器&#xff0c;可以向其中添加或删除任意元素. 列表数据类型是一种容器类型&#xff0c;列表中可以存放不同数据类型的值,代码示例如下&#xff1a; 列表中可以实现元素的增、删、改、查。 示例代码如下&#xff1a; 增 …

基于Unity3D的Rokid AR Glass项目开发环境搭建

初识Rokid AR 一、SDK简介二、准备工作1.软件环境2.硬件环境 三、快速接入SDK1.配置Package Manager2.安装UXR2.0 SDK 四、导入官方Demo进行模拟器测试五、Rokid AR系列教程 一、SDK简介 UXR2.0 SDK是Rokid为Unity开发者提供的AR开发工具包&#xff0c;提供空间定位跟踪、双目…

Windows 网络重置及重置网络可能出现的问题( WIFI 没有了 / WLAN 图标消失)

netsh int ip reset 命令是用于重置 Windows 操作系统中的网络设置和配置的命令。 在网络故障排除、修复网络连接问题以及清除可能存在的网络配置冲突时非常有用。 命令详解&#xff1a; netsh: 用于配置各种网络设置 int: 用于管理网络接口 ip: 用于管理网络接口的 IP 配…

学习嵌入式对于学历有要求吗?

学习嵌入式系统开发通常并不对学历有严格的要求&#xff0c;尤其是在技术行业中&#xff0c;实际的技能和经验往往比学历更为重要。我收集归类了一份嵌入式学习包&#xff0c;对于新手而言简直不要太棒&#xff0c;里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕…