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

如何用C预处理器处理逗号分隔宏列表,生成函数指针与字符串数组?

基于逗号分隔令牌宏自动生成函数指针数组与字符串数组

你提出的需求完全可以实现,借助C预处理器的宏连接(##)、字符串化(#)以及可变参数宏的递归展开能力,就能让数组内容随TOKENS宏的变更自动同步。以下是具体实现方案:

一、核心宏定义

首先实现两个核心辅助逻辑:给每个令牌添加_callback后缀,以及将每个令牌转换为对应的字符串。我们可以通过通用的"逐个元素应用宏"工具宏来复用逻辑。

// 原始令牌列表
#define TOKENS ACC, STOP, RUN, BACK

// 辅助宏1:给单个令牌添加_callback后缀
#define ADD_CALLBACK(token) token##_callback

// 辅助宏2:将单个令牌字符串化
#define STRINGIFY(token) #token

// 通用工具宏:逐个将宏应用到列表的每个元素
#define _APPLY_EACH_HELPER(macro, elem, ...) \
    macro(elem) \
    _IF_HAS_NEXT(__VA_ARGS__, , _APPLY_EACH_HELPER(macro, __VA_ARGS__))

#define _IF_HAS_NEXT(...) \
    _IF_HAS_NEXT_HELPER(__VA_ARGS__, 1, 0)

#define _IF_HAS_NEXT_HELPER(_1, _2, has_next, ...) has_next

#define _APPLY_EACH(macro, ...) \
    _APPLY_EACH_HELPER(macro, __VA_ARGS__)

二、生成函数指针数组

利用上面的工具宏,实现some_macro_shenanigans来展开为带后缀的函数指针列表:

#define some_macro_shenanigans(tokens) \
    _APPLY_EACH(ADD_CALLBACK, tokens)

// 定义对应的回调函数(需与令牌一一对应)
int ACC_callback(const char *arg) { /* 自定义逻辑 */ }
int STOP_callback(const char *arg) { /* 自定义逻辑 */ }
int RUN_callback(const char *arg) { /* 自定义逻辑 */ }
int BACK_callback(const char *arg) { /* 自定义逻辑 */ }

// 生成函数指针数组
int (*callbacks[])(const char * arg) = {
    some_macro_shenanigans(TOKENS)
};

展开后等价于:

int (*callbacks[])(const char * arg) = {
    ACC_callback, STOP_callback, RUN_callback, BACK_callback
};

三、生成字符串数组

同样复用工具宏,实现some_other_macro_shenanigans来生成字符串数组:

#define some_other_macro_shenanigans(tokens) \
    { _APPLY_EACH(STRINGIFY, tokens) }

// 生成字符串数组
const char * const token_str[] = some_other_macro_shenanigans(TOKENS);

展开后等价于:

const char * const token_str[] = { "ACC", "STOP", "RUN", "BACK" };

四、完整可运行示例

#include <stdio.h>

// 原始令牌列表
#define TOKENS ACC, STOP, RUN, BACK

// 辅助宏1:给单个令牌添加_callback后缀
#define ADD_CALLBACK(token) token##_callback

// 辅助宏2:将单个令牌字符串化
#define STRINGIFY(token) #token

// 通用工具宏:逐个将宏应用到列表的每个元素
#define _APPLY_EACH_HELPER(macro, elem, ...) \
    macro(elem) \
    _IF_HAS_NEXT(__VA_ARGS__, , _APPLY_EACH_HELPER(macro, __VA_ARGS__))

#define _IF_HAS_NEXT(...) \
    _IF_HAS_NEXT_HELPER(__VA_ARGS__, 1, 0)

#define _IF_HAS_NEXT_HELPER(_1, _2, has_next, ...) has_next

#define _APPLY_EACH(macro, ...) \
    _APPLY_EACH_HELPER(macro, __VA_ARGS__)

// 生成函数指针数组的宏
#define some_macro_shenanigans(tokens) \
    _APPLY_EACH(ADD_CALLBACK, tokens)

// 生成字符串数组的宏
#define some_other_macro_shenanigans(tokens) \
    { _APPLY_EACH(STRINGIFY, tokens) }

// 示例回调函数实现
int ACC_callback(const char *arg) {
    printf("执行ACC回调,参数:%s\n", arg);
    return 0;
}

int STOP_callback(const char *arg) {
    printf("执行STOP回调,参数:%s\n", arg);
    return 0;
}

int RUN_callback(const char *arg) {
    printf("执行RUN回调,参数:%s\n", arg);
    return 0;
}

int BACK_callback(const char *arg) {
    printf("执行BACK回调,参数:%s\n", arg);
    return 0;
}

int main() {
    // 生成函数指针数组
    int (*callbacks[])(const char * arg) = {
        some_macro_shenanigans(TOKENS)
    };

    // 生成字符串数组
    const char * const token_str[] = some_other_macro_shenanigans(TOKENS);

    // 测试调用所有回调
    size_t token_count = sizeof(callbacks) / sizeof(callbacks[0]);
    for (size_t i = 0; i < token_count; i++) {
        callbacks[i](token_str[i]);
    }

    return 0;
}

五、注意事项

  • 当TOKENS列表新增或删除令牌时,只需修改#define TOKENS这一行,数组会自动同步更新,无需手动修改数组元素。
  • 回调函数的命名必须严格遵循令牌名_callback的格式,否则会出现未定义符号的编译错误。
  • 上述宏基于标准C预处理器实现,兼容绝大多数编译器(GCC、Clang、MSVC等)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 16:18:18