【问题标题】:Unwanted C Preprocessor Macro Expansion不需要的 C 预处理器宏扩展
【发布时间】:2012-06-19 17:15:55
【问题描述】:

我正在使用一个单元测试框架,该框架依赖于 REQUIRE 宏来执行断言。

简化,宏的工作方式如下:

#define REQUIRE( expr ) INTERNAL_REQUIRE( expr, "REQUIRE" )

其定义与此类似:

#define INTERNAL_REQUIRE( expr, macroName ) \
PerformAssertion( macroName, #expr, expr );

PerformAssertion 的前两个参数的类型为:const char*。第二个参数 (#expr) 的原因是可以记录断言的确切表达式。这就是问题所在。预处理器在将表达式作为const char * 传递之前对其进行扩展,因此它与最初断言的表达式不同。

例如:

REQUIRE( foo != NULL );

将导致此调用:

PerformAssertion( "REQUIRE", "foo != 0", foo != 0 );

如您所见,表达式被部分扩展,例如表达式 foo != NULL 在日志中显示为 foo != 0NULL(定义为 0 的宏)在构建断言消息文本之前由 C 预处理器扩展。有没有办法可以忽略或绕过消息文本的扩展?

编辑:对于任何好奇的人来说,这是解决方案:

#define REQUIRE( expr ) INTERNAL_REQUIRE( expr, #expr, "REQUIRE" )

#define INTERNAL_REQUIRE( expr, exprString, macroName ) \
PerformAssertion( macroName, exprString, expr );

【问题讨论】:

  • 只使用INTERNAL_REQUIRE(expr, #expr, "REQUIRE")而不是两个参数的形式?

标签: c++ macros c-preprocessor


【解决方案1】:

尝试在调用内部需求之前进行字符串化。您的问题是它在扩展 NULL 的第二次扩展中传递给内部要求。如果您在此之前进行字符串化,例如在require宏中,它不会扩展NULL。

【讨论】:

  • 完美运行,谢谢!您愿意更详细地解释一下它是如何工作的吗?
【解决方案2】:

这里是怎么回事:因为你应用“字符串化”操作符#的宏是二级宏,操作顺序如下:

  • 预处理器识别REQUIRE(NULL) 的参数并按照C 6.10.3.1 执行argument substitution。此时,替换看起来像INTERNAL_REQUIRE( 0, "REQUIRE" ),因为NULL 扩展为0
  • 预处理器继续用INTERNAL_REQUIRE扩展宏链;此时,使用NULL 调用宏的事实丢失了:就预处理器而言,传递给INTERNAL_REQUIRE 的表达式是0

解决这个问题的关键在于标准的这一段:

替换列表中的参数,除非前面有 # 或 ## 预处理标记或后跟 ## 预处理标记(见下文),在其中包含的所有宏都已展开后,将被相应的参数替换。

这意味着,如果您想捕获确切的表达式,则需要在宏展开的第一级进行。

【讨论】:

  • 谢谢,这满足了我的好奇心并解释了 Dani 的解决方案为何有效。
猜你喜欢
  • 2018-01-04
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 2014-04-17
  • 2020-09-18
  • 2011-03-01
  • 1970-01-01
相关资源
最近更新 更多