引入#
在C++
中經常打印變數來調試代碼,但無論是printf
還是cout
總是很麻煩:
-
printf
int a = 1; float b = 2.0; char c = 'c'; printf("a = %d, b = %f, c = %c", a, b, c);
-
cout
int a = 1; float b = 2.0; char c = 'c'; std::cout << "a = " << a << ", b = " << b << ", c = " << c << '\n';
可變參數宏#
可變參數宏是C99
引入的一個特性,C++11
開始支持。
#define def_name(...) def_body(__VA_ARGS__)
可變參數模板#
C++11
允許模板定義有任意類型任意數量的模板參數(包括 $0$ 個模板參數)。
template<typename... Values> class tuple;
如果不希望有 $0$ 個模板實參,可以如下聲明:
template<typename First, typename... Rest> class tuple;
可變參數模板適用於函數模板,例如printf
函數的實現:
template<typename... Args>
void printf(const std::string &str_format, Args... args);
參數包的展開#
- 對於模板函數
- 遞歸展開
// 遞歸終止函數 void print() {} template<typename T, typename... Args> void print(T head, Args... args) { std::cout << head << (sizeof...(args) > 0 ? ", " : ""); print(args...); }
- 逗號表達式展開:這種方式有點繁瑣,不太好理解,不推薦
- 對於模板類
- 遞歸展開
- 繼承展開
宏運算符#
-
#
:將宏參數轉換為字符串#include <stdio.h> #define stringer( x ) printf( #x "\n" ) int main() { stringer( In quotes in the printf function call ); stringer( "In quotes when printed to the screen" ); stringer( "This: \" prints an escaped double quote" ); }
使用
gcc -E
預處理之後,得到如下結果int main() { printf("In quotes in the printf function call" "\n"); printf("\"In quotes when printed to the screen\"" "\n"); printf("\"This: \\\" prints an escaped double quote\"" "\n"); }
-
#@
:將參數用單引號括起來,作為一個字符處理#define makechar(x) #@x c = makechar(b);
預處理之後得到:
c = 'b';
-
##
:將兩個獨立的token
連接在一起#define paster(n) printf("token" #n " = %d", token##n) int token9 = 9; paster(9);
預處理之後得到:
printf("token9 = %d", token9);
實現DBG
宏#
掌握了以上技能,我們就可以實現一個簡單的調試宏:
#include <iostream>
#include <string>
template <typename T>
void _DBG(const char *s, T t) {
std::cout << s << "=" << t << '\n';
}
template <typename T, typename... Args>
void _DBG(const char *s, T t, Args... args) {
while (*s != ',')
std::cout << *s++;
std::cout << "=" << t << ",";
_DBG(s + 1, args...);
}
#define DBG(...) _DBG(#__VA_ARGS__, __VA_ARGS__)
int main() {
int a = 1;
float b = 2.0;
char c = 'c';
std::string d = "def";
DBG(a, b, c, d);
return 0;
}
上面的代碼輸出:
a=1, b=2, c=c, d=def