pb生成文件和反射

news/2024/10/23 15:08:30/

1.protoc生成文件

指定生成的目录和proto文件路径,

protoc --cpp_out=./ ./echo.proto
// echo.proto
syntax = "proto3";package echo;option cc_generic_services = true;message EchoRequest {string msg = 1;
}message EchoResponse {string msg = 2;
}service EchoService {rpc Echo(EchoRequest) returns (EchoResponse);
}

使用 protoc 命令编译 .proto 文件时,会生成两个主要的 C++ 文件:

  1. echo.pb.h(头文件)
  2. echo.pb.cc(源文件)

1.1 pb头文件

echo.pb.h 是 Protocol Buffers 自动生成的头文件,主要用于声明消息(message类型)的类、方法、枚举以及相关的类型信息,还有服务类(Service类)。它的内容包含以下几个部分:

  • 类声明:为每个 .proto 文件中的消息定义一个对应的 C++ 类。例如,如果你在 .proto 文件中定义了一个 Message 类型,头文件中会生成一个 Message 类。

  • 字段的访问器和修改器方法:每个消息类中的字段都会有 getter 和 setter 函数。这些函数用于访问和修改 Protocol Buffers 消息中的字段。例如,set_field()field()clear_field() 等函数。

  • // 序列化和反序列化函数:包含用于将消息对象序列化为二进制格式或从二进制格式反序列化的函数。常见的方法包括 SerializeToString()ParseFromString()。// 这个实际不在生成的文件中。

  • 其他辅助函数:如比较函数(operator==),清理函数(Clear()),以及元数据函数(例如 descriptor())。

服务类类型:

服务类 EchoService 是一个抽象类,声明了服务器端和客户端接口,具体如下:

  • 服务端接口:EchoService::Echo() 是一个虚函数,必须在服务器端的实现类中进行具体的定义。服务器端代码需要继承这个类并实现其中的 Echo() 方法。
class EchoService : public ::grpc::Service {public:virtual ~EchoService();// Abstract method to be implemented on the server sidevirtual ::grpc::Status Echo(::grpc::ServerContext* context,const EchoRequest* request,EchoResponse* response) = 0;// Method to register the service with a gRPC server::grpc::ServerUnaryCallHandlerFactoryBase<EchoRequest, EchoResponse>* RegisterEcho();// Other methods for service registration and reflection
};

服务器端实现:服务器端需要继承 EchoService 类并实现 Echo() 方法。例如:

class MyEchoServiceImpl : public EchoService {::grpc::Status Echo(::grpc::ServerContext* context, const EchoRequest* request, EchoResponse* response) override {// Implement your logic hereresponse->set_msg("Response to: " + request->msg());return ::grpc::Status::OK;}
};
  • 客户端接口Stub 是客户端调用远程 RPC 服务的接口,声明如下。客户端代码通过 Stub 调用 Echo() 方法进行远程调用,发送请求并接收响应。
class EchoService::Stub : public ::grpc::Client {public:// Method to invoke the RPC from the client::grpc::Status Echo(::grpc::ClientContext* context, const EchoRequest& request, EchoResponse* response);
};

1.2 pb源文件

echo.pb.cc 是 Protocol Buffers 自动生成的源文件,主要包含消息类方法的实现。它是 echo.pb.h 中声明的方法的具体定义。源文件的内容主要包括:

  • 构造函数和析构函数:消息类的构造函数、析构函数,以及复制构造函数等。

  • 字段访问器和修改器的实现:具体实现 pb.h 文件中声明的 getter 和 setter 函数,用于访问和修改pb 消息中的字段。

  • 序列化和反序列化的实现:具体实现序列化和反序列化的方法,例如 SerializeToArray()ParseFromArray() 等。

  • 字段的内存管理:管理动态分配的内存(如果消息包含嵌套消息或 repeated 字段)。

  • 消息的元数据:通常包含与消息相关的反射信息,用于支持 Protocol Buffers 的动态功能(如反射系统,descriptor 元数据等)。

2.pb反射机制

Protocol Buffers 的反射机制允许你在运行时动态地操作消息对象,而无需在编译时明确知道其类型或字段。// 可以认为能获取到元数据的机制就是反射机制。

反射使得开发者可以在运行时通过一种通用的方式来操纵 Protocol Buffers 消息,包括:

  1. 获取消息的元数据(如字段的名称、类型等)。
  2. 动态访问和修改消息的字段,即使这些字段在编译时是未知的。
  3. 遍历所有的字段,包括已设置的和未设置的字段。
  4. 动态序列化和反序列化消息,无需依赖具体的消息类型。

反射机制特别适合以下场景:

  • 通用工具或框架:如调试器、序列化框架、消息检查工具等,使用反射可以处理多种不同的消息类型。
  • 不确定消息类型的情况下:如当消息的类型在运行时才能确定时(例如在 RPC 系统中使用动态消息分发)。
const google::protobuf::Descriptor* descriptor = echo::EchoRequest::descriptor();
std::cout << descriptor->name();  // 打印消息名称
  • 在这个例子中,descriptor() 方法返回的是 EchoRequest 消息的描述符,通过它可以获取消息的名称、字段的数量等信息。
echo::EchoRequest req;
req.set_msg("Hello, World!");// 返回反射对象
const google::protobuf::Reflection* reflection = req.GetReflection();
// 通过字段名称获取 FieldDescriptor
const google::protobuf::FieldDescriptor* field = req.descriptor()->FindFieldByName("msg");// 通过反射获取字段的值
std::string value = reflection->GetString(req, field);
std::cout << "msg field value: " << value << std::endl;

反射对象通过描述符与消息的实际实例交互。通过反射对象,你可以在运行时获取和设置消息字段的值,而不需要直接访问消息对象的成员函数

其他常用的反射操作:

// 获取字段值
int32_t id_value = reflection->GetInt32(message, field_descriptor);// 设置字段值
reflection->SetString(&message, field_descriptor, "New Value");// 检查字段是否已设置
if (reflection->HasField(message, field_descriptor)) {// 字段已设置
}// 清除字段
reflection->ClearField(&message, field_descriptor);

// 看起来还是要知道字段名和类型。

2.1 动态消息遍历

const google::protobuf::Descriptor* descriptor = req.descriptor();
const google::protobuf::Reflection* reflection = req.GetReflection();for (int i = 0; i < descriptor->field_count(); ++i) {const google::protobuf::FieldDescriptor* field = descriptor->field(i);if (field->type() == google::protobuf::FieldDescriptor::TYPE_STRING) {std::string value = reflection->GetString(req, field);std::cout << field->name() << ": " << value << std::endl;}
}

使用 field_count() 获取消息的字段总数,然后遍历每个字段,检查字段类型并获取其值。

// 但感觉这里有点怪怪的,reflection指针是从req返回的,但是reflection调用自己成员函数的时候还要把req传进去。


http://www.ppmy.cn/news/1541363.html

相关文章

Android 15 推出新安全功能以保护敏感数据

Android 15 带来了增强的安全功能&#xff0c;可保护您的敏感健康、财务和个人数据免遭盗窃和欺诈。 它还为大屏幕设备带来了生产力改进&#xff0c;并对相机、消息和密钥等应用进行了更新。 Android 防盗保护 Google 开发并严格测试了一套全面的功能&#xff0c;以在盗窃之…

C++ —— map系列的使用

目录 1. map和multimap参考文档 2. map类的介绍 3. pair 4. map的增删查 4.1 插入 4.2 删除 4.3 查找 5. map的数据修改 6. map的operator[] 7. multimap和map的差异 1. map和multimap参考文档 - C Referencehttps://legacy.cplusplus.com/reference/map/ 2. map类的…

Spring的底层原理

文章目录 1. Bean的生命周期2. 推断构造方法3. 依赖注入4. 初始化前5. 初始化6. 初始化后7. AOP8. Spring事务9. Spring事务失效10. Configuration11. 循环依赖12. Lazy 1. Bean的生命周期 UserService.class --> 推断构造方法 --> 普通对象 --> 依赖注入 --> 初始…

nosql课本习题

nosql题目 1. 文档数据库相比其他 NoSQL 的突出优势和特点是什么&#xff1f; 答案&#xff1a; 文档数据库的突出优势在于它的灵活性和可扩展性。不同于传统的关系型数据库&#xff0c;文档数据库允许存储半结构化和非结构化数据&#xff0c;每个文档可以有不同的字段&#x…

简述微服务高可用之Sentinel、Seate

简述微服务高可用之Sentinel、Seate使用 下文主要讲述使用sentinel,如何降级限流熔断及如何使用seata管理分布式事务 sentinel服务端安装与使用 1、下载 进入https://github.com/alibaba/Sentinel/releases 根据你的需求进行下载对应版本 我这里是JDK17 下载的1.8.8版本&am…

网络安全——防火墙技术

目录 前言基本概念常见防火墙技术防火墙的主要功能防火墙的不足之处相关题目1.组织外部未授权用户访问内部网络2.DMZ区3.包过滤防火墙和代理服务防火墙 前言 这是在软件设计师备考时编写的资料文章&#xff0c;相关内容偏向软件设计师 基本概念 防火墙技术是网络安全领域中的…

Java | Leetcode Java题解之第491题非递减子序列

题目&#xff1a; 题解&#xff1a; class Solution {List<Integer> temp new ArrayList<Integer>();List<List<Integer>> ans new ArrayList<List<Integer>>();public List<List<Integer>> findSubsequences(int[] nums) …

走廊泼水节——求维持最小生成树的完全图的最小边权和

题目 思考 代码 #include <bits/stdc.h> using namespace std; const int N 6010; const int M N; int p[N], sz[N]; struct edge{int a;int b;int c;bool operator < (const edge& v) const{return c < v.c;} }e[M]; int find(int x) {if(p[x] ! x) p[x] …