【问题标题】:Using a macro as an argument in an x-macro definition在 x 宏定义中使用宏作为参数
【发布时间】:2026-01-27 02:25:01
【问题描述】:

考虑以下用户样式x-macro

#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(5)

我们可以使用它来重复调用传入的宏func,并使用前三个素数。例如:

#define MAKE_FUNC(num) void foo ## num();
PRIMES_X(MAKE_FUNC)

将声明返回 void 的函数 foo2()foo3()foo5()

到目前为止,一切都很好。现在假设我想在 x-macro 本身的定义中使用一个宏作为参数,如下所示:

#define MAX_PRIME 5
#define PRIMES_X(func) \
  func(2) \
  func(3) \
  func(MAX_PRIME)

它不起作用,因为MAKE_FUNC 现在将尝试声明void fooMAX_PRIME(),因为(我想)令牌连接发生而不扩展MAX_PRIME

我可以解决这个问题,让它像以前一样声明 foo5() 吗?

【问题讨论】:

  • 顺便说一句,出于好奇,您打算将 x_macro 与 #undefs 一起使用吗?
  • @Yunnosch - 不,我目前不取消定义 MAKE_FUNC 之类的东西。用户表单的好处之一。
  • 没错。请问您从哪里获得了 x_macros 的 undef-free 版本?我问是因为前段时间我在 * 上声明了这项发明,要求参考任何“以前的艺术”。所以我对你的消息来源很感兴趣,即使这意味着我必须停止声明它。
  • 这里是the link。这篇文章不是 Andrei 写的,但请看底部的评论:当然,您可能已经在使用名为 X 的宏或变量,而 X 是硬编码在宏体中的。 Andrei Alexandrescu 建议进行以下改进,其中 X 宏本身就是一个参数...,但除此之外没有参考线索。评论线程也很有趣,人们指出 x-macros 有时会丢失。我认为他们至少是半主流的,因为他们有一个*页面......@Yunnosch
  • 谢谢,以后再研究。

标签: c c-preprocessor x-macros


【解决方案1】:

您可以插入另一个级别的宏扩展(PRIMES_X2 下面)。

#define MAKE_FUNC(num) void foo ## num();
#define MAX_PRIME 5
#define PRIMES_X(func) PRIMES_X2(func, MAX_PRIME)
#define PRIMES_X2(func, maxPrimePar) \
  func(2) \
  func(3) \
  func(maxPrimePar)

PRIMES_X(MAKE_FUNC)

输出gcc -E:

void foo2(); void foo3(); void foo5();

【讨论】:

【解决方案2】:

Yunnosch 的回答很好,但要进一步旋转 X 宏的疯狂,您也可以使用列表内部的宏调用来执行此操作,而不是在列表外部使用包装宏。这样做的好处是您可以将列表中的“变量”传递给被调用的宏。

我想这可能有一些用处 - 例如,假设您希望使用 X 宏来声明不同类型的函数?

例子:

#define MAX_PRIME 5

#define CREATE_FUNC(func, ret_type, param) func(ret_type, param)

#define PRIMES_X(func)                 \
  CREATE_FUNC(func, int,    2)         \
  CREATE_FUNC(func, void,   3)         \
  CREATE_FUNC(func, double, MAX_PRIME) \

#define MAKE_FUNC(ret_type, num) ret_type foo ## num(void);
  PRIMES_X(MAKE_FUNC)
#undef MAKE_FUNC

调试代码以检查函数确实得到了预期的原型:

int main(void)
{
  (void)foo2();
  foo3();
  (void)foo5();
}

int foo2 (void){ return 0;}
void foo3 (void){}
double foo5 (void){ return 0.0;}

【讨论】:

  • 谢谢,这项技术也有效!不过,我不太了解相对于其他方法的好处 - 使用其他方法,您可以通过修改 PRIMES_X2 来调用 func(int, 2) \ func(void, 3)... 来轻松传递 ret_type 参数,对吗?
  • @BeeOnRope 这个版本的好处是类型是 X 宏列表的一部分,而不是调用者代码。 X 宏的全部目的是将所有维护集中到一个列表中——这个版本通过让 X 宏列表确定使用的类型来实现这一点。看起来很方便,但如果可能的话,我当然会首先避免创建函数的宏。
  • 是的,我明白了,但你不能用另一个答案的形式做同样的事情吗?具体来说,将类型添加到PRIMES_X2 列表中。我不明白这种方法如何允许这样做,而另一种则不允许。