C 中可变参数宏的问题

发布于 2024-10-10 02:33:36 字数 1266 浏览 0 评论 0原文

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

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


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

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

I'm having a problem with optional arguments in #define statements in C, or more specifically with 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));
}

That's obviously a contrived example, but does show the problem. Does anyone know how I can get the optional arguments to "resolve" correctly?

Additional information:
If I remove the ## before __VA_ARGS__, and do something like this:

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

That compiles, but it no longer works with zero arguments since it would resolve to func(tmp, )

EDIT: Right after converting all of my code to rely on P99 instead of what I had earlier (which ended up breaking my code considerably, whoops), I accidentally discovered that this works:

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

Compiles and works with any number of parameters (and the correct values are passed in and returned), but... is this supposed to be legal?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

分分钟 2024-10-17 02:33:36

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

问题在于,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))

在这个特定的宏调用中,CALL 现在是 ("func2", "tmp", CALL(func1, false))
因此它尝试调用 func1,传入 tmp,以及 CALL(func1, false)

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

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

The ## operator does exact token substitution, so in that case it's trying to send the token "CALL(func1, false)" as the last argument to the func1 C function.

The problem with this is that CALL is a macro, and you cannot nest variadic macro invocations within a ##__VA_ARGS__ list.

The reason why it works when the inner macro is passed as a named argument is because the preprocessor will parse named arguments for inner macros, but not ##__VA_ARGS__ lists, where there is just simple token substitution.

One way to solve this is to assign the result of the inner CALL to a placeholder variable and then pass that along to the macro.

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

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

Another way to solve this is to just use __VA_ARGS__ as the only argument to the func function, and that would allow you to pass in nested macro invocations, like so:

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

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

Let's analyze your dilemma in a bit more detail:

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

In this particular macro invocation, CALL is now ("func2", "tmp", CALL(func1, false))
So it tries to invoke func1, passing in tmp, and, well, CALL(func1, false).

This is where the line is drawn between the preprocessor and the actual C compiler.

The preprocessor, once it starts doing substitution, it's done parsing, so the compiler receives CALL(func1, false) as an actual C function, not a macro, because the compiler doesn't know about macros, only the preprocessor does.

最笨的告白 2024-10-17 02:33:36

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

只需付出一点努力,您就可以构建可以对收到的参数数量做出反应并进行正确替换的宏。 P99 为此提供了帮助程序:

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

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

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

You are using a gcc extension with the , ## construct. It is probably not a good idea to use that if you have portability in mind.

With a little bit of effort you can construct macros that may react on the number of arguments they receive and do the right replacement. P99 provides helpers for that:

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

But in your case I think there is an easy solution:

#define CALL(func, ...) func(__VA_ARGS__)
迷爱 2024-10-17 02:33:36

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

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

So I just converted my code to use the design mentioned at the end of my original post, and it suddenly started working again. I'm going to assume there was a typo with my P99 wrapper.

It now compiles and functions correctly for any number of arguments and any amount of nesting, as any parameter. Thanks everyone!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文