我正在使用依赖于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 );
答案 0 :(得分:5)
尝试在调用内部require之前进行字符串化。您的问题是它在第二次扩展中传递给内部require,它扩展了NULL。如果您在此之前进行字符串化,例如在require宏中,它不会扩展NULL。
答案 1 :(得分:2)
以下是正在发生的事情:由于您应用“stringization”运算符#
的宏是第二级的,因此操作序列的工作方式如下:
REQUIRE(NULL)
的参数并执行argument substitution。此时,替换看起来像INTERNAL_REQUIRE( 0, "REQUIRE" )
,因为NULL
已扩展为0
。INTERNAL_REQUIRE
扩展宏链;此时,使用NULL
调用宏的事实将丢失:就预处理器而言,传递给INTERNAL_REQUIRE
的表达式为0
。解决这个问题的关键在于本段标准:
替换列表中的参数除非前面带有#或##预处理标记或后跟##预处理标记(见下文),否则在扩展其中包含的所有宏之后将被相应的参数替换。
这意味着如果您想捕获精确的表达式,则需要在宏扩展的第一级执行此操作。