有很多问题讨论如何计数__VA_ARGS__
以及零参数的问题(例如 [1] 和 [2] )。然而,这些问题的答案通常是不可移植的,因为它们使用 GCC 特定的 ##__VA_ARGS__
来解释“0 个参数”的情况,或者它们是可移植的,但不能解释 0 个参数( COUNT_ARGS()
和 COUNT_ARGS(something)
都被评估为 1
)。
有没有办法可以统计 __VA_ARGS__
中的参数个数,包括0,可以在任何符合标准的C编译器中工作吗?
最佳答案
经过一番研究,我发现了 Jens Gustedt 的一篇博客文章,名为“Detect empty macro arguments ”(我在 this answer 中找到了他的评论)。与counting solution found in another answer一起通过 H Walters (类似于 this one ),我们可以构建一个可以在任何 C99 编译器中工作的解决方案。下面的代码是这两种方法的统一。
我所做的一个值得注意的更改是添加额外的 EXPAND
宏。正如 this question 中所讨论的,MSVC不展开__VA_ARGS__
与大多数其他编译器一样,因此需要额外的扩展步骤。
/* NOTE: In these macros, "1" means true, and "0" means false. */
#define EXPAND(x) x
#define _GLUE(X,Y) X##Y
#define GLUE(X,Y) _GLUE(X,Y)
/* Returns the 100th argument. */
#define _ARG_100(_,\
_100,_99,_98,_97,_96,_95,_94,_93,_92,_91,_90,_89,_88,_87,_86,_85,_84,_83,_82,_81, \
_80,_79,_78,_77,_76,_75,_74,_73,_72,_71,_70,_69,_68,_67,_66,_65,_64,_63,_62,_61, \
_60,_59,_58,_57,_56,_55,_54,_53,_52,_51,_50,_49,_48,_47,_46,_45,_44,_43,_42,_41, \
_40,_39,_38,_37,_36,_35,_34,_33,_32,_31,_30,_29,_28,_27,_26,_25,_24,_23,_22,_21, \
_20,_19,_18,_17,_16,_15,_14,_13,_12,_11,_10,_9,_8,_7,_6,_5,_4,_3,_2,X_,...) X_
/* Returns whether __VA_ARGS__ has a comma (up to 100 arguments). */
#define HAS_COMMA(...) EXPAND(_ARG_100(__VA_ARGS__, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ,1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
/* Produces a comma if followed by a parenthesis. */
#define _TRIGGER_PARENTHESIS_(...) ,
#define _PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _IS_EMPTY_CASE_0001 ,
/* Returns true if inputs expand to (false, false, false, true) */
#define _IS_EMPTY(_0, _1, _2, _3) HAS_COMMA(_PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
/* Returns whether __VA_ARGS__ is empty. */
#define IS_EMPTY(...) \
_IS_EMPTY( \
/* Testing for an argument with a comma \
e.g. "ARG1, ARG2", "ARG1, ...", or "," */ \
HAS_COMMA(__VA_ARGS__), \
/* Testing for an argument around parenthesis \
e.g. "(ARG1)", "(...)", or "()" */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
/* Testing for a macro as an argument, which will \
expand the parenthesis, possibly generating a comma. */ \
HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
/* If all previous checks are false, __VA_ARGS__ does not \
generate a comma by itself, nor with _TRIGGER_PARENTHESIS_ \
behind it, nor with () after it. \
Therefore, "_TRIGGER_PARENTHESIS_ __VA_ARGS__ ()" \
only generates a comma if __VA_ARGS__ is empty. \
So, this tests for an empty __VA_ARGS__ (given the \
previous conditionals are false). */ \
HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \
)
#define _VAR_COUNT_EMPTY_1(...) 0
#define _VAR_COUNT_EMPTY_0(...) EXPAND(_ARG_100(__VA_ARGS__, \
100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81, \
80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61, \
60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41, \
40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21, \
20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))
#define VAR_COUNT(...) GLUE(_VAR_COUNT_EMPTY_, IS_EMPTY(__VA_ARGS__))(__VA_ARGS__)
这些是一些示例输出:
#define EATER0(...)
#define EATER1(...) ,
#define EATER2(...) (/*empty*/)
#define EATER3(...) (/*empty*/),
#define EATER4(...) EATER1
#define EATER5(...) EATER2
#define MAC0() ()
#define MAC1(x) ()
#define MACV(...) ()
#define MAC2(x,y) whatever
VAR_COUNT() // 0
VAR_COUNT(/*comment*/) // 0
VAR_COUNT(a) // 1
VAR_COUNT(a, b) // 2
VAR_COUNT(a, b, c) // 3
VAR_COUNT(a, b, c, d) // 4
VAR_COUNT(a, b, c, d, e) // 5
VAR_COUNT((a, b, c, d, e)) // 1
VAR_COUNT((void)) // 1
VAR_COUNT((void), c, d) // 3
VAR_COUNT((a, b), c, d) // 3
VAR_COUNT(_TRIGGER_PARENTHESIS_) // 1
VAR_COUNT(EATER0) // 1
VAR_COUNT(EATER1) // 1
VAR_COUNT(EATER2) // 1
VAR_COUNT(EATER3) // 1
VAR_COUNT(EATER4) // 1
VAR_COUNT(MAC0) // 1
VAR_COUNT(MAC1) // 1
VAR_COUNT(MACV) // 1
/* This one will fail because MAC2 is not called correctly. */
VAR_COUNT(MAC2) // error
/* But only if it's at the end spot. */
VAR_COUNT(MACV, MAC1, MAC2) // error
VAR_COUNT(MAC2, MAC1, MACV) // 3
正如Jens Gustedt在他的博客文章中指出的那样,这个解决方案有一个缺陷。引用:
In fact ISEMPTY should work when it is called with macros as argument that expect
0
,1
or a variable list of arguments. If called with a macroX
as an argument that itself expects more than one argument (such asMAC2
) the expansion leads to an invalid use of that macroX
.
因此,如果传递的列表末尾包含一个类似函数的宏,需要两个或多个参数(例如 MAC2
), VAR_COUNT
将会失败。
除此之外,我已经在 GCC 9.3.0 和 Visual Studio 2019 上测试了宏,它也应该适用于任何 C99(或更新版本)编译器。
任何修复上述缺陷的修改都会受到赞赏。
关于c - 一种计算 __VA_ARGS__ 参数数量(包括 0)的方法,无需编译器特定的构造,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66556552/