不需要的C预处理器宏扩展

时间:2012-06-19 17:15:55

标签: c++ macros c-preprocessor

我正在使用依赖于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 != 0。在构建断言消息文本之前,C预处理器扩展了NULL(定义为0的宏)。有没有办法可以忽略或绕过消息文本的扩展?

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

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

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

2 个答案:

答案 0 :(得分:5)

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

答案 1 :(得分:2)

以下是正在发生的事情:由于您应用“stringization”运算符#的宏是第二级的,因此操作序列的工作方式如下:

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

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

  

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

这意味着如果您想捕获精确的表达式,则需要在宏扩展的第一级执行此操作。