這一篇主要是CPP manual第三章macro的心得,所以都是在講述macro。
object-like macro是最簡單的macro,就是直接將macro的name取代成code。
如:
#define NUMBER 1, \
2, \
3,
int x[] = {NUMBER}
-> int x[] = { 1, 2 3 };
NUMBER會被展開誠1, 2, 3
當macro被展開之後,會再度的被檢驗是否還有macro需要被展開。
如:
#define TABLESIZE BUFSIZE
#define BUFSIZE 1024
TABLESIZE
-> BUFSIZE
-> 1024
TABLESIZE會先被展開成BUFSIZE,接著BUFSIZE再被展成1024。
Function-like macro用起來就像在用function,所以被稱為function-like macro。function-like macro是在macro name後面"緊"接括號。
如:
#define lang_init() c_init()
lang_init()
-> c_init()
當macro name後面緊接著(),這個macro才會被當成function-like macro,否則會當成一般macro展開。
如:
extern void foo(void);
#define foo() boo()
....
foo(); // macro
funcptr = foo; // function
#define lang_init () c_init() // 多了空白在name和()之間
lang_init()
-> () c_init()() // 結果就會造成錯誤
macro arguments,function macro可以像一般function一帶參數,這些參數會先被展開,接著再展開macro,參數要以","區隔開來(和一般的function一樣)。
如:
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
x = min(a + 28, *p)
-> x = ((a + 28), (*p) ? (a + 28) : (*p));
y = min(min(a, b), c)
-> min(((a) < (b) ? (a) : (b)), c)
-> .... // 產生的結果可能會和你想的不一樣
stringification(中文不知道怎麼稱呼,暫稱他字串化吧),當macro所帶的參數前面加上"#"之後,這個參數就會被展成字串,這就是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);
目前只有stringification,並沒有辦法轉成character。另外,stringification的優先權會高於argument expand。
如:
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
-> "foo"
xstr (foo)
-> xstr (4)
-> str (4)
-> "4"
token concatenation或稱
token pasting,是利用"##"將兩個token組成一個token,'##'被置於兩個token之間,如:
#define COMMAND(name) {#name, name ## _command}
struct command {
char *name;
void (*f)(void);
} cmds[] = {
COMMAND(quit),
COMMAND(help),
};
->
struct command {
char *name;
void (*f)(void);
} cmds[] = {
{"quit", quit_command},
{"help", help_command},
};
variadic其實就是不定參數的function-like macro,__VA_ARGS__代表"..."(即後面所有的參數),如:
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
eprintf("%s(#%d)\n", __FUNCTION__, __LINE__)
-> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)
cpp也可以使用name取代__VA_ARGS__,用法上只是在"..."前面加上name,"_VA_ARGS__"則用name取代即可,如:
#define eprintf(args...) fprintf(stderr, args)
eprintf("%s(#%d)\n", __FUNCTION__, __LINE__)
-> fprintf(stderr, "%s(#%d)\n", __FUNCTION__, 7)
來看另外一個例子
#define eprintf(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
eprintf("hello\n");
-> fprintf(stderr, "hello\n", ); // error!!
這裡的問題就是如何將__VA_ARGS__前面的","在沒有參數帶入的時候刪除,答案就是"##"。
#define eprintf(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
eprintf("hello\n");
-> fprintf(stderr, "hello\n"); // correct!!