如何在模板方法中提取__VA_ARGS__的类型并传递?
问题:从
__VA_ARGS__提取类型填充模板,以及无宏的现代实现 我想知道有没有简洁的方法从__VA_ARGS__中提取类型,用来填充模板方法或结构体定义?提前感谢!
我遇到的问题如下:
我的宏定义:
#define MY_MACRO(topic, callback_name, message_type, ...) \ Callback_data<message_type, ##__VA_ARGS__> callback_name##_data(callback_name, topic); \ Handler::add_callback<message_type, ##__VA_ARGS__>(callback_name##_data)
宏调用:
MY_MACRO("/my_topic_1", callback_1, bool, int a, int b);
我期望宏展开为:
Callback_data<bool, int, int> callback_1_data(callback_1, "/my_topic_1"); Handler::add_callback<bool, int, int>(callback_1_data);
但实际展开结果是:
Callback_data<bool,int a, int b> callback_1_data(callback_1, "/my_topic_1"); Handler::add_callback<bool,int a, int b>(callback_1_data); //! error on both lines, template filling expecting only type definitions !
另外,若有无需使用宏的现代C++实现方式,我也很感兴趣!
一、宏层面的解决方案
核心问题是你传入的__VA_ARGS__包含了参数名(比如int a里的a),而模板参数只接受纯类型。可以通过辅助宏剥离参数名,只保留类型:
// 辅助宏:从"type name"格式中提取type #define GET_TYPE(type, ...) type // 递归处理可变参数,将每个参数转换为纯类型并添加逗号分隔 #define EXPAND_TYPES(...) _EXPAND_TYPES(__VA_ARGS__) #define _EXPAND_TYPES(a, ...) GET_TYPE(a) _COMMA_IF(__VA_ARGS__) _EXPAND_TYPES(__VA_ARGS__) #define _COMMA_IF(...) _COMMA_IF_HELPER(__VA_ARGS__) #define _COMMA_IF_HELPER(...) , // 终止递归的空宏 #define _EXPAND_TYPES(...) // 重写你的宏 #define MY_MACRO(topic, callback_name, message_type, ...) \ Callback_data<message_type, EXPAND_TYPES(__VA_ARGS__)> callback_name##_data(callback_name, topic); \ Handler::add_callback<message_type, EXPAND_TYPES(__VA_ARGS__)>(callback_name##_data)
调用原宏时,EXPAND_TYPES(int a, int b)会展开为int, int,模板参数就能正确匹配预期格式。
二、现代C++无宏实现
C++11及以后的版本可以利用模板推导、可调用对象等特性完全替代宏,代码更安全、可读性更强:
方法1:基于函数指针的自动推导
利用编译器对函数签名的自动推导,直接从回调函数中提取模板参数:
// 假设Callback_data和Handler的定义符合模板参数要求 template<typename MsgT, typename... ArgsT> void register_callback(const std::string& topic, void(*callback)(MsgT, ArgsT...)) { Callback_data<MsgT, ArgsT...> data(callback, topic); Handler::add_callback<MsgT, ArgsT...>(data); } // 使用示例 void callback_1(bool msg, int a, int b) { // 回调逻辑 } // 注册时自动推导所有模板参数 register_callback("/my_topic_1", callback_1);
方法2:支持任意可调用对象的灵活实现
用std::function封装成员函数、lambda等任意可调用对象,适配更复杂的场景:
#include <functional> template<typename MsgT, typename... ArgsT> void register_callback(const std::string& topic, std::function<void(MsgT, ArgsT...)> callback) { Callback_data<MsgT, ArgsT...> data(callback, topic); Handler::add_callback<MsgT, ArgsT...>(data); } // 使用示例(成员函数) class MyClass { public: void callback_2(bool msg, int a, int b) { // 逻辑实现 } }; MyClass obj; register_callback("/my_topic_2", [&obj](bool msg, int a, int b) { obj.callback_2(msg, a, b); });
方法3:C++20概念增强类型约束
如果需要严格校验回调签名,可以用C++20概念添加类型约束:
#include <concepts> template<typename Func, typename MsgT> concept ValidCallback = requires(Func f, MsgT msg, auto... args) { f(msg, args...); }; template<typename MsgT, ValidCallback<MsgT> Func> void register_callback(const std::string& topic, Func callback) { // 利用std::invoke_result推导参数类型 using CallbackSig = decltype(std::function{callback}); // 根据实际Callback_data的定义调整参数提取逻辑 Callback_data<MsgT, /* 自动提取的Args类型 */> data(callback, topic); Handler::add_callback<MsgT, /* 自动提取的Args类型 */>(data); }
内容的提问来源于stack exchange,提问作者Gabriel De Champeaux
相关产品推荐
相关产品推荐

