C语言中可变参数宏的问题

Bon*_*uin 4 c c-preprocessor variadic-macros

我在C中的#define语句中有可选参数的问题,或者更具体地说是gcc 4.2:

bool func1(bool tmp) { return false; }
void func2(bool tmp, bool tmp2) {}
#define CALL(func, tmp, ...) func(tmp, ##__VA_ARGS__)

int main() {
   // this compiles
   CALL(func2, CALL(func1, false), false);

   // this fails with: Implicit declaration of function 'CALL'
   CALL(func2, false, CALL(func1, false));
}
Run Code Online (Sandbox Code Playgroud)

这显然是一个人为的例子,但确实显示了问题.有谁知道我怎么能得到正确的"解决"的可选参数?


附加信息:如果我删除##之前__VA_ARGS__,并执行以下操作:

bool func2(bool tmp, bool tmp2) { return false; }
#define CALL(func, tmp, ...) func(tmp, __VA_ARGS__)

int main() {
   CALL(func2, false, CALL(func2, false, false));
}
Run Code Online (Sandbox Code Playgroud)

编译,但它不再适用零参数,因为它将解决 func(tmp, )

编辑:在将我的所有代码转换为依赖P99而不是之前的代码(最终破坏了我的代码,哎呀)之后,我意外地发现这有效:

bool func1(bool tmp) { return false; }
void func2(bool tmp, bool tmp2) {}
#define CALL2(func, tmp, p...) func(tmp, ##p)
#define CALL(func, tmp...) CALL2(func, tmp)

int main() {
   // works
   CALL(func2, CALL(func1, false), false);

   // ...also works
   CALL(func2, false, CALL(func1, false));
}
Run Code Online (Sandbox Code Playgroud)

编译并使用任意数量的参数(并传入和返回正确的值),但......这应该是合法的吗?

Jac*_*kin 14

##运营商不准确的令牌替换,所以在这种情况下,它试图发送令牌"CALL(func1, false)"作为最后一个参数func1Ç 功能.

这个问题是它CALL是一个宏,你不能##__VA_ARGS__列表中嵌套可变参数宏调用.

当内部宏作为命名参数传递时它工作的原因是因为预处理器将解析内部宏的命名参数,而不是##__VA_ARGS__列表,其中只有简单的令牌替换.

解决此问题的一种方法是将内部结果分配给CALL占位符变量,然后将其传递给宏.

int main() {
   CALL(func2, CALL(func1, false), false);

   bool result = CALL(func1, false);
   CALL(func2, false, result);
}
Run Code Online (Sandbox Code Playgroud)

解决此问题的另一种方法是仅使用函数__VA_ARGS__的唯一参数func,这将允许您传入嵌套的宏调用,如下所示:

#define CALL(func, ...) func(__VA_ARGS__)

int main() {
   CALL(func2, false, CALL(func2, false, false));
}
Run Code Online (Sandbox Code Playgroud)

让我们更详细地分析你的困境:

CALL(func2, false, CALL(func1, false))
Run Code Online (Sandbox Code Playgroud)

在这个特定的宏调用中,CALL现在是("func2", "tmp", CALL(func1, false)) 它试图调用func1,传入tmp,等等CALL(func1, false).

这是在预处理器和实际C编译器之间绘制线的位置.

预处理器,一旦开始进行替换,它就完成了解析,因此编译器接收的CALL(func1, false)是实际的C函数,而不是宏,因为编译器不知道宏,只有预处理器才知道.