我有一个来自第三方C库的变量函数。
int func(int argc, ...);
argc
表示传递的可选参数的数量。 我用一个宏来包装它,计算参数的数量,就像建议的那样。此处. 为了方便阅读,这里是宏。
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
_61, _62, _63, N, ...) N
#define PP_RSEQ_N() \
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, 0
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_NARG(...) PP_NARG_(__VA_ARGS__, PP_RSEQ_N())
我是这样包装的:
#define my_func(...) func(PP_NARG(__VA_ARGS__), __VA_ARGS__)
这个... PP_NARG
宏对于接受一个或多个参数的函数非常有效。例如: PP_NARG("Hello", "World")
评价为 2
.
问题是,当没有传递参数时。PP_NARG()
评价为 1
而不是 0
. 我明白 这个宏的工作原理但我想不出办法来修改它,使它在这种情况下也能正常工作。
有什么想法吗?
EDIT: 我找到了一个变通的办法 PP_NARG
,并将其作为答案发布。不过我在封装变量函数的时候还是遇到了问题。当 __VA_ARGS__
是空的。my_func
扩大到 func(0, )
触发了一个编译错误。
另一种可能,它没有使用 sizeof
也不是GCC扩展,而是在你的代码中添加以下内容
#define PP_COMMASEQ_N() \
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, 0
#define PP_COMMA(...) ,
#define PP_HASCOMMA(...) \
PP_NARG_(__VA_ARGS__, PP_COMMASEQ_N())
#define PP_NARG(...) \
PP_NARG_HELPER1( \
PP_HASCOMMA(__VA_ARGS__), \
PP_HASCOMMA(PP_COMMA __VA_ARGS__ ()), \
PP_NARG_(__VA_ARGS__, PP_RSEQ_N()))
#define PP_NARG_HELPER1(a, b, N) PP_NARG_HELPER2(a, b, N)
#define PP_NARG_HELPER2(a, b, N) PP_NARG_HELPER3_ ## a ## b(N)
#define PP_NARG_HELPER3_01(N) 0
#define PP_NARG_HELPER3_00(N) 1
#define PP_NARG_HELPER3_11(N) N
结果是
PP_NARG() // expands to 0
PP_NARG(x) // expands to 1
PP_NARG(x, 2) // expands to 2
这些宏的诀窍是 PP_HASCOMMA(...)
当调用零或一个参数时,扩展为0,当调用至少两个参数时,扩展为1。为了区分这两种情况,我使用了 PP_COMMA __VA_ARGS__ ()
的时候,它将返回一个逗号。__VA_ARGS__
是空的,当 __VA_ARGS__
是非空的。
现在有三种可能的情况。
__VA_ARGS__
是空的。PP_HASCOMMA(__VA_ARGS__)
返回0和 PP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
返回1。
__VA_ARGS__
包含一个参数。PP_HASCOMMA(__VA_ARGS__)
返回0和 PP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
返回0。
__VA_ARGS__
包含两个或多个参数。PP_HASCOMMA(__VA_ARGS__)
返回1和 PP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
返回1。
的 PP_NARG_HELPERx
宏是解决这些情况的刚需。
为了解决 func(0, )
问题,我们需要测试我们是否提供了zer或更多的参数。在这个问题上,我们需要测试我们是否提供了零或多个参数。PP_ISZERO
宏在此发挥作用。
#define PP_ISZERO(x) PP_HASCOMMA(PP_ISZERO_HELPER_ ## x)
#define PP_ISZERO_HELPER_0 ,
现在让我们定义另一个宏,它将参数的数量预置到参数列表中。
#define PP_PREPEND_NARG(...) \
PP_PREPEND_NARG_HELPER1(PP_NARG(__VA_ARGS__), __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER1(N, ...) \
PP_PREPEND_NARG_HELPER2(PP_ISZERO(N), N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER2(z, N, ...) \
PP_PREPEND_NARG_HELPER3(z, N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER3(z, N, ...) \
PP_PREPEND_NARG_HELPER4_ ## z (N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER4_1(N, ...) 0
#define PP_PREPEND_NARG_HELPER4_0(N, ...) N, __VA_ARGS__
我们还需要许多助手来扩展宏的数值。最后测试一下。
#define my_func(...) func(PP_PREPEND_NARG(__VA_ARGS__))
my_func() // expands to func(0)
my_func(x) // expands to func(1, x)
my_func(x, y) // expands to func(2, x, y)
my_func(x, y, z) // expands to func(3, x, y, z)
http:/coliru.stacked -crooked.coma73b4b6d75d45a1c8
它可以在GCC中使用 ##VA_ARGS 延长。
#define PP_ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \
_21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \
_41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \
_51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \
_61, _62, _63, N, ...) N
/* Note 63 is removed */
#define PP_RSEQ_N() \
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, 0
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
/* Note dummy first argument _ and ##__VA_ARGS__ instead of __VA_ARGS__ */
#define PP_NARG(...) PP_NARG_(_, ##__VA_ARGS__, PP_RSEQ_N())
#define my_func(...) func(PP_NARG(__VA_ARGS__), __VA_ARGS__)
现在 PP_NARG(a, b, c)
给出3和 PP_NARG()
给出0。
不幸的是,我没有看到一般的方法来使它工作。
我想出了以下的变通方法来解决这个问题 PP_NARG
:
#define PP_NARG(...) (sizeof(#__VA_ARGS__) - 1 ? \
PP_NARG_(__VA_ARGS__, PP_RSEQ_N()) : 0)
它是字符串化的 __VA_ARGS__
所以如果它是空的,它的长度等于1(因为 #__VA_ARGS__ == '\0'
). 它的工作原理是 -std=c99 -pedantic
.
不过我在封装变量函数时还是遇到了问题。当 __VA_ARGS__
是空的。my_func
扩大到 func(0, )
触发了一个编译错误。
不幸的是,Mehrwolf答案中的完整例子在VS2010上无法编译,在VS2015上也无法编译(我也尝试过在 这个 在线VS编译器)。) 在这个问题上,一个不同的方法是使用非标准的扩展,这些扩展在MS编译器中也是可用的,如我的 "跨平台编译"。回答 到类似的问题。同时,与引用的答案形式不同,结果是:"我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说,我是说。PP_NARG((a, b, c))
(在MS和gccclang编译器中)都是1而不是0。