简体   繁体   English

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

[英]Unwanted C Preprocessor Macro Expansion

I'm using a unit test framework that relies on a REQUIRE macro for performing assertions. 我正在使用一个单元测试框架,它依赖于REQUIRE宏来执行断言。

Simplified, the macro works like this: 简化后,宏的工作原理如下:

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

Which is defined similar to this: 其定义与此类似:

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

PerformAssertion 's first two parameters are of the type: const char* . PerformAssertion的前两个参数类型为: const char* The reason for the second parameter ( #expr ) is so the exact expression that was asserted can be logged. 第二个参数( #expr )的原因是可以记录断言的确切表达式。 This is where the issue lies. 这就是问题所在。 The preprocessor expands the expression before it is passed as a const char * , so it's not the same expression that was originally asserted. 预处理器在将表达式作为const char *传递之前扩展表达式,因此它与最初声明的表达式不同。

For instance: 例如:

REQUIRE( foo != NULL );

Would result in this call: 会导致此次通话:

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

As you can see, the expression is partially expanded, eg the expression foo != NULL appears in the log as foo != 0 . 如您所见,表达式部分展开,例如表达式foo != NULL在日志中显示为foo != 0 The NULL (which is a macro defined to be 0 ) was expanded by the C preprocessor before building the assertions message text. 在构建断言消息文本之前,C预处理器扩展了NULL (这是一个定义为0的宏)。 Is there a way I can ignore or bypass the expansion for the message text? 有没有办法可以忽略或绕过消息文本的扩展?

EDIT: Here's the solution, for anyone curious: 编辑:这是解决方案,对任何好奇的人:

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

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

Try making the stringifying before the call to the internal require. 尝试在调用内部require之前进行字符串化。 Your problem is that it is passed to internal require in the second expansion which expands NULL. 您的问题是它在第二次扩展中传递给内部require,它扩展了NULL。 If you make the stringifying happen before that, eg In the require macro, it will not expand the NULL. 如果在此之前进行字符串化,例如在require宏中,它将不会扩展NULL。

Here is what's going on: since you macro where the "stringization" operator # is applied is second-level, the sequence of operations works as follows: 这是正在发生的事情:因为您应用“stringization”运算符#宏是二级的,所以操作序列的工作方式如下:

  • Preprocessor identifies the arguments of REQUIRE(NULL) and performs argument substitution as per C 6.10.3.1. 预处理器识别REQUIRE(NULL)的参数,并根据C 6.10.3.1执行参数替换 At this point, the replacement looks like INTERNAL_REQUIRE( 0, "REQUIRE" ) , because NULL is expanded as 0 . 此时,替换看起来像INTERNAL_REQUIRE( 0, "REQUIRE" ) ,因为NULL扩展为0
  • Preprocessor continues expanding the macro chain with INTERNAL_REQUIRE ; 预处理器继续使用INTERNAL_REQUIRE扩展宏链; at this point, the fact that the macro has been called with NULL is lost: as far as the preprocessor is concerned, the expression passed to INTERNAL_REQUIRE is 0 . 此时,宏已被NULL调用的事实将丢失:就预处理器而言,传递给INTERNAL_REQUIRE的表达式为0

A key to solving this problem is in this paragraph from the standard: 解决这个问题的关键在于本段标准:

A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. 替换列表中的参数除非前面带有#或##预处理标记或后跟##预处理标记(见下文),否则在扩展其中包含的所有宏之后,相应的参数将替换该参数。

This means that if you would like to capture the exact expression, you need to do it in the very first level of the macro expansion. 这意味着如果您想捕获精确的表达式,则需要在宏扩展的第一级执行此操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM