Variadic 宏,输出由“||”分隔的参数
Variadic macro that outputs its arguments separated by `||`
我想制作一个宏 F
,它采用可变数量的参数,扩展为每个参数由 ||
分隔的参数。例如F(a, b, c)
应该扩展到 a || b || c
,F(a)
应该扩展到 a
,等等
我知道 C 不支持递归宏。我目前最多只需要 4 个参数。
我在想类似 #define F(a, ...) a || F(__VA_ARGS__)
的东西,然后是第二个宏,让它扩展 4 倍,但我完全不确定另一个宏应该是什么样子。我 运行 在某些时候遇到了空 __VA_ARGS__
的问题。任何其他想法将不胜感激。
限制:必须使用任何符合标准的 C99 编译器。
编辑:我已经使用 Overloading Macro on Number of Arguments 完成了这项工作,但仍然很好奇是否还有其他解决方案。
基础很简单:
#define COUNT(...) COUNT_(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_(A0, A1, A2, A3, A4, A5, A6, A7, ...) A7
#define F0()
#define F1(A0) A0
#define F2(A0, A1) A0 || A1
#define F3(A0, A1, A2) F2(A0, A1) || A2
#define F4(A0, A1, A2, A3) F3(A0, A1, A2) || A3
#define F(...) C(F, COUNT(__VA_ARGS__))(__VA_ARGS__)
#define C(X, Y) C_(X, Y)
#define C_(X, Y) X ## Y
其中 C
是通常的两步连接宏。不过还有一个问题:空 __VA_ARGS__
.
实际上,这不受 C 标准本身的支持(您必须切换到即将推出的 C++20,或者可能是 C20 向后移植?)。而且任务非常繁重(原样,COUNT()
屈服于 1,其中 COUNT_
宏中的第一个参数是空参数!)。我很久以前就为类似的任务解决了这个问题,所以你可能想看看 there,应该很容易将相关部分导入到这里...
这有点繁重,但如果您可以使用库,那么 Boost.PP 可以为您完成大部分繁重的工作。它同时支持 C 和 C++,并且是一个仅包含头文件的库(很明显)。您的用例将如下所示:
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/seq/fold_left.hpp>
#define OP(s, state, x) state || x
#define F(...) BOOST_PP_SEQ_FOLD_LEFT(OP, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
上面将可变参数转换为 boost PP 序列,然后通过应用 OP
将其折叠。 state
是每个 "iteration" 的中间结果。它的初始 "value" 是 0(因此结果表达式不依赖于它)。
当 运行 F(1, 2, 3)
through gcc -E
我们得到:
0 || 1 || 2 || 3
空参数列表(正式的 GCC 扩展,无效的 C99)的主要警告是它将扩展为 0 ||
。所以这一点值得牢记。参数计数(使用 Boost.PP)可能用于根据 0 个或多个参数在两个实现之一之间进行选择以解决此问题。
我想制作一个宏 F
,它采用可变数量的参数,扩展为每个参数由 ||
分隔的参数。例如F(a, b, c)
应该扩展到 a || b || c
,F(a)
应该扩展到 a
,等等
我知道 C 不支持递归宏。我目前最多只需要 4 个参数。
我在想类似 #define F(a, ...) a || F(__VA_ARGS__)
的东西,然后是第二个宏,让它扩展 4 倍,但我完全不确定另一个宏应该是什么样子。我 运行 在某些时候遇到了空 __VA_ARGS__
的问题。任何其他想法将不胜感激。
限制:必须使用任何符合标准的 C99 编译器。
编辑:我已经使用 Overloading Macro on Number of Arguments 完成了这项工作,但仍然很好奇是否还有其他解决方案。
基础很简单:
#define COUNT(...) COUNT_(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define COUNT_(A0, A1, A2, A3, A4, A5, A6, A7, ...) A7
#define F0()
#define F1(A0) A0
#define F2(A0, A1) A0 || A1
#define F3(A0, A1, A2) F2(A0, A1) || A2
#define F4(A0, A1, A2, A3) F3(A0, A1, A2) || A3
#define F(...) C(F, COUNT(__VA_ARGS__))(__VA_ARGS__)
#define C(X, Y) C_(X, Y)
#define C_(X, Y) X ## Y
其中 C
是通常的两步连接宏。不过还有一个问题:空 __VA_ARGS__
.
实际上,这不受 C 标准本身的支持(您必须切换到即将推出的 C++20,或者可能是 C20 向后移植?)。而且任务非常繁重(原样,COUNT()
屈服于 1,其中 COUNT_
宏中的第一个参数是空参数!)。我很久以前就为类似的任务解决了这个问题,所以你可能想看看 there,应该很容易将相关部分导入到这里...
这有点繁重,但如果您可以使用库,那么 Boost.PP 可以为您完成大部分繁重的工作。它同时支持 C 和 C++,并且是一个仅包含头文件的库(很明显)。您的用例将如下所示:
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/seq/fold_left.hpp>
#define OP(s, state, x) state || x
#define F(...) BOOST_PP_SEQ_FOLD_LEFT(OP, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
上面将可变参数转换为 boost PP 序列,然后通过应用 OP
将其折叠。 state
是每个 "iteration" 的中间结果。它的初始 "value" 是 0(因此结果表达式不依赖于它)。
当 运行 F(1, 2, 3)
through gcc -E
我们得到:
0 || 1 || 2 || 3
空参数列表(正式的 GCC 扩展,无效的 C99)的主要警告是它将扩展为 0 ||
。所以这一点值得牢记。参数计数(使用 Boost.PP)可能用于根据 0 个或多个参数在两个实现之一之间进行选择以解决此问题。