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 有一个在实践中永远不会达到的数十亿次迭代的限制。
我想要一个接受多个函数指针的宏调用,每个函数指针由第二个宏调用,它是一个函数声明。
我要在表单上添加两个宏
#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__)
您还可以使用传统的递归技术设计出更通用的解决方案,但这通常需要支持库(例如
这是因为预处理器中的所有循环都必须有一个预先确定的上限,这是没有直接宏递归的结果:在像上面代码这样的简单情况下,您可以将限制硬编码到循环实现中,或者您可以让循环构造运算符下面的递归驱动程序包含限制,并为请求它们的构造提供 N 迭代(后者的优点是它可以让您集中,然后忘记这个限制,只要它足够高,例如 Order-PP 有一个在实践中永远不会达到的数十亿次迭代的限制。