最近在研究 Redis-2.6.12 的源码时,重新对一处宏展开的用法进行了梳理,记录如下:
在 zmalloc.h 中有如下定义,
在注释中出现了一个专有名词“
stringification
”,经查阅,网络上比较认可的翻译是“字符串化”。所以上面双重宏定义的用途为“用于宏值字符串化的双重宏展开
”。这个概念似乎比较模糊,无法让我深刻理解其用意,幸好有下面的
资料
:
内容有点长,但确实是对
Stringification 作出了全面的解释。其中也对双重宏定义的用法进行了举例说明。针对 xstr(foo) 展开的情况,由于 xstr() 本身是一个符合普通宏展开定义的东东,而 foo 同样是这样的一个东东
,所以,在对
xstr(foo) 进行宏展开的时,会按照正常的展开顺序进行,即先展开 foo,再展开 xstr 。
至于采用二级宏定义的好处,当然就是可以更加灵活的对宏展开时的内容进行控制。
在 zmalloc.h 中有如下定义,
1
2
3
|
/* Double expansion needed for stringification of macro values. */
#define __xstr(s) __str(s)
#define __str(s) #s
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
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.
|
至于采用二级宏定义的好处,当然就是可以更加灵活的对宏展开时的内容进行控制。