stringification of macro values

引言

       学习redis源码的过程中看到zmallo.h文件中有如下的宏定义:



       联想到项目中也使用了这个技巧,当时使用这个技巧的原因在于,项目中定义了一个宏#define MARCS xxx,如果直接使用,还有一个宏为#define SPS_PRINT(a) #a。如果不在SPS_PRINT宏外侧封装另外一层宏,那么直接调用SPS_PRINT(MARCS)时,会发现没有调用#xxx,而是直接将MARCS字符串化了,这显然不是我们预期的结果。因此,需要在SPS_PRINT(a)外封装第二个宏,这个宏什么也不做,直接调用SPS_PRINT(a),这样,第一层宏会将MARCS展开,将xxx传递给SPS_PRINT,接着SPS_PRINT做正确的字符串化操作。当时对这个只是一知半解,现在才知道他有个专业的名字--stringification of macro values,借着这个机会,来探索下该技巧的内部机制。

正文

       先来看一段网上搜索到的解释,

3.4 Stringification

Sometimes you may want to convert a macro argument into a string constant. Parameters are not replaced inside string constants, but you can use the ‘#’ preprocessing operator instead. When a macro parameter is used with a leading ‘#’, the preprocessor replaces it with the literal text of the actual argument, converted to a string constant. Unlike normal parameter replacement, the argument is not macro-expanded first. This is called stringification.
There is no way to combine an argument with surrounding text and stringify it all together. Instead, you can write a series of adjacent string constants and stringified arguments. The preprocessor will replace the stringified arguments with string constants. The C compiler will then combine all the adjacent string constants into one long string.
Here is an example of a macro definition that uses stringification:

     #define WARN_IF(EXP) \
     do { if (EXP) \
             fprintf (stderr, "Warning: " #EXP "\n"); } \
     while (0)
     WARN_IF (x == 0);
          ==> do { if (x == 0)
                fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);
The argument for EXP is substituted once, as-is, into the if statement, and once,stringified, into the argument to fprintf. If x were a macro, it would be expanded in the if statement, but not in the string.
The do and while (0) are a kludge to make it possible to write WARN_IF (arg);, which the resemblance of WARN_IF to a function would make C programmers want to do; see Swallowing the Semicolon.
Stringification in C involves more than putting double-quote characters around the fragment. The preprocessor backslash-escapes the quotes surrounding embedded string constants, and all backslashes within string and character constants, in order to get a valid C string constant with the proper contents. Thus, stringifying p = "foo\n"; 
results in "p = \"foo\\n\";". However, backslashes that are not inside string or character constants are not duplicated: ‘\n’ by itself stringifies to "\n".
All leading and trailing whitespace in text being stringified is ignored. Any sequence of whitespace in the middle of the text is converted to a single space in the stringified result. Comments are replaced by whitespace long before stringification happens, so they never appear in stringified text.
There is no way to convert a macro argument into a character constant.
If you want to stringify the result of expansion of a macro argument, you have to use two levels of macros.

     #define xstr(s) str(s)
     #define str(s) #s
     #define foo 4
     str (foo)
          ==> "foo"
     xstr (foo)
          ==> xstr (4)
          ==> str (4)
          ==> "4"
s is stringified when it is used in str, so it is not macro-expanded first. But s is an ordinary argument to xstr, so it is completely macro-expanded before xstr itself is expanded (see Argument Prescan). Therefore, by the time str gets to its argument, it has already been macro-expanded.

       其实说白了,就是预处理器在碰到#指令时,#的优先级会高于宏展开的优先级;所以需要额外定义一个外层宏,提前做好宏展开!

你可能感兴趣的:(stringification of macro values)