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

news2024/12/28 6:12:22

目录

  • 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 100
        int32 field2 = 101; //告警:Field 'field2' uses reserved number 101
        int32 field3 = 102; //告警:Field name 'field3' is reserved
        int32 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 () const
          int 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.coloradmin.cn/o/1912744.html

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

相关文章

elasticsearch集群模式部署

系统版本&#xff1a;CentOS Linux release 7.9.2009 (Core) es版本&#xff1a; elasticsearch-7.6.2 本次搭建es集群为三个节点 添加启动用户 确保elasticsearch的启动用户为普通用户&#xff0c;这里我创建了es用户用于启动elasticsearch 执行命令为es用户添加sudo权限 v…

数学建模及国赛

认识数学建模及国赛 认识数学建模 环境类&#xff1a;预测一下明天的气温 实证类&#xff1a; 评价一下政策的优缺点 农业类&#xff1a; 预测一下小麦的产量 财经类&#xff1a; 分析一下理财产品的最优组合 规划类&#xff1a; 土地利用情况进行 合理的划分 力学类&#xf…

如何在 CentOS 中配置 Linux 命名空间(ip netns)

引言 Linux 命名空间是一项强大的技术&#xff0c;允许在同一系统上创建多个独立的虚拟化实例&#xff0c;每个实例可以拥有自己的网络栈、路由表、IP 地址等网络资源&#xff0c;实现资源的隔离和管理。本文将深入探讨如何在 CentOS 中配置和使用 ip netns 命名空间&#xff0…

网络安全合规建设

网络安全合规建设 一、法律安全需求基本合规&#xff08;1&#xff09;《网络安全法》重要节点等级保护政策核心变化 二、安全需求 业务刚需&#xff08;1&#xff09;内忧&#xff08;2&#xff09;外患 三、解决方法&#xff08;1&#xff09;总安全战略目标图&#xff08;2&…

PaddleVideo:Squeeze Time算法移植

参考PaddleVideo/docs/zh-CN/contribute/add_new_algorithm.md at develop PaddlePaddle/PaddleVideo GitHubAwesome video understanding toolkits based on PaddlePaddle. It supports video data annotation tools, lightweight RGB and skeleton based action recognitio…

Xilinx FPGA UltraScale SelectIO 接口逻辑资源

目录 1. 简介 2. Bank Overview 2.1 Diagram 2.2 IOB 2.3 Slice 2.4 Byte Group 2.5 I/O bank 示例 2.6 Pin Definition 2.7 数字控制阻抗(DCI) 2.8 SelectIO 管脚供电电压 2.8.1 VCCO 2.8.2 VREF 2.8.3 VCCAUX 2.8.4 VCCAUX_IO 2.8.5 VCCINT_IO 3. 总结 1. 简介…

基于信号量的生产者消费者模型

文章目录 信号量认识概念基于线程分析信号量信号量操作 循环队列下的生产者消费者模型理论认识代码部分 信号量 认识概念 信号量本质: 计数器 它也叫做公共资源 为了线程之间,进程间通信------>多个执行流看到的同一份资源---->多个资源都会并发访问这个资源(此时易出现…

【Qt课设】基于Qt实现的中国象棋

一、摘 要 本报告讨论了中国象棋程序设计的关键技术和方法。首先介绍了中国象棋的棋盘制作&#xff0c;利用Qt中的一些绘画类的函数来进行绘制。在创作中国象棋棋子方面&#xff0c;首先&#xff0c;我们先定义一下棋子类&#xff0c;将棋子中相同的部分进行打包&#xff0c;使…

Python:安装/Mac

之前一直陆陆续续有学python&#xff01;今天开始&#xff01;正式开肝&#xff01;&#xff01;&#xff01; 进入网站&#xff1a;可能会有点慢&#xff0c;多开几个网页 https://www.python.org 点击下载&#xff0c;然后进入新的页面&#xff0c;往下滑 来到File&#xff0…

PHP验证日本免费电话号码格式

首先&#xff0c;您需要了解免费电话号码的格式。 日本免费电话也就那么几个号段&#xff1a;0120、0990、0180、0570、0800等开头的&#xff0c;0800稍微特殊点&#xff0c;在手机号里面有080 开头&#xff0c;但是后面不一样了。 关于免费电话号码的划分&#xff0c;全部写…

忘记Apple ID密码怎么退出苹果ID账号?

忘记Apple ID密码怎么退出账号&#xff1f;Apple ID对每个苹果用户来说都是必不可少的&#xff0c;没有它&#xff0c;用户就不能享受iCloud、App Store、iTunes等服务。苹果手机软件下载、丢失解锁、恢复出厂设置等都需要使用Apple ID。如果忘记Apple ID 密码&#xff0c;这会…

Linux——多线程(五)

1.线程池 1.1初期框架 thread.hpp #include<iostream> #include <string> #include <unistd.h> #include <functional> #include <pthread.h>namespace ThreadModule {using func_t std::function<void()>;class Thread{public:void E…

九、Linux二进制安装ElasticSearch集群

目录 九、Linux二进制安装ElasticSearch集群1 下载2 安装前准备(单机&#xff0c;集群每台机器都需要配置)3 ElasticSearch单机&#xff08;7.16.2&#xff09;4 ElasticSearch集群&#xff08;8.14.2&#xff09;4.1 解压文件&#xff08;先将下载文件放到/opt下&#xff09;4…

Java系列-valitile

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

Vue基础--v-model/v-for/事件属性/侦听器

目录 一 v-model表单元素 1.1 v-model绑定文本域的value 1.1.1 lazy属性&#xff1a;光标离开再发请求 1.1.2 number属性&#xff1a;如果能转成number就会转成numer类型 1.1.3 trim属性&#xff1a;去文本域输入的前后空格 1.2v-model绑定单选checkbox 1.3代码展示 二 …

Python OpenCV 教学取得视频资讯

这篇教学会介绍使用OpenCV&#xff0c;取得影像的长宽尺寸、以及读取影像中某些像素的颜色数值。 因为程式中的OpenCV 会需要使用镜头或GPU&#xff0c;所以请使用本机环境( 参考&#xff1a;使用Python 虚拟环境) 或使用Anaconda Jupyter 进行实作( 参考&#xff1a;使用Anaco…

基于单片机的温湿度感应智能晾衣杆系统设计

&#xff3b;摘 要&#xff3d; 本设计拟开发一种湿度感应智能晾衣杆系统 &#xff0c; 此新型晾衣杆是以单片机为主控芯片 来控制的实时检测系统 &#xff0e; 该系统使用 DHT11 温湿度传感器来检测大气的温湿度 &#xff0c; 然后通过单 片机处理信息来控制 28BYJ &…

配置路由器支持Telnet操作 计网实验

实验要求&#xff1a; 假设某学校的网络管理员第一次在设备机房对路由器进行了初次配置后&#xff0c;他希望以后在办公室或出差时也可以对设备进行远程管理&#xff0c;现要在路由器上做适当配置&#xff0c;使他可以实现这一愿望。 本实验以一台R2624路由器为例&#xff0c;…

使用 Hugging Face 的 Transformers 库加载预训练模型遇到的问题

题意&#xff1a; Size mismatch for embed_out.weight: copying a param with shape torch.Size([0]) from checkpoint - Huggingface PyTorch 这个错误信息 "Size mismatch for embed_out.weight: copying a param with shape torch.Size([0]) from checkpoint - Hugg…

Redis管理禁用命令

在redis数据量比较大时&#xff0c;执行 keys * &#xff0c;fluashdb 这些命令&#xff0c;会导致redis长时间阻塞&#xff0c;大量请求被阻塞&#xff0c;cpu飙升&#xff0c;严重可能导致redis宕机&#xff0c;数据库雪崩。所以一些命令在生产环境禁止使用。 Redis 禁用命令…