C++开发:rest_rpc路由器类router
·
RPC 路由器类(router) 的实现,用于在服务端或客户端中根据函数名(或其哈希值)来注册和调用远程过程。这个类使用了现代 C++ 技术(如模板元编程、tuple 解包、lambda 表达式、std::function 等),具有良好的扩展性和类型安全性。
整体结构概述
类定义
class router : asio::noncopyable {
- 继承自
asio::noncopyable:禁止拷贝构造与赋值操作。 - 使用了命名空间嵌套(如
rest_rpc::rpc_service::router)。
核心成员变量
std::unordered_map<uint32_t, std::function<void(...)>> map_invokers_;
std::unordered_map<uint32_t, std::string> key2func_name_;
map_invokers_:核心映射表,将函数签名对应的 hash 值(uint32_t)映射到一个可调用对象(invoker)。key2func_name_:记录函数名与 hash 值的对应关系,便于调试或错误提示。
枚举和结构体定义
enum class router_error { ok, no_such_function, has_exception, unkonw };
struct route_result_t {
router_error ec = router_error::unkonw;
std::string result;
};
定义了路由错误类型和路由结果结构体。
helper_t模板类
template <typename Tuple, bool is_pub> class helper_t {
public:
helper_t(Tuple &tp) : tp_(tp) {}
void operator()() {}
private:
Tuple &tp_;
};
template <typename Tuple> class helper_t<Tuple, true> {
public:
helper_t(Tuple &tp) : tp_(tp) {}
void operator()() {
auto &arg = std::get<std::tuple_size<Tuple>::value - 1>(tp_);
msgpack_codec codec;
arg = codec.unpack<std::string>(arg.data(), arg.size());
}
private:
Tuple &tp_;
};
这是一个辅助模板类,用于处理发布/订阅模式下的参数解包。
注册函数接口(register_handler)
提供了两个重载版本的 register_handler(),支持注册普通函数和成员函数。
1. 注册非成员函数(静态函数或 lambda)
template <bool is_pub = false, typename Function>
void register_handler(std::string const &name, Function f, bool pub = false)
- 计算函数名的 MD5 哈希作为唯一标识符。
- 检查是否重复注册。
- 调用
register_nonmember_func<is_pub>(key, f)完成实际注册。
2. 注册成员函数
template <bool is_pub = false, typename Function, typename Self>
void register_handler(std::string const &name, const Function &f, Self *self)
- 同样计算 hash 并检查重复。
- 调用
register_member_func<is_pub>(key, f, self)注册成员函数。
注册内部实现(register_nonmember_func / register_member_func)
这两个函数负责将用户注册的函数包装成统一格式,并存入 map_invokers_ 中。
典型处理逻辑:
- 将原始函数封装为一个 lambda。
- 使用
msgpack_codec解码传入的字符串参数为 tuple。 - 使用 helper 工具类(如
helper_t<args_tuple, is_pub>)进行参数验证(可能是权限校验或参数数量检查)。 - 调用
call()或call_member()来执行函数并返回结果。
通用调用辅助函数(call / call_member)
这两个函数实现了对任意函数签名的支持,通过 std::tuple 解包参数并转发给目标函数。
call()
template <typename F, typename... Args>
static typename std::enable_if<!std::is_void<...>::value>::type
call(const F &f, std::weak_ptr<connection> ptr, std::string &result, std::tuple<Args...> tp)
- 支持返回值的函数。
- 使用
call_helper()执行函数调用。 - 使用
msgpack_codec打包结果。
call_member()
template <typename F, typename Self, typename... Args>
static void call_member(const F &f, Self *self, std::weak_ptr<connection> ptr, std::string &result, std::tuple<Args...> tp)
- 类似于
call(),但适用于成员函数。 - 使用
call_member_helper()来调用成员函数指针。
路由调用入口(route())
这是整个路由器的核心方法,用于根据函数 hash 值调用对应的函数。
template <typename T>
route_result_t route(uint32_t key, nonstd::string_view data, std::weak_ptr<T> conn)
主要逻辑:
- 查找 hash 对应的 invoker。
- 如果找不到,返回错误信息。
- 否则调用 invoker 函数,传入连接指针、数据和输出结果。
- 返回包含结果和错误码的
route_result_t。
其他实用方法
| 方法 | 功能 |
|---|---|
remove_handler(name) | 删除已注册的函数 |
get_name_by_key(key) | 根据 hash 获取函数名(调试/日志用) |
call_helper() / call_member_helper() | 辅助调用函数,利用 index_sequence 解包 tuple 参数 |
helper_t | 可能用于参数合法性检查(如 pub/sub 场景下的特殊处理) |
关键类型与工具类说明
1. msgpack_codec
- 用于序列化/反序列化消息体,基于 MessagePack 协议。
- 支持从
string_view解包为tuple,也支持打包结果。
2. function_traits<Function>
- 提取函数签名信息(如参数个数、参数类型等)。
- 是实现泛型调用的关键。
3. nonstd::index_sequence / make_index_sequence
- 编译期生成索引列表,用于解包
std::tuple。
4. route_result_t
- 包含函数调用结果(如 OK、异常等)和实际响应内容。
设计总结
| 特性 | 描述 |
|---|---|
| 泛型支持 | 支持任意函数签名(带返回值、无返回值、成员函数) |
| 高性能 | 使用 hash 快速查找函数 |
| 安全性 | 使用 std::weak_ptr<connection> 防止循环引用 |
| 错误处理 | 异常捕获、统一返回格式 |
| 扩展性强 | 易于添加新的调用方式或中间件逻辑 |
| 支持 pub/sub | 通过 is_pub 模板参数区分不同调用语义 |
router是一个高度泛型、模块清晰、设计优雅的 RPC 路由器类。它结合了现代 C++ 的多种高级特性,能够很好地支持远程函数注册、调用、错误处理和序列化通信。无论是构建高性能 RPC 服务端还是客户端,都具备很高的实用性。
更多推荐
所有评论(0)