4

我在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));
}

这显然是一个人为的例子,但确实显示了问题。有谁知道我怎样才能正确获得“解决”的可选参数?


附加信息:如果我删除##before __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));
}

编译,但它不再适用于零参数,因为它会解析为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));
}

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

4

3 回答 3

14

运算符会进行精确的##标记替换,因此在这种情况下,它会尝试将标记"CALL(func1, false)"作为最后一个参数发送给func1C函数

问题在于它CALL是一个宏,您不能##__VA_ARGS__在列表中嵌套可变参数宏调用。

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

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

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

   bool result = CALL(func1, false);
   CALL(func2, false, result);
}

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

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

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

让我们更详细地分析一下您的困境:

CALL(func2, false, CALL(func1, false))

在这个特定的宏调用中,CALLis now("func2", "tmp", CALL(func1, false)) 所以它尝试调用func1,传入tmp,并且,好吧,CALL(func1, false)

这是预处理器和实际 C 编译器之间的界线。

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

于 2011-01-01T12:07:42.397 回答
4

您正在使用带有, ##构造的 gcc 扩展。如果您考虑到可移植性,使用它可能不是一个好主意。

稍加努力,您就可以构建宏,这些宏可能会对它们收到的参数数量做出反应并进行正确的替换。P99为此提供了助手:

#define CALL(...) P99_IF_EQ_2(P99_NARG(__VA_ARGS__))(dosomethingwithtwo(__VA_ARGS__))(dosomethingwithmore(__VA_ARGS__))

但在你的情况下,我认为有一个简单的解决方案:

#define CALL(func, ...) func(__VA_ARGS__)
于 2011-01-01T13:43:53.570 回答
0

所以我只是将我的代码转换为使用我原始帖子末尾提到的设计,它突然又开始工作了。我假设我的 P99 包装器有错字。

它现在可以正确编译和运行任意数量的参数和任意数量的嵌套,作为任何参数。感谢大家!

于 2011-01-01T17:57:26.797 回答