为什么某些预处理器宏不展开,除非它们是另一个宏的参数?
Why are some preprocessor macros not expanded unless they are arguments to another macro?
在某些情况下,某些标记序列未完全预处理。
例如:
#define EMPTY()
#define DELAY(x) x EMPTY()
#define PRAGMA(args) _Pragma(args)
#define WRAP( BODY ) { BODY }
#define LOOP_Good( body, i, LB, UB ) \
WRAP( \
DELAY(PRAGMA)("omp parallel for") \
for( i = LB; i < UB; ++i ){ \
body \
} \
)
#define LOOP_Bad( body, i, LB, UB ) \
{ \
DELAY(PRAGMA)("omp parallel for") \
for( i = LB; i < UB; ++i ){ \
body \
} \
}
#define LOOP_Good_Again( body, i, LB, UB ) \
{ \
PRAGMA("omp parallel for") \
for( i = LB; i < UB; ++i ){ \
body \
} \
}
// Good
int i;
int lower_i = 0;
int upper_i = 10;
LOOP_Good( printf("%d\n", i);, i, lower_i, upper_i )
// Bad
LOOP_Bad( printf("%d\n", i);, i, lower_i, upper_i )
// Good again
LOOP_Good_Again( printf("%d\n", i);, i, lower_i, upper_i )
其中(使用 -E -fopenmp
gcc 9.1)扩展到以下内容(带格式):
int i;
int lower_i = 0;
int upper_i = 10;
// Good
{
#pragma omp parallel for
for( i = lower_i; i < upper_i; ++i ){
printf("%d\n", i);
}
}
// Bad
{
PRAGMA ("omp parallel for")
for( i = lower_i; i < upper_i; ++i ){
printf("%d\n", i);
}
}
// Good again
{
#pragma omp parallel for
for( i = lower_i; i < upper_i; ++i ){
printf("%d\n", i);
}
}
在 'good' 的情况下,DELAY(PRAGMA)
扩展为 PRAGMA
,然后扩展(使用相邻参数)为 _Pragma(...)
在 'bad' 的情况下,DELAY(PRAGMA)
扩展为 PRAGMA
但处理停止并且 PRAGMA
留在输出中。
如果您获取 'bad' 输出并重新处理它(使用所有先前定义的宏),它会正确扩展。
唯一的区别是 'good' 情况,DELAY(PRAGMA)
是 WRAP
宏参数的一部分,而 'bad' 情况不通过 DELAY(PRAGMA)
到任何宏。如果在 'bad' 的情况下,我们单独使用 PRAGMA
,问题就解决了(如 'good again' 的情况)。
'good' 和 'bad' 案例中不同行为的原因是什么?
在糟糕的情况下,您打算作为 PRAGMA
的参数的内容永远不会与 PRAGMA
一起出现在扫描宏替换的标记中。
我们可以忽略LOOP_xxx
宏;它们只是简单地扩展为各种标记,并且处理生成的标记就像它们正常出现在源文件中一样。我们可以只考虑 DELAY(PRAGMA)(foo)
和 WRAP(DELAY(PRAGMA)(foo)
.
根据 C 2018 6.10.3.1 和 6.10.3.4,处理宏的参数以进行宏替换,然后将生成的标记替换为宏的替换标记,然后生成的标记和源文件的后续标记重新扫描以进行进一步替换。 (处理宏参数的标记时,它们被视为构成整个源文件。)
在DELAY(PRAGMA)(foo)
中:
PRAGMA
是x
到DELAY
的参数,但是后面没有括号,所以不是宏来替换。
PRAGMA
在 DELAY
的替换标记 x EMPTY()
中替换了 x
。
- 扫描结果
PRAGMA EMPTY()
以进行替换。
- EMPTY 被替换为空。
- 替换 EMPTY 的结果,以及后续标记(
(foo)
,以及它后面的任何内容)都会被扫描。请注意 PRAGMA
在这些标记中是 而不是 :它不是由替换 EMPTY
. 产生的标记的一部分
- 宏替换完成。
在WRAP(PRAGMA)(foo)
中,前五步相同,其余步骤结果替换为PRAGMA (foo)
:
PRAGMA
是x
到DELAY
的参数,但是后面没有括号,所以不是宏来替换。
PRAGMA
在 DELAY
的替换标记 x EMPTY()
中替换了 x
。
- 扫描结果
PRAGMA EMPTY()
以进行替换。
- EMPTY 被替换为空。
- 扫描 EMPTY 替换的结果以及后续标记 (
(foo)
)。如上,PRAGMA
不在这些token中,所以不替换。
WRAP
参数的宏替换已完成,生成了 PRAGMA (foo)
。
- 参数中的这些标记被代入
WRAP
的 { BODY }
,生成 { PRAGMA (foo) }
.
- 重新扫描这些标记(以及源文件中的以下标记)以进行进一步替换。现在
PRAGMA (foo)
出现在这些标记中,所以它被替换了。
在某些情况下,某些标记序列未完全预处理。 例如:
#define EMPTY()
#define DELAY(x) x EMPTY()
#define PRAGMA(args) _Pragma(args)
#define WRAP( BODY ) { BODY }
#define LOOP_Good( body, i, LB, UB ) \
WRAP( \
DELAY(PRAGMA)("omp parallel for") \
for( i = LB; i < UB; ++i ){ \
body \
} \
)
#define LOOP_Bad( body, i, LB, UB ) \
{ \
DELAY(PRAGMA)("omp parallel for") \
for( i = LB; i < UB; ++i ){ \
body \
} \
}
#define LOOP_Good_Again( body, i, LB, UB ) \
{ \
PRAGMA("omp parallel for") \
for( i = LB; i < UB; ++i ){ \
body \
} \
}
// Good
int i;
int lower_i = 0;
int upper_i = 10;
LOOP_Good( printf("%d\n", i);, i, lower_i, upper_i )
// Bad
LOOP_Bad( printf("%d\n", i);, i, lower_i, upper_i )
// Good again
LOOP_Good_Again( printf("%d\n", i);, i, lower_i, upper_i )
其中(使用 -E -fopenmp
gcc 9.1)扩展到以下内容(带格式):
int i;
int lower_i = 0;
int upper_i = 10;
// Good
{
#pragma omp parallel for
for( i = lower_i; i < upper_i; ++i ){
printf("%d\n", i);
}
}
// Bad
{
PRAGMA ("omp parallel for")
for( i = lower_i; i < upper_i; ++i ){
printf("%d\n", i);
}
}
// Good again
{
#pragma omp parallel for
for( i = lower_i; i < upper_i; ++i ){
printf("%d\n", i);
}
}
在 'good' 的情况下,DELAY(PRAGMA)
扩展为 PRAGMA
,然后扩展(使用相邻参数)为 _Pragma(...)
在 'bad' 的情况下,DELAY(PRAGMA)
扩展为 PRAGMA
但处理停止并且 PRAGMA
留在输出中。
如果您获取 'bad' 输出并重新处理它(使用所有先前定义的宏),它会正确扩展。
唯一的区别是 'good' 情况,DELAY(PRAGMA)
是 WRAP
宏参数的一部分,而 'bad' 情况不通过 DELAY(PRAGMA)
到任何宏。如果在 'bad' 的情况下,我们单独使用 PRAGMA
,问题就解决了(如 'good again' 的情况)。
'good' 和 'bad' 案例中不同行为的原因是什么?
在糟糕的情况下,您打算作为 PRAGMA
的参数的内容永远不会与 PRAGMA
一起出现在扫描宏替换的标记中。
我们可以忽略LOOP_xxx
宏;它们只是简单地扩展为各种标记,并且处理生成的标记就像它们正常出现在源文件中一样。我们可以只考虑 DELAY(PRAGMA)(foo)
和 WRAP(DELAY(PRAGMA)(foo)
.
根据 C 2018 6.10.3.1 和 6.10.3.4,处理宏的参数以进行宏替换,然后将生成的标记替换为宏的替换标记,然后生成的标记和源文件的后续标记重新扫描以进行进一步替换。 (处理宏参数的标记时,它们被视为构成整个源文件。)
在DELAY(PRAGMA)(foo)
中:
PRAGMA
是x
到DELAY
的参数,但是后面没有括号,所以不是宏来替换。PRAGMA
在DELAY
的替换标记x EMPTY()
中替换了x
。- 扫描结果
PRAGMA EMPTY()
以进行替换。 - EMPTY 被替换为空。
- 替换 EMPTY 的结果,以及后续标记(
(foo)
,以及它后面的任何内容)都会被扫描。请注意PRAGMA
在这些标记中是 而不是 :它不是由替换EMPTY
. 产生的标记的一部分
- 宏替换完成。
在WRAP(PRAGMA)(foo)
中,前五步相同,其余步骤结果替换为PRAGMA (foo)
:
PRAGMA
是x
到DELAY
的参数,但是后面没有括号,所以不是宏来替换。PRAGMA
在DELAY
的替换标记x EMPTY()
中替换了x
。- 扫描结果
PRAGMA EMPTY()
以进行替换。 - EMPTY 被替换为空。
- 扫描 EMPTY 替换的结果以及后续标记 (
(foo)
)。如上,PRAGMA
不在这些token中,所以不替换。 WRAP
参数的宏替换已完成,生成了PRAGMA (foo)
。- 参数中的这些标记被代入
WRAP
的{ BODY }
,生成{ PRAGMA (foo) }
. - 重新扫描这些标记(以及源文件中的以下标记)以进行进一步替换。现在
PRAGMA (foo)
出现在这些标记中,所以它被替换了。