プログラム書いていて、途中結果を出力したいときってありますよね。うんうん。
今まで
こんな感じでやってました
#include <bits/stdc++.h> using namespace std; #define debug(x) cout<<#x<<" = "<<(x)<<endl #define debug2(x,y) cout<<"("<<#x<<","<<#y<<") = ("<<(x)<<","<<(y)<<")"<<endl #define debug3(x,y,z) cout<<"("<<#x<<","<<#y<<","<<#z<<") = ("<<(x)<<","<<(y)<<","<<(z)<<")"<<endl int main(){ int a = 10; double b = 3.14; string c = "hoge"; debug(a); debug2(a,b); debug3(a,b,c); }
出力はこんな感じ
a = 10 (a,b) = (10,3.14) (a,b,c) = (10,3.14,hoge)
ここで使っているのが、「文字列化演算子」#
です。変数の名前自体を文字列に変換して出力できるので、どの変数がどの値なのかわかりやすいです。
可変引数テンプレート
ただ、変数の数ごとに使い分けをしているのがアホらしいですね。ここで、どこかで小耳に挟んだ「可変引数テンプレート」を使います。
パラメータパックを最初の要素から順番に処理していきたい場合には、「任意の型のパラメータをひとつと、任意の個数の任意の型のパラメータを受け取る」というような形式のパラメータリストとし、再帰によって処理をする:
可変引数テンプレート - cpprefjp C++日本語リファレンス
とのことなのでそこに書いてあるのをほぼコピペして書きます。
追記 : 改良しました。今までは改行の手前に余計なが入っていたけどなんか気持ち悪いので引数1つになったところで分岐(?)するようにしました(これ以降はそのままになっていますので各自書き換えをm( )m)
//旧 void debug(){cout<<endl;} //引数がなくなったときのためのdebug() template <class H, class... Ts> void debug(H&& h, Ts&&... ts){ cout<<h<<" "; debug(forward<Ts>(ts)...); } //新 template <class H> void debug(H&& h){ cout<<h<<endl;; } template <class H, class... Ts> void debug(H&& h, Ts&&... ts){ cout<<h<<" "; debug(forward<Ts>(ts)...); }
int main(){ int a = 10; double b = 3.14; string c = "hoge"; debug(a); //10 debug(a,b); //10 3.14 debug(a,b,c); //10 3.14 hoge }
右辺値参照&&
とかfoward()
とかは、よくわかんなかったけど動くからええかって…………(一応参考: forward - cpprefjp C++日本語リファレンス, 右辺値参照・ムーブセマンティクス - cpprefjp C++日本語リファレンス)
テンプレートで対応
pair
このままだとpair
を出力できないので、cout<<h<<" ";
のところをテンプレートで書き換えます
template <class T> void show(T &x){cout<<x<<" ";} template <class P, class Q> void show(pair<P, Q> &x){ cout<<"("<<x.first<<", "<<x.second<<") "; } void debug(){cout<<endl;} //引数がなくなったときのためのdebug() template <class H, class... Ts> void debug(H&& h, Ts&&... ts){ show(h); debug(forward<Ts>(ts)...); }
int main(){ int a = 10; double b = 3.14; string c = "hoge"; debug(make_pair(a,b)); //(10, 3.14) debug(make_pair(b,c)); //(3.14, hoge) }
vector
vectorを突っ込んで出力したいので。
template <class T> void debug(vector<T> &vt){ for(auto x: vt){show(x);}cout<<endl; }
int main(){ vector<int> vi = {1,2,3,4,5}; debug(vi); //1 2 3 4 5 }
初期化子リスト
debug({1, 3, 5});
ってやりたいなって(でもこれ型合わせなきゃいけないとかであんまりデバッグ的に意味はない。{}
に入れなくて良いじゃん)。
template <class T> void debug(initializer_list<T> init){ for(auto x: init){show(x);}cout<<endl; }
int main(){ debug({1,3,5}); //1 3 5 }
行番号出したい
どこでバグってるか多少わかりやすくするために行番号を出力するようにします。
#define ldebug(...) do{cout<<"["<<setw(3)<<__LINE__<<"] "; debug(__VA_ARGS__);}while(0)
- マクロの引数
(...)
は可変引数マクロで、マクロ内では__VA_ARGS__
の位置に展開されます。 __LINE__
はプリプロセッサの段階で行数に置換されます。setw(3)
はcout
の書式設定で、printf("%3d")
に対応しています。- do-while文にしているのは、マクロで複文を書くときの常套手段です
やっぱり変数名も出したい
最初に書いたみたいに変数名表示したいですよね。ええ。
#define DEBUG(...) do{cout<<#__VA_ARGS__<<" = "; debug(__VA_ARGS__);}while(0)
- 先ほどの
__VA_ARGS__
に、冒頭で紹介した「文字列化演算子」#
を合わせます
行番号も変数名も出したい
欲張り。
#define lDEBUG(...) do{cout<<"["<<setw(3)<<__LINE__<<"] "<<#__VA_ARGS__<<" = "; debug(__VA_ARGS__);}while(0)
まとめ
今までのをまとめました(冒頭の追記の思想(余計なを出力しない)で結構書き換えてあります)
まともな使用例
/* debugのアレコレ */ int main(){ for(int i=1; i<=3; i++){ for(int j=4; j<=6; j++){ DEBUG(i,j,i+j,i*j); } } }
i,j,i+j,i*j = 1 4 5 4 i,j,i+j,i*j = 1 5 6 5 i,j,i+j,i*j = 1 6 7 6 i,j,i+j,i*j = 2 4 6 8 i,j,i+j,i*j = 2 5 7 10 i,j,i+j,i*j = 2 6 8 12 i,j,i+j,i*j = 3 4 7 12 i,j,i+j,i*j = 3 5 8 15 i,j,i+j,i*j = 3 6 9 18
参考
- #と##演算子
- テキストマクロの置換 - cppreference.com
- 可変引数テンプレート - cpprefjp C++日本語リファレンス
- 適当なC++テンプレート入門 - Qiita
- 初期化子リスト - cpprefjp C++日本語リファレンス
- initializer_list - cpprefjp C++日本語リファレンス
- 可変引数マクロ - cpprefjp C++日本語リファレンス
- C言語の__LINE__マクロをプリプロセッサの段階で文字列に変換する - Narrow Escape
- std::cout で出力する書式を指定する - C++ プログラミング
- PRE10-C. 複数の文からなるマクロは do-while ループで包む