在某些情况下,某些令牌序列未得到充分预处理。 例如:
#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);
}
}
在“好”情况下,DELAY(PRAGMA)
扩展为PRAGMA
,然后(与相邻的参数一起)扩展为_Pragma(...)
在“坏”情况下,DELAY(PRAGMA)
扩展为PRAGMA
,但是处理停止,并且PRAGMA
保留在输出中。
如果您获取“不良”输出并对其进行预处理(使用所有先前定义的宏),则该输出会正确展开。
唯一的区别是,DELAY(PRAGMA)
宏是'{'1'}宏的参数的一部分,而'坏'的情况不会将WRAP
传递给任何宏宏。如果在“不好”的情况下,我们改为单独使用DELAY(PRAGMA)
,则问题得以解决(在“再次好”的情况下)。
在“好”和“坏”情况下,行为不同的原因是什么?
答案 0 :(得分:3)
在糟糕的情况下,您打算作为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
的替换令牌x
中,DELAY
代替了x EMPTY()
。PRAGMA EMPTY()
进行替换。(foo)
及其后面的所有内容)。请注意,在这些标记中,PRAGMA
不是 :它不是替换EMPTY
所导致的标记的一部分。在WRAP(PRAGMA)(foo)
中,前五个步骤相同,其余步骤导致替换PRAGMA (foo)
:
PRAGMA
是x
的参数DELAY
,但是后面没有括号,因此它不是要替换的宏。PRAGMA
的替换令牌x
中,DELAY
代替了x EMPTY()
。PRAGMA EMPTY()
进行替换。(foo)
)。如上所述,PRAGMA
不在这些令牌中,因此不会被替换。WRAP
的参数的宏替换已经完成,产生了PRAGMA (foo)
。WRAP
的{{1}},从而产生{ BODY }
。{ PRAGMA (foo) }
出现在这些标记中,因此将其替换。