You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在模板方法中提取__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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 17:05:44