基于动态类型调度C++模板函数的更优实现方案问询
动态模板调度的优雅实现方案(兼容C++17及更高版本)
问题背景
假设有类型P,逻辑上由类型T模板化,但P封装了Perl库,T只能在运行时确定。C侧可通过P的hasmembertype方法查询其成员类型T,可选类型数量有限。现在需要根据T实例化模板函数f,实现动态模板调度。当前实现需要为每个类似f的函数包装functor,想找更优雅的实现方式,要求兼容C17,同时也关注更高版本C++的解决方案。
补充说明:P本身并非T1、T2等类型,而是拥有可通过hasmembertype查询的成员类型T。
现有实现代码
调度函数
template<typename Fun, typename... Args> P dispatch(Fun fun, P p, Args&&... args){ if(p.hasmembertype("T1")) return fun.template operator()<T1>(p, std::forward<Args>(args)...); if(p.hasmembertype("T2")) return fun.template operator()<T2>(p, std::forward<Args>(args)...); // ... 其他类型判断 else throw std::runtime_error("Unsupported type."); }
函数包装器
struct { template<typename T> P operator()(P& p) { return f<T>(p); } } static const f_wrapper; // 使用方式 dispatch(f_wrapper, p, ...);
优化方案
C++17 实现:模板元编程+函数指针表
可以预先构建类型到处理函数的映射表,不用重复写functor包装,直接传递模板函数的实例化版本。
首先定义包含所有可能T类型的标签列表:
using SupportedTypes = std::tuple<T1, T2, /* ... 其他支持的类型 */>;
然后编写辅助函数生成调度表并执行逻辑:
template<typename Func, typename... Args> P dispatch_v2(Func&& func, P p, Args&&... args) { return std::apply([&](auto... Ts) { using FuncType = P(*)(P, Args&&...); // 构建类型名到处理函数的映射 const std::unordered_map<std::string, FuncType> dispatch_map = { {typeid(decltype(Ts)).name(), +[](P p, Args&&... args) -> P { return func.template operator()<decltype(Ts)>(std::move(p), std::forward<Args>(args)...); }}... }; // 遍历匹配类型并执行 for (const auto& [type_name, handler] : dispatch_map) { if (p.hasmembertype(type_name)) { return handler(std::move(p), std::forward<Args>(args)...); } } throw std::runtime_error("Unsupported type."); }, SupportedTypes{}); }
如果f是普通模板函数,直接用lambda包装即可,不用写functor:
// 使用方式 dispatch_v2([](auto T_tag, P p, auto&&... args) { using T = decltype(T_tag); return f<T>(std::move(p), std::forward<decltype(args)>(args)...); }, p, ...);
或者用模板包装器简化:
template<template<typename> typename F, typename... Args> struct TemplateFuncWrapper { template<typename T> P operator()(P p, Args&&... args) { return F<T>(std::move(p), std::forward<Args>(args)...); } }; // 使用时 dispatch_v2(TemplateFuncWrapper<f>{}, p, ...);
C++20 实现:std::variant + std::visit
如果能把P的成员类型T映射为std::variant的标签,就能用std::visit实现更简洁的调度:
首先定义variant类型:
using TypeVariant = std::variant<std::type_identity<T1>, std::type_identity<T2>, /* ... */>;
然后写函数把P转成对应的variant标签:
TypeVariant get_type_variant(const P& p) { if (p.hasmembertype("T1")) return std::type_identity<T1>{}; if (p.hasmembertype("T2")) return std::type_identity<T2>{}; // ... 其他类型判断 throw std::runtime_error("Unsupported type."); }
调度逻辑就简化成这样:
template<template<typename> typename F, typename... Args> P dispatch_v3(P p, Args&&... args) { return std::visit([&](auto tag) { using T = typename decltype(tag)::type; return F<T>(std::move(p), std::forward<Args>(args)...); }, get_type_variant(p)); } // 使用方式 dispatch_v3<f>(p, ...);
这种方式完全不需要包装functor,直接传模板函数模板就行,代码更清爽。
C++17 单函数简化方案:预生成处理函数表
如果只是针对单个模板函数f做调度,可以提前生成处理函数表,直接根据类型名查找调用:
// 为每个支持的类型生成处理函数 template<typename T> P handle_f(P p) { return f<T>(p); } // 全局调度表 const std::unordered_map<std::string, P(*)(P)> f_dispatch_table = { {"T1", handle_f<T1>}, {"T2", handle_f<T2>}, // ... 其他类型 }; // 调度函数 P dispatch_f(P p) { // 假设P有方法直接返回类型名字符串,没有的话可以遍历判断 auto it = f_dispatch_table.find(p.get_membertype_name()); if (it != f_dispatch_table.end()) { return it->second(std::move(p)); } throw std::runtime_error("Unsupported type."); } // 使用方式 dispatch_f(p);
这种方式不用重复写通用dispatch逻辑,维护对应函数的调度表就行,适合单个函数的场景。
内容的提问来源于stack exchange,提问作者Bubaya
相关产品推荐
相关产品推荐

