C - 可变参数宏,它扩展为对每个参数的一组宏调用

C - Variadic macro which expands into set of macro calls on each argument

我想要一个接受多个函数指针的宏调用,每个函数指针由第二个宏调用,它是一个函数声明。

我要在表单上添加两个宏

#define FUNCTION_DEF(func) extern int func(void);
#define FUNCTION_DEFS(...) (???)

这样称呼

FUNCTION_DEFS(
    myFunc1,
    myFunc2,

    otherFunc1,
    otherFunc2,

    defaultFunc
)

扩展为

FUNCTION_DEF(myFunc1)
FUNCTION_DEF(myFunc2)

FUNCTION_DEF(otherFunc1)
FUNCTION_DEF(otherFunc2)

FUNCTION_DEF(defaultFunc)

换句话说,对 FUNCTION_DEFS 的单个调用扩展为所有可变参数的函数声明。

目前我只是跳过第一步并在每个函数指针上调用 FUNCTION_DEF,但是对此的解决方案会很棒。

这可能吗?

编辑:

感谢@Vality 向我介绍 X-Macro。我发现这个 post“Real-world use of X-Macros”正是我所需要的。

我不相信使用标准 C 预处理器可以准确实现您想要的结果。但是,可以使用 X 宏来完成类似的解决方案。

要使用它们执行与您的代码等效的操作,您首先需要将函数列表定义为 X 宏:

#define FUNCTION_LIST_A \
    X(myFunc1) \
    X(myFunc2) \
    X(otherFunc1) \
    X(otherFunc2) \
    X(defaultFunc)

然后要使用特定的宏实例化这些函数,您将定义要在每个函数上执行的宏:

#define X(name) FUNCTION_DEF(name)
FUNCTION_LIST_A
#undef X

然后将扩展为:

FUNCTION_DEF(myFunc1)
FUNCTION_DEF(myFunc2)
FUNCTION_DEF(otherFunc1)
FUNCTION_DEF(otherFunc2)
FUNCTION_DEF(defaultFunc)

希望这是有用的并且接近你想要的。不可否认,语法有很大不同,但如果您希望完成的是将选定的函数或宏应用于整个数据列表(在本例中为函数指针),这是我所知道的使用 c 预处理器的最惯用的方法.

有很多方法可以做到这一点。最简单的方法是为每个可能的长度预定义一个循环版本(基于更小的版本),然后根据要迭代的参数数量简单地选择正确的版本:

#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N

#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_ID(...) __VA_ARGS__

#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__)

#define M_FOR_EACH_0(ACTN, E) E
#define M_FOR_EACH_1(ACTN, E) ACTN(E)
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__)
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__)
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__)
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__)
//...etc

#define FUNCTION_DEF(func) extern int func(void);
#define FUNCTION_DEFS(...) M_FOR_EACH(FUNCTION_DEF, __VA_ARGS__)

您还可以使用传统的递归技术设计出更通用的解决方案,但这通常需要支持库(例如 , [2])来提供该机制,因为直接定义的宏不支持递归。

这是因为预处理器中的所有循环都必须有一个预先确定的上限,这是没有直接宏递归的结果:在像上面代码这样的简单情况下,您可以将限制硬编码到循环实现中,或者您可以让循环构造运算符下面的递归驱动程序包含限制,并为请求它们的构造提供 N 迭代(后者的优点是它可以让您集中,然后忘记这个限制,只要它足够高,例如 Order-PP 有一个在实践中永远不会达到的数十亿次迭代的限制。