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

如何根据传入宏的函数参数数量选择对应绑定宏?

问题

已知可通过宏展开与__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;
}

实现说明

  1. 编译期参数检测:has_int_param利用SFINAE特性,当传入的函数可接收一个int参数时,返回std::true_type;否则返回std::false_type,整个判断过程在编译阶段完成,无运行时开销。
  2. 宏自动选择:SELECT_BIND通过std::conditional_t进行编译期条件判断,根据has_int_param的结果选择对应的BIND_FN_DATA或BIND_FN宏,最终生成符合要求的lambda对象。

若希望完全脱离模板实现,由于预处理器不具备类型分析能力,无法直接判断函数参数个数,因此上述方案是最符合C++标准且满足需求的实现方式。

内容的提问来源于stack exchange,提问作者avilapa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 02:00:38