C 語言 stringify 技巧

一般 #define macro 不會將 parameter 展開成字串,只會把 parameter 放到對應位置,例如:

php
1
2
<?php
echo "Hello world";
1
2
3
4
5
6
#define ECHO(str) printf("%s\n", str)
int main() {
char s[] = "hello";
ECHO(s);
return 0;
}

經過 preprocess:

1
2
3
4
5
6
7
$ cpp stringify.c

int main() {
char s[] = "hello";
printf("%s\n", s);
return 0;
}

如果在 parameter 前加 #,preprocessor 會把 actual argument 變成字串,稱為 Stringizing。用個例子說明:

1
2
3
4
5
6
#define ECHO(str) printf("%s\n", #str)
int main() {
char s[] = "hello";
ECHO(s);
return 0;
}

經過 preprocess(只是例子,code 本身不太 make sense):

1
2
3
4
5
int main() {
char s[] = "hello";
printf("%s\n", "s");
return 0;
}

Linux kernel 使用這個技巧將 macro 展開成字串。

__stringify 定義在 include/linux/stringify.h

1
2
#define __stringify_1(x...)	#x
#define __stringify(x...) __stringify_1(x)

為什麼 __stringify 要 define 兩次呢?

Argument Prescan 提到 macro 的參數如果也是 macro,一般在被替換進 macro body 前就會被展開,但如果是 stringized 則不會被展開。

1
2
3
4
5
6
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
#define FOO bar

__stringify_1(FOO) // become "FOO"
__stringify(FOO) // become "bar"

這例子裡 __stringify_1(FOO) 因為是 stringized 的 macro 參數,所以 FOO 不會被展開,macro 替換後最後變成 "FOO"。而 __stringify(FOO) 會先展開 FOO 並替換,變成 __stringify_1(bar),接著再 scan 一次將 macro 展開為 "bar"

__stringify define 兩次是為了讓參數可以使用 macro。像上面的例子,通常期望 __stringify(FOO) 得到 "bar" 而非 "FOO"

... 是跟 function 一樣的不定參數,可參考 Variadic Macros