《protobuf》基础语法2

文章目录

枚举类型

protobuf里有枚举类型,定义如下

cpp 复制代码
enum PhoneType
{
	string home_addr = 0;
	string work_addr = 1;
}

message一样,可分为 嵌套定义,文件内定义,文件外定义。不过多演示

但是值得注意的点是

  1. 同级(同层)的枚举类型,各个枚举类型中的常量不能重名,否则编译会报错。如下:
cpp 复制代码
enum A
{
	int32 a = 0;
}
enum B
{
	int32 a = 0;
}

A和B有相同的变量a,所以会报错

  1. 单个 .proto 文件下,最外层枚举类型和嵌套枚举类型,不算同级。如下:
cpp 复制代码
enum A
{
	int32 a = 0;
}
message C
{
	enum B
	{
		int32 a = 0;
	}
}
  1. 多个 .proto 文件下,若一个文件引入了其他⽂件,且每个文件都未声明 package,每个 proto 文
    件中的枚举类型都在最外层,算同级。如下:
cpp 复制代码
// 在 A.proto 文件里
import "B.proto"
enum A
{
	int32 a = 0;   // 报错,B.proto 里以及定义
}
-----------------------
// 在 B.proto 文件里
enum B
{
	int32 a = 0;
}
  1. 多个 .proto 文件下,若一个文件引入了其他文件,且每个文件都声明了 package,不算同级。如下
cpp 复制代码
// 在 A.proto 文件里
import "B.proto"
package A;
enum A
{
	int32 a = 0;
}
-----------------------
// 在 B.proto 文件里
package B;
enum B
{
	int32 a = 0;
}

ANY 类型

介绍一下ANY类型,ANY类型表示任意类型

  • 头文件位置 /usr/local/protobuf/include/google/protobuf/

如何将其引入 .proto 文件里

cpp 复制代码
// 在/usr/local/protobuf/include
import "google/protobuf/any.proto";
// 使用
message Any{
	google.protobuf.Any data = 1;
}
  • 介绍常用函数:
cpp 复制代码
// 已知信息如下
/ contacts.proto文件  //
syntax = "proto3"
package contacts;
import "google/protobuf/any.proto";  // 引入Any类型
message PeopleInfo
{
	// 可以用于任何类型的any
	google.protobuf.Any data = 1;
}

message Address
{
	string home_addr;
}

 test.cc文件 //
#include <iostream>
#include "contacts.pb.h"  // contacts.proto 编译后生成的头文件
using namespace std;

int main()
{
    // 包装
	contact2::PeopleInfo people;
	contact2::Address addr;
	addr.set_home_addr("中国");
	google::protobuf::Any* any = people.mutable_data();  // 开辟一段any对象的空间
    any->PackFrom(addr);  // 将addr打包成Any类型

    // 转换
    contact2::Address addr2;
    if(people.has_data())
    {
        google::protobuf::Any any = people.data();  // 获取people里的Any类对象 
        if(any.Is<contact2::Address>()) // 判断any类型是否为 Address 类
        {
            any.UnpackTo(&addr2);  // 输出型参数,填充add2
            cout << addr2.home_addr() << endl;  // 打印
        }
    }
    
	return 0;
}

编译 .proto 文件,然后编译连接 test.cc 文件,执行结果如下:

执行大致流程

分类讲解一下常用函数:

  • 属于people对象的
    • Any* mutable_data(); ------- // 开辟一段 any 对象的空间
    • Any& data() const; ----------- // 获得 any 对象(即:people里的data)
    • bool has_data() const; ------- // 判断有无对 any 对象赋值
  • 属于 people 里 Any类型的data 对象的
    • bool PackFrom(const Message& message); -------- // 将任意类型转换成 Any 类
    • bool UnpackTo(Message* message) const; -------- // 将 any 对象里的值赋给相对应类型的对象
    • template< class T> bool Is() const; -------- // 判断是否为对应类型

oneof 类型

oneof 类型语法简单:

  • 定义如下
cpp 复制代码
message PeopleInfo
{
	string name = 1;
	oneof gender
	{
		string male = 2;
		string female = 3;
	}
}

但是值得注意的点是

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

看看编译后的 .pb.h 文件里定义的内容

cpp 复制代码
class PeopleInfo final :
    public ::PROTOBUF_NAMESPACE_ID::Message 
{
	enum GenderCase 
	{
	 kMale = 2,
	 kFemale = 3,
	 GENDER_NOT_SET = 0,
	};

  // string male = 2;
  bool has_male() const;
  void clear_male();
  const std::string& male() const;
  template <typename ArgT0 = const std::string&, typename... ArgT>
  void set_male(ArgT0&& arg0, ArgT... args);
  std::string* mutable_male();
  PROTOBUF_NODISCARD std::string* release_male();
  void set_allocated_male(std::string* male);

  // string female = 3;
  bool has_female() const;
  void clear_female();
  const std::string& female() const;
  template <typename ArgT0 = const std::string&, typename... ArgT>
  void set_female(ArgT0&& arg0, ArgT... args);
  std::string* mutable_female();
  PROTOBUF_NODISCARD std::string* release_female();
  void set_allocated_female(std::string* female);

  void clear_gender();
  GenderCase gender_case() const;
}

会将 oneof 中的多个字段定义为⼀个枚举类型。

  • 设置和获取:对 oneof 内的字段进⾏常规的设置和获取即可,但要注意只能设置⼀个。如果设置多个,那么只会保留最后⼀次设置的成员
  • 清空oneof字段:clear_ 方法
  • 获取当前设置了哪个字段:_case 方法
  • 设置值方法: set_
  • 判断该值是否存在方法:has_

map 类型

类似于C++里面的 map 类型,protobuf自己实现了一个类似的数据结构,protobuf 的 map 类型的实现是基于它的 Message 类型的。

  • 定义如下
cpp 复制代码
message PeopleInfo
{
	map<string, string> info = 1;
}

但是值得注意的点是

repeated map< key_type, value_type> map_name = N;

  • key_type 是除了 float 和 byte 的其他任何标量类型。value_type 可以是任意类型
  • map 也不可以被 repeated 修饰
  • map 中存入的元素是无序

讲解一下几个相关常用函数

  • 在PeopleInfo对象里,一般用 mutable_info ( 这里的 info 是对应map类对象 info ) 来开辟一段空间,返回 map 指针来操控info。
    函数原型Map< std::string, std::string >*mutable_info()
  • void clear_info(); 清空对象里的内容。

此外,map还支持迭代器,和C++里的 unorderedmap 十分类似。

改进通讯录实例

运用上述知识点,对上一篇的通讯录代码进行增添功能

contacts.proto

cpp 复制代码
syntax = "proto3";
package contact2;

// 在/usr/local/protobuf/include
import "google/protobuf/any.proto";

// 地址信息
message Address
{
    string home_addr = 1;
    string work_addr = 2;
}
// 个人信息
message PeopleInfo
{
    string name = 1;
    int32 age = 2;
    // 嵌套定义
    message Phone{
        string number = 1;
        // 嵌套枚举
        enum PhoneType{
            MP = 0;  // 移动电话
            TEL = 1; // 固定电话
        }
        PhoneType type = 2;
    }
    // repeated 修饰词 修饰的变量相当于数组
    repeated Phone phone = 3;
    google.protobuf.Any data = 4;
    // 其他联系方式
    oneof other_contacts
    {
        string QQ = 5;
        string Wechat = 6;
    }
    // 备注
    map<string, string> remark = 7;
}

// 通讯录
message Contacts
{
    repeated PeopleInfo contacts = 1;
}

write.cpp

执行结果

read.cpp


相关推荐
半夜修仙10 小时前
延迟队列的介绍及常见问题
java·数据库·中间件·rabbitmq
手握风云-11 小时前
一条消息的旅程:RabbitMQ 学习与实践(一)
中间件·rabbitmq
RH2312111 天前
2026.6.8Linux
java·数据库·中间件
理人综艺好会2 天前
双Token机制在实际项目中的应用与实践
中间件·token
番茄去哪了2 天前
神领物流面试题(一)
java·大数据·中间件
念何架构之路2 天前
消息中间件
中间件
都说名字长不会被发现2 天前
Spring Boot Starter 中间件账号密码加密方案设计与实现
java·spring boot·后端·中间件
瀚高PG实验室3 天前
java中间件无法连接数据库
java·数据库·中间件·瀚高数据库
之歆3 天前
Day11_Express 深入解析:从中间件到项目实战
中间件·express
码农飞哥3 天前
RocketMQ消费接口设计实战:为什么HTTP回调接口必须吞掉所有异常,始终返回成功?
网络协议·http·中间件·消息队列·rocketmq