为什么某些预处理器宏不能扩展,除非它们是另一个宏的参数?

时间:2019-07-26 21:39:06

标签: c macros c-preprocessor preprocessor pragma

在某些情况下,某些令牌序列未得到充分预处理。 例如:

#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),则问题得以解决(在“再次好”的情况下)。

在“好”和“坏”情况下,行为不同的原因是什么?

1 个答案:

答案 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)中:

  1. PRAGMAx的参数DELAY,但是后面没有括号,因此它不是要替换的宏。
  2. PRAGMA的替换令牌x中,
  3. DELAY代替了x EMPTY()
  4. 扫描结果PRAGMA EMPTY()进行替换。
  5. 空无一物。
  6. 将扫描EMPTY的替换结果以及随后的标记((foo)及其后面的所有内容)。请注意,在这些标记中,PRAGMA不是 :它不是替换EMPTY所导致的标记的一部分。
  7. 宏替换已完成。

WRAP(PRAGMA)(foo)中,前五个步骤相同,其余步骤导致替换PRAGMA (foo)

  1. PRAGMAx的参数DELAY,但是后面没有括号,因此它不是要替换的宏。
  2. PRAGMA的替换令牌x中,
  3. DELAY代替了x EMPTY()
  4. 扫描结果PRAGMA EMPTY()进行替换。
  5. 空无一物。
  6. 将扫描EMPTY的替换结果以及后续标记((foo))。如上所述,PRAGMA不在这些令牌中,因此不会被替换。
  7. WRAP的参数的宏替换已经完成,产生了PRAGMA (foo)
  8. 参数中的这些标记被替换为WRAP的{​​{1}},从而产生{ BODY }
  9. 将重新扫描这些令牌(以及源文件中的以下令牌)以进行进一步替换。现在{ PRAGMA (foo) }出现在这些标记中,因此将其替换。