如何根据传入宏的函数参数数量选择对应绑定宏?
问题
已知可通过宏展开与__VA_ARGS__实现根据宏接收参数数量选择对应宏的逻辑,示例代码如下:
#define EXP(x) x #define SELECT_MACRO(_1, _2, macro) macro #define FOO1(str) printf(#str); #define FOO2(str, num) printf(#str, num); #define SELECT_FOO(...) EXP(SELECT_MACRO(__VA_ARGS__, FOO2, FOO1)(__VA_ARGS__)) int main() { int a = 5; SELECT_FOO("Hello\n"); SELECT_FOO("Number %d \n", 5); return 0; }
该方式只需记忆一个宏,提升了易用性。现需实现类似逻辑:针对传入宏的函数(参数为0个或1个int类型参数),自动选择对应的BIND_FN_DATA或BIND_FN宏,生成签名一致的lambda(均接收int&类型参数),且无运行时开销。相关简化代码如下:
void PrintNumber(int n) { printf("%d\n", n); } void PrintHello() { printf("Hello\n"); } #define BIND_FN_DATA(fn) [](int& num) { fn(num); } #define BIND_FN(fn) [](int& num) { (void)num; fn(); } #define SELECT_BIND(...) // 需要实现这个宏 int main() { auto a = BIND_FN_DATA(PrintNumber); auto b = BIND_FN(PrintHello); // 期望调用方式 // auto c = SELECT_BIND(PrintNumber); // auto d = SELECT_BIND(PrintHello); return 0; }
请问SELECT_BIND宏应如何编写?
解决方案
由于C预处理器无法直接感知函数的参数个数,我们需要结合C++编译期SFINAE特性与宏来实现需求。以下是完整的可行实现:
#include <cstdio> #include <type_traits> void PrintNumber(int n) { printf("%d\n", n); } void PrintHello() { printf("Hello\n"); } #define BIND_FN_DATA(fn) [](int& num) { fn(num); } #define BIND_FN(fn) [](int& num) { (void)num; fn(); } // 编译期辅助函数:检测函数是否接受一个int参数 template<typename F> constexpr auto has_int_param(F&&) -> decltype(std::declval<F>()(std::declval<int>()), std::true_type{}) { return {}; } constexpr std::false_type has_int_param(...) { return {}; } // 核心宏:根据函数参数情况自动选择对应BIND宏 #define SELECT_BIND(fn) \ std::conditional_t<decltype(has_int_param(fn))::value, \ decltype(BIND_FN_DATA(fn)), \ decltype(BIND_FN(fn))>(BIND_FN_DATA(fn)) int main() { auto a = BIND_FN_DATA(PrintNumber); auto b = BIND_FN(PrintHello); // 使用自动选择的宏 auto c = SELECT_BIND(PrintNumber); auto d = SELECT_BIND(PrintHello); int num = 10; c(num); // 输出10 d(num); // 输出Hello return 0; }
实现说明
- 编译期参数检测:
has_int_param利用SFINAE特性,当传入的函数可接收一个int参数时,返回std::true_type;否则返回std::false_type,整个判断过程在编译阶段完成,无运行时开销。 - 宏自动选择:
SELECT_BIND通过std::conditional_t进行编译期条件判断,根据has_int_param的结果选择对应的BIND_FN_DATA或BIND_FN宏,最终生成符合要求的lambda对象。
若希望完全脱离模板实现,由于预处理器不具备类型分析能力,无法直接判断函数参数个数,因此上述方案是最符合C++标准且满足需求的实现方式。
内容的提问来源于stack exchange,提问作者avilapa
相关产品推荐
相关产品推荐

