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

伊恩·贝托拉奇(Ian Bertolacci)

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

#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 -fopenmpgcc 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)WRAP参数的一部分,而“坏”情况不会传递DELAY(PRAGMA)给任何宏。如果在“不好”的情况下,我们改为PRAGMA单独使用,则问题得到解决(如在“再次好”的情况下)。

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

埃里克·波斯蒂奇(Eric Postpischil)

在最坏的情况下,您打算成为参数的参数将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. PRAGMA为参数xDELAY,但后面没有括号,所以它不是一个宏替换。
  2. PRAGMA被替换为xinDELAY的替换令牌x EMPTY()
  3. PRAGMA EMPTY()扫描结果进行替换。
  4. 空无一物。
  5. (foo)扫描EMPTY的替换结果以及随后的标记(之后的所有标记)。请注意,PRAGMA不是在这些令牌:这是不是源于更换令牌的一部分EMPTY
  6. 宏替换已完成。

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

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

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

调用另一个宏的预处理器宏:MSVC问题?

使用预处理器宏来编写另一个宏调用

使用Autoconf在另一个库中检测预处理器宏

如何使用另一个定义使 if/else 预处理器宏无效?

C预处理器宏扩展

为什么在 C 预处理器宏中嵌套扩展与串联失败?

为什么不能从一个处理器直接访问另一个处理器的缓存?

我可以验证预处理器宏以确保它是一个数字吗?

如何将C宏的某些参数定义为另一个宏?

使用##预处理程序运算符与另一个宏输出

C 预处理器宏扩展 - 使用计算

C ++停止预处理器宏扩展

如何在宏中扩展预处理器变量?

为什么从宏获取的一个函数起作用而不能编译另一个函数?

用于测试是否正在构建应用程序扩展的预处理器宏是什么?

C预处理器宏参数中的令牌检测

为什么我的 .exe 文件可以在另一个处理器上运行

Rust宏可以像十六进制数字的C预处理器宏一样扩展吗?

我们可以定义一个预处理器宏,该宏根据计时器打印调试消息吗?

为什么__func__,__FUNCTION__和__PRETTY_FUNCTION__不是预处理器宏?

为什么预处理器宏是邪恶的,还有哪些替代方法?

为什么我的cocoapods post_install钩子不更新我的预处理器宏?

为什么预处理器宏会忽略括号中的语句

为什么预处理器禁止宏粘贴奇怪的标记

如何使预处理器宏贪婪?

Apple Watch的预处理器宏?

预处理器存储的宏定义

从预处理器宏包含

预处理器宏问题