おはやし日記

特にテーマ無しの日記。

自作デバッグ関数の覚書き

プログラム書いていて、途中結果を出力したいときってありますよね。うんうん。

今まで

こんな感じでやってました

#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)

まとめ

今までのをまとめました(冒頭の追記の思想(余計なを出力しない)で結構書き換えてあります)

my debug

まともな使用例

/* 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 

参考

プライバシーポリシー ・お問い合わせはこちら