【问题标题】:Function-like macro definition in CC中的类函数宏定义
【发布时间】:2010-07-09 17:39:27
【问题描述】:

我想定义一个类似 MACRO 的函数。即

#define foo(x)\
#if x>32\
 x\
#else\
 (2*x)\
#endif

也就是说,

if x>32, then foo(x) present x
else, foo(x) present (2*x)

但我的 GCC 抱怨:

int a = foo(31);

我认为 C 预处理器应该正确处理这个问题。因为在编译时,它知道x=33。它可以用(2*33)替换foo(33)

【问题讨论】:

  • 请问您为什么需要一个宏?一个函数会更干净并且(类型)更安全。
  • 如果您对提出这个问题知之甚少,您应该忘记#define foo(x) 结构的存在,因为它只会帮助您编写代码,如果它是偶然的。
  • 我希望 C 预处理器进行评估而不是运行时流控制。这样可以在运行时节省时间

标签: c gcc c-preprocessor


【解决方案1】:

你可以如下

#define foo(x) ((x) > 32 ? (x) : (2 * (x)))

但这会多次评估x。您可以改为创建一个更简洁的静态函数

static int foo(int x) {
  if(x > 32) 
    return x;
  return 2 * x;
}

然后您也可以将具有副作用的东西传递给foo,并且副作用只发生一次。

您所写的是使用#if#else#endif 预处理器指令,但是如果您将变量传递给宏并想要评估它们的值,则需要使用语言构造。在实际语言结构中使用ifelse 语句也不起作用,因为控制流语句不计算值。换句话说,if 语句仅用于控制控制流(“如果 A,则执行 B,否则执行 C”),不计算任何值。

【讨论】:

  • 肯定 x 在这两种情况下都会被计算两次?
  • @Neil 参数(不是参数)在函数情况下只计算一次。尝试调用int i = 0; foo(i++); 对于功能案例,之后我将是1。但对于宏观情况,它将是2
  • 我希望 C 预处理器进行评估而不是运行时流控制。这样可以在运行时节省时间。顺便说一句,为什么你说“#define foo(x) ((x) > 32 ? (x) : (2 * (x))) 多次评估 x ”?
  • @richard:使函数内联 - 如果值在编译时已知,编译器通常会在编译时执行评估。即使对于未标记为inline 的静态函数,编译器也可能会这样做。您甚至可以使用更现代的编译器/链接器工具优化 extern 函数(但可能性降低)。使用 VS2008 进行的快速测试表明,它内联 litb 的 foo() 函数没有问题,即使它是在单独的源文件中定义的 extern。当传递字面量整数时,结果是在编译时计算的。
  • @richard:(继续) - 使用今天的工具,几乎不需要担心您的问题所涉及的“优化”类型。您通常不需要使用这些宏/预处理器技巧跳过箍来“在运行时节省时间”。首先以正确、可维护并使用高效算法的方式编写代码(注意:这与尝试计算周期不同)。只有当最初的尝试速度不够快时,才需要担心跳圈。
【解决方案2】:
#define \
    foo(x) \
    ({ \
        int xx = (x); \
        int result = (xx > 32) ? xx : (2*xx); \
        result; \
    })

【讨论】:

  • @Michael:当然!在带有 gcc 的 Ubuntu 10.04 上。 Linux 内核宏经常做同样的事情。我在我的一个 Linux C++ 项目中广泛使用了它。在我的代码 sn-p 中你觉得奇怪的是什么?
  • @Michael:在 Linux 源代码树上尝试 grep -C2 -rn '})' . | grep -v Documentation | less,您会发现很多地方都使用了相同的技术 :-)
  • @Vanni:我忘记了 GCC 有一个名为“语句表达式”的扩展。由于提出问题的人用gcc 标记了它,这对他来说可能没问题。但是,我认为一个简单的内联函数也可以工作并且更便携。
  • @Michael:感谢您指出这是一个 gcc 扩展!我不知道它的名字,也不知道它不是便携式的! :)
【解决方案3】:
int a = foo(31);

扩展到

int a = if 31>32
31
else
(2*31)
endif;

这就是 C 宏的工作方式,通过简单、愚蠢的替换。如果您期望 gcc 对它们进行更复杂或更智能的操作,那么您的期望是错误的。

鉴于此,很容易看出您的代码为何不起作用。对于这个例子来说,一个足够的替代方案是:

#define foo(x) (x > 32 ? x : 2*x)

另一方面,我会质疑宏是否真的是处理此类事情的合适工具。只要把它放在函数中,编译器就会内联代码,如果它认为它会加快速度。

【讨论】:

  • 我的答案中宏中的 x 应该像 Johannes 的宏一样用括号括起来。否则,复杂表达式将失败。这是避免使用宏的另一个原因 –简单、无害的错误可能会导致非常奇怪的错误。
【解决方案4】:

考虑:

int x = rand()
int y = foo( x );

x 在编译时是未知的。

【讨论】:

  • 编译器可以判断表达式是否为常量(例如,GCC 有 __builtin_constant_p),但是 ISO C 预处理器不能。
【解决方案5】:

问题不在于理论:假设您出于某种原因想要一个宏根据传递给它的参数的值进行不同的扩展,并且该参数是宏预处理器已知的常量,它没有理由不能工作......对于通用宏处理器......但不幸的是,cpp不允许在宏定义中出现其他宏处理器“命令”......

所以你的

#define foo(x) \
#if x>32 \
  x      \
#else    \
  2*x    \
#endif

不扩展为

#if X>32
  X
#else
  2*X
#endif

其中 X 是已知参数(因此将 X 更改为例如 31),这需要预处理器再次通过。

此外,换行符被忽略,虽然它们对于这种用途很重要;否则,以下可以被认为是一个技巧(但是需要另一个预处理通道)

#define foo(x,y) \
y if x>32 \
  x  \
y else \ 
  2*x \
y endif

foo(20,#) 产生

# if 20>32 20 # else 2*20 # endif

如果可行的话,哪个可行

# if 20>32
  20
# else
  2*20
# endif

...但事实并非如此(如前所述,预处理器的输出必须再次馈送到预处理器...)

所以我的回答是,如果你需要这些东西,你不能使用 C 预处理器;你应该使用一个不常见的(不是标准的?)C预处理器,或者只是另一个宏处理器,如果你需要“cpp”必须与C“集成”自身的东西,那么你不能使用通用的(像 M4) 这么容易...

【讨论】:

  • 问题不在于理论:假设您出于某种原因想要一个宏根据传递给它的参数的值进行不同的扩展,并且该参数是一个常量,宏预处理器已知。是的,你明白我的意思。但解决方案非常棘手。有没有其他办法?
  • 如前所述,不更改宏(预处理)处理器...目前我不知道具有您需要的功能的类似于 cpp 的宏处理器
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-09
  • 2012-04-28
相关资源
最近更新 更多