当前位置: 首页 > news >正文

【ProtoBuf 语法详解】oneof 类型

文章目录

  • 1. oneof 类型
  • 2. 升级通讯录至 2.3 版本
    • 2.1 更新 contacts.proto 文件
    • 2.2 更新 write.cc 文件
    • 2.3 更新 read.cc 文件
    • 2.4 编译运行

1. oneof 类型

如果消息中有很多可选字段,并且将来同时只有一个字段会被设置,那么就可以使用oneof加强这个行为,也能有节约内存的效果。

2. 升级通讯录至 2.3 版本

通讯录 2.3 版本想新增联系人的其他联系方式,比如 qq 或者 wechat 二选一,我们就可以使用 oneof 字段来加强多选一这个行为。

oneof 字段定义的格式为:

oneof 字段名 { 字段1; 字段2; ... }

2.1 更新 contacts.proto 文件

代码如下:

syntax = "proto3"; package contacts; // package 是一个可选的声明符, 声明其命名空间 // 引入 any.proto 文件 import "google/protobuf/any.proto"; // 地址 message Address { string home_address = 1; // 家庭地址 string unit_address = 2; // 单位地址 } // 定义联系人message message PeopleInfo { string name = 1; // 姓名 int32 age = 2; // 年龄 message Phone { string number = 1; // 电话号码 enum PhoneType { MP = 0; // 移动电话 TEL = 1; // 固定电话 } PhoneType type = 2; } repeated Phone phone = 3; // 电话 google.protobuf.Any data = 4; // 地址 // 社交联系方式 (多选一) oneof other_contact { string qq = 5; string wechat = 6; } } // 通讯录:修改消息名为ContactBook(大驼峰,避免和字段名contacts冲突) message ContactBook { repeated PeopleInfo contacts = 1; }

更新内容如下:

注意:

  • 可选字段中的字段编号,不能与非可选字段的编号冲突。
  • 不能在 oneof 中使用 repeated 字段。
  • 将来在设置 oneof 字段中值时,如果将 oneof 中的字段设置多个,那么只会保留最后一次设置的成员,之前设置的 oneof 成员会自动清除。

然后更新代码:

protoc--cpp_out=. contacts.proto

在 contacts.pb.h 更新的代码中,对于 oneof 字段:

  • 会将 oneof 中的多个字段定义为一个枚举类型。
  • 设置和获取:对 oneof 内的字段进行常规的设置和获取即可,但要注意只能设置一个。如果设置多个,那么只会保留最后一次设置的成员。
  • 清空 oneof 字段:clear_方法
  • 获取当前设置了哪个字段:_case方法

2.2 更新 write.cc 文件

代码如下:

#include<iostream>#include<fstream>#include"contacts.pb.h"usingnamespacestd;usingnamespacecontacts;// 把命名空间展开// --- 强行补齐缺失的 Arena 模板实现 ---#include<google/protobuf/any.pb.h>namespacegoogle{namespaceprotobuf{template<>Any*Arena::CreateMaybeMessage<Any>(Arena*arena){returnArena::CreateMessageInternal<Any>(arena);}}}// ----------------------------------// 新增联系人voidAddPeopleInfo(PeopleInfo*people){cout<<"-------------新增联系人-------------"<<endl;cout<<"请输入联系人姓名: ";string name;getline(cin,name);people->set_name(name);cout<<"请输入联系人年龄: ";intage;cin>>age;people->set_age(age);cin.ignore(256,'\n');// 清除回车for(inti=0;;i++){cout<<"请输入联系人电话"<<i+1<<" (输入回车即可完成电话新增): ";string number;getline(cin,number);if(number.empty()){break;}PeopleInfo_Phone*phone=people->add_phone();phone->set_number(number);cout<<"请输入该电话类型 (1、移动电话 2、固定电话): ";inttype;cin>>type;cin.ignore(256,'\n');// 清除回车switch(type){case1:phone->set_type(PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_MP);// 设置移动电话break;case2:phone->set_type(PeopleInfo_Phone_PhoneType::PeopleInfo_Phone_PhoneType_TEL);// 设置固定电话break;default:cout<<"选择有误! "<<endl;break;}}Address address;// 定义一个地址对象cout<<"请输入联系人家庭地址: ";string homeAddress;getline(cin,homeAddress);address.set_home_address(homeAddress);cout<<"请输入联系人单位地址: ";string unitAddress;getline(cin,unitAddress);address.set_unit_address(unitAddress);// 把Address类型的对象转化为Any类型people->mutable_data()->PackFrom(address);cout<<"选择添加一个其他联系方式 (1、qq号 2、微信号): ";intotherContact;cin>>otherContact;cin.ignore(256,'\n');if(1==otherContact){cout<<"请输入qq号: ";string qq;getline(cin,qq);people->set_qq(qq);}elseif(2==otherContact){cout<<"请输入微信号: ";string wechat;getline(cin,wechat);people->set_wechat(wechat);}else{cout<<"非法选择, 该项设置失败! "<<endl;}cout<<"-----------添加联系人成功-----------"<<endl;}intmain(){GOOGLE_PROTOBUF_VERIFY_VERSION;ContactBook contacts;// 读取本地已存在的通讯录文件fstreaminput("contacts.bin",ios::in|ios::binary);if(!input){cout<<"contacts.bin not find, create new file!"<<endl;}elseif(!contacts.ParseFromIstream(&input)){cerr<<"parse error!"<<endl;input.close();return-1;}// 向通讯录中添加一个联系人AddPeopleInfo(contacts.add_contacts());// 将通讯录写入本地文件中fstreamoutput("contacts.bin",ios::out|ios::trunc|ios::binary);if(!contacts.SerializeToOstream(&output)){cerr<<"write error!"<<endl;input.close();output.close();return-1;}cout<<"write success!"<<endl;input.close();output.close();google::protobuf::ShutdownProtobufLibrary();return0;}

更新内容如下:

2.3 更新 read.cc 文件

代码如下:

#include<iostream>#include<fstream>#include"contacts.pb.h"usingnamespacestd;usingnamespacecontacts;// 把命名空间展开// --- 强行补齐缺失的 Arena 模板实现 ---#include<google/protobuf/any.pb.h>namespacegoogle{namespaceprotobuf{template<>Any*Arena::CreateMaybeMessage<Any>(Arena*arena){returnArena::CreateMessageInternal<Any>(arena);}}}// ----------------------------------// 打印联系人列表voidPrintContacts(ContactBook&contacts){for(inti=0;i<contacts.contacts_size();i++){cout<<"---------------联系人"<<i+1<<"---------------"<<endl;constPeopleInfo&people=contacts.contacts(i);cout<<"联系人姓名: "<<people.name()<<endl;cout<<"联系人年龄: "<<people.age()<<endl;for(intj=0;j<people.phone_size();j++){// 打印联系人电话constPeopleInfo_Phone&phone=people.phone(j);cout<<"联系人电话"<<j+1<<": "<<phone.number();// 这里不需要换行符// 打印联系人电话类型cout<<" ("<<phone.PhoneType_Name(phone.type())<<")"<<endl;}// 如果data有数据 并且 data的数据类型是Address, 那么就打印联系人地址if(people.has_data()&&people.data().Is<Address>()){Address address;people.data().UnpackTo(&address);if(!address.home_address().empty()){cout<<"联系人家庭地址: "<<address.home_address()<<endl;}if(!address.unit_address().empty()){cout<<"联系人单位地址: "<<address.unit_address()<<endl;}}// 打印qq号或者微信号switch(people.other_contact_case()){casePeopleInfo::OtherContactCase::kQq:cout<<"qq号: "<<people.qq()<<endl;break;casePeopleInfo::OtherContactCase::kWechat:cout<<"微信号: "<<people.wechat()<<endl;break;default:break;}}}intmain(){GOOGLE_PROTOBUF_VERIFY_VERSION;ContactBook contacts;// 读取本地已存在的通讯录文件(以二进制方式读取)fstreaminput("contacts.bin",ios::in|ios::binary);if(!contacts.ParseFromIstream(&input)){cerr<<"parse error!"<<endl;input.close();return-1;}// 打印通讯录列表PrintContacts(contacts);google::protobuf::ShutdownProtobufLibrary();return0;}

更新内容如下:

2.4 编译运行

make 编译以后,新增一个联系人。

以上就是 oneof 语法的详细用法。

http://www.jsqmd.com/news/493381/

相关文章:

  • 春节AI热潮后,网民真的“上车”了吗?
  • Debian 9.x 安装 Proxmox VE 保姆级教程(含NAT端口转发避坑指南)
  • 5步搞定!用FUTURE POLICE为爬取的播客/访谈录音添加毫秒级精准字幕
  • win10/11爆满的元凶!!!清空了140多GB
  • 【MCP 2026AI推理集成终极指南】:20年架构师亲授3大避坑红线、5步零故障上线法与实时吞吐提升217%的实测参数
  • HY-MT1.5-1.8B翻译模型性能优化:提升推理速度与降低显存占用
  • 永磁同步电机控制资料详解:涵盖参考论文、公式推导、模型构建及电机控制书籍等内容,CSDN沉沙分享
  • Qwen-Image-Lightning应用场景:快速为社交媒体生成8K高清配图
  • APM通过mission planner地面站摇杆指令给飞控
  • LeetCode-44 回溯解法
  • 【实战】ESP32 + LN298N 驱动编码器推杆:从零搭建位置闭环控制系统
  • 如何在3分钟内通过手机号找回QQ账号:终极快速解决方案
  • 力扣算法刷题 Day 14
  • 3大突破!图像矢量化技术如何解决中小企业设计资源优化难题
  • 抖音批量监控千名博主视频更新,实时下载技术解析
  • Python默认参数详解
  • VS Code 聊天功能深度解析:从激活到精通,解锁AI编程新范式
  • 从保护环设计到势垒高度设置:Silvaco仿真肖特基二极管的3个关键陷阱
  • Task2:ESP32代码学习和基础API需求
  • CLIP-GmP-ViT-L-14在嵌入式设备端的轻量化部署探索
  • 如何用Python实现三角函数公式的自动计算与验证
  • CTF流量分析新选择:3个核心功能让你轻松应对网络安全挑战
  • 从零开始:tModLoader全面指南 - 打造专属泰拉瑞亚模组世界
  • 原本该有一篇文章发出来
  • 从零学 Linux:从发行版到包管理器,一篇吃透基础要点
  • SiameseAOE中文-base参数详解:Prompt+Text构建思路与schema定义规范
  • SecGPT-14B开源模型落地:适配国产化GPU环境的网络安全垂直大模型实践
  • STM32F4实战:CoreMark跑分从移植到优化的完整指南(附常见问题排查)
  • 如何3分钟实现抖音视频批量下载:douyin-downloader完整指南
  • cmux多智能体管理工具