GCC错误_Pragma如果出现在中间预处理步骤中会触发?

时间:2017-05-10 08:30:06

标签: c gcc c-preprocessor pragma

以下代码(如果使用gcc -E进行预处理)会从_Pragma("GCC error")产生一些错误:

_Pragma("GCC error \"ERROR\"") // error

#define MACRO_ERROR _Pragma("GCC error \"MACRO_ERROR\"")
MACRO_ERROR // error

#define VOID(arg)
VOID(_Pragma("GCC error \"VOID_ERROR\"")) // no error

#define MACRO_VOID_ERROR VOID(_Pragma("GCC error \"MACRO_VOID_ERROR\""))
MACRO_VOID_ERROR // no error

#define FORWARD(macro, arg) macro(arg)
FORWARD(VOID, _Pragma("GCC error \"FORWARD_VOID_ERROR\"")) // error

#define MACRO_FORWARD_VOID_ERROR FORWARD(VOID, _Pragma("GCC error \"MACRO_FORWARD_VOID_ERROR\""))
MACRO_FORWARD_VOID_ERROR // error

FORWARD(VOID, _Pragma("GCC error \"FORWARD_VOID_ERROR\""))MACRO_FORWARD_VOID_ERROR会产生错误,尽管最终扩展中的pragma运算符(为空)。

这是预期的行为吗?

相比之下,VOID(_Pragma("GCC error \"VOID_ERROR\""))MACRO_VOID_ERROR不会产生错误。看起来这是因为pragma算子被“预处理得足够快”了。

这背后的规则是什么?

以前,我假设如果pragma运算符只显示在中间扩展步骤中,则它没有效果。显然这是错误的,至少对于我的gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)

gcc -E的输出(删除空行):

# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "<stdin>"
# 1 "<stdin>"
<stdin>:1:11: error: ERROR
# 1 "<stdin>"
# 4 "<stdin>"
<stdin>:4:11: error: MACRO_ERROR
# 4 "<stdin>"
# 13 "<stdin>"
<stdin>:13:11: error: FORWARD_VOID_ERROR
# 13 "<stdin>"
# 16 "<stdin>"
<stdin>:16:11: error: MACRO_FORWARD_VOID_ERROR
# 16 "<stdin>"

1 个答案:

答案 0 :(得分:1)

这实际上是预期的(或至少,我期望的),虽然它是不直观的,因为你将表达水平的副作用混合成一种最初设计为完全没有副作用且部分懒惰的语言

参考C标准而不是GCC的文档,我们可以在6.10.3.1中找到以下内容:

  

替换列表中的参数,除非前面有###预处理令牌或后跟##预处理令牌(见下文),   在扩展了其中包含的所有宏之后,用相应的参数替换。在被替换之前,每个参数的预处理标记都被完全宏替换,好像它们形成了预处理文件的其余部分;没有其他预处理令牌可供使用。

这里模糊措辞的关键部分是标准只表示在被替换之前扩展了一个参数(“好像它形成了文件的其余部分”)。它没有明确地说如果一个参数根本不会被替换,那么它需要被扩展,因为该段的第一部分清楚地说明了如何处理一个参数取决于它实际上的上下文宏使用,这是一个公平的优化(非常需要处理复杂的元编程库,如Boost)。

“好像它们形成了文件的其余部分”是为什么在替换为参数之前执行_Pragma运算符的关键:5.1.1.2列出_Pragma执行为属于与宏替换相同的阶段,因此如果替换应用于给定的令牌序列,当参数替换时肯定是这样,那么应该执行_Pragma

所以_Pragma应该在FORWARD情况下运行,这完全不足为奇,因为它需要进行评估,并且应用了它的效果,选择VOID宏之前的两个步骤在重新扫描期间进行扩展。在直接调用VOID的情况下是否应该应用它可能是模糊的,但是由于标准明确地让宏决定如何根据参数的使用方式处理参数,所以它应该是有意义的允许完全 un 的参数完全不被评估,即使没有直接指定。