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

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

デバッガよくわかんないので途中出力しています。うんうん。

多分ところどころガバがあります。



## 今まで

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

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

ここで使っているのが、「文字列化演算子」`#`です。変数の名前自体を文字列に変換して出力できるので、どの変数がどの値なのかわかりやすいです。

## 可変引数テンプレート

ただ、変数の数ごとに使い分けをしているのがアホらしいですね。ここで、どこかで小耳に挟んだ「[可変引数テンプレート](https://cpprefjp.github.io/lang/cpp11/variadic_templates.html)」を使います。

<blockquote>
パラメータパックを最初の要素から順番に処理していきたい場合には、「任意の型のパラメータをひとつと、任意の個数の任意の型のパラメータを受け取る」というような形式のパラメータリストとし、再帰によって処理をする：<br>
<a>https://cpprefjp.github.io/lang/cpp11/variadic_templates.html</a>
</blockquote>

とのことなのでそこに書いてあるのをほぼコピペして書きます。

```cpp
void debug(){cout<<endl;} //引数がなくなったときのためのdebug()

template <class H, class... Ts>
void debug(H&& h, Ts&&... ts){
  cout<<h<<" ";
  debug(forward<Ts>(ts)...);
}
```
```cpp
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()`とかは、よくわかんなかったけど動くからええかって…………（一応参考: https://cpprefjp.github.io/reference/utility/forward.html , https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html  ）

## テンプレートで対応

### pair

このままだと`pair`を出力できないので、`cout<<h<<" ";`のところをテンプレートで書き換えます

```cpp
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)...);
}
```
```cpp
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を突っ込んで出力したいので。

```cpp
template <class T>
void debug(vector<T> &vt){
  for(auto x: vt){show(x);}cout<<endl;
}
```
```cpp
int main(){
  vector<int> vi = {1,2,3,4,5};
  debug(vi); //1 2 3 4 5
}
```

### 初期化子リスト

`debug({1, 3, 5});`ってやりたいなって（でもこれ型合わせなきゃいけないとかであんまりデバッグ的に意味はない。`{}`に入れなくて良いじゃん）。

```cpp
template <class T>
void debug(initializer_list<T> init){
  for(auto x: init){show(x);}cout<<endl;
}
```
```cpp
int main(){
  debug({1,3,5}); //1 3 5
}
```

## 行番号出したい

どこでバグってるか多少わかりやすくするために行番号を出力するようにします。

```cpp
#define ldebug(...) do{cout<<"["<<setw(3)<<__LINE__<<"] "; debug(__VA_ARGS__);}while(0)
```

+ マクロの引数`(...)`は可変引数マクロで、マクロ内では`__VA_ARGS__`の位置に展開されます。
+ `__LINE__`はプリプロセッサの段階で行数に置換されます。
+ `setw(3)`は`cout`の書式設定で、`printf("%3d")`に対応しています。
+ do-while文にしているのは、マクロで複文を書くときの常套手段です

## やっぱり変数名も出したい

最初に書いたみたいに変数名表示したいですよね。ええ。

```cpp
#define DEBUG(...) do{cout<<#__VA_ARGS__<<" = "; debug(__VA_ARGS__);}while(0)
```

+ 先ほどの`__VA_ARGS__`に、冒頭で紹介した「文字列化演算子」`#`を合わせます

## 行番号も変数名も出したい

欲張り。

```cpp
#define lDEBUG(...) do{cout<<"["<<setw(3)<<__LINE__<<"] "<<#__VA_ARGS__<<" = "; debug(__VA_ARGS__);}while(0)
```

## まとめ

今までのをまとめました

```cpp
#include <bits/stdc++.h>
using namespace std;

namespace /* debug */{
  #define DEBUG(...) do{cout<<#__VA_ARGS__<<" = ";  debug(__VA_ARGS__);}while(0) //変数
  #define ldebug(...) do{cout<<"["<<setw(3)<<__LINE__<<"] "; debug(__VA_ARGS__);}while(0) //行数
  #define lDEBUG(...) do{cout<<"["<<setw(3)<<__LINE__<<"] "<<#__VA_ARGS__<<" = ";  debug(__VA_ARGS__);}while(0) //変数, 行数

  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)...);
  }

  template <class T>
  void debug(vector<T> &vt){
    for(auto x: vt)show(x);
    cout<<endl;
  }

  template <class T>
  void debug(initializer_list<T> init){
    for(auto x: init)show(x);
    cout<<endl;
  }
}

int main(){
  int a=1, b=10, c=-1;
  double d=3.14, e=1.41;
  string S = "hoge";

  debug(a);
  DEBUG(99);
  ldebug(d);
  lDEBUG(S);

  cout<<"//////////"<<endl;

  debug(a,b,c);
  DEBUG(d,e,S);
  ldebug(a,d,S);
  lDEBUG(b,c,e,"fuga");

  cout<<"//////////"<<endl;

  debug({a,b,c});
  DEBUG({d,e,1.23});
  ldebug({a,b,99});
  lDEBUG({"piyo","nyo"});

  cout<<"//////////"<<endl;

  vector<pair<int, double>> pid = {{a,d}, {c,e}};
  debug(pid);
  DEBUG(pid);
  ldebug(pid);
  lDEBUG(pid);
}
```
```output.txt
1 
99 = 99 
[ 45] 3.14 
[ 46] S = hoge 
//////////
1 10 -1 
d,e,S = 3.14 1.41 hoge 
[ 52] 1 3.14 hoge 
[ 53] b,c,e,"fuga" = 10 -1 1.41 fuga 
//////////
1 10 -1 
{d,e,1.23} = 3.14 1.41 1.23 
[ 59] 1 10 99 
[ 60] {"piyo","nyo"} = piyo nyo 
//////////
(1, 3.14) (-1, 1.41) 
pid = (1, 3.14) (-1, 1.41) 
[ 67] (1, 3.14) (-1, 1.41) 
[ 68] pid = (1, 3.14) (-1, 1.41) 
```

## まともな使用例

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

## 参考
+ [#と##演算子](http://wisdom.sakura.ne.jp/programming/c/c42.html)
+ [テキストマクロの置換 - cppreference.com](https://ja.cppreference.com/w/cpp/preprocessor/replace:)
+ [可変引数テンプレート - cpprefjp C++日本語リファレンス](https://cpprefjp.github.io/lang/cpp11/variadic_templates.html)
+ [適当なC++テンプレート入門 - Qiita](https://qiita.com/hal1437/items/b6deb22a88c76eeaf90c)
+ [初期化子リスト - cpprefjp C++日本語リファレンス](https://cpprefjp.github.io/lang/cpp11/initializer_lists.html)
+ [initializer_list - cpprefjp C++日本語リファレンス](https://cpprefjp.github.io/reference/initializer_list/initializer_list.html)
+ [可変引数マクロ - cpprefjp C++日本語リファレンス](https://cpprefjp.github.io/lang/cpp11/variadic_macros.html)
+ [C言語の__LINE__マクロをプリプロセッサの段階で文字列に変換する - Narrow Escape](https://www.hiroom2.com/2015/09/07/c%E8%A8%80%E8%AA%9E%E3%81%AE-line-%E3%83%9E%E3%82%AF%E3%83%AD%E3%82%92%E3%83%97%E3%83%AA%E3%83%97%E3%83%AD%E3%82%BB%E3%83%83%E3%82%B5%E3%81%AE%E6%AE%B5%E9%9A%8E%E3%81%A7%E6%96%87%E5%AD%97%E5%88%97%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B/)
+ [std::cout で出力する書式を指定する - C++ プログラミング](https://ez-net.jp/article/DB/ii5RQp5C/cbWeIevlNiNT/)
+ [PRE10-C. 複数の文からなるマクロは do-while ループで包む](https://www.jpcert.or.jp/sc-rules/c-pre10-c.html)