デバッグ用の可変長引数関数を作ってみた

## 目標の形
```cpp
int main(){
    int abc=10;
    int hoge=5;
    string str="aiueo";
    debug(abc,hoge,str);
}
```
```:理想の出力結果
abc:10 hoge:5 str:aiueo
```
debugはマクロでも関数でもなんでも良いから変数名とその値を空白区切りで出力してほしかった（できれば標準エラー出力）

## 結論
```cpp
#define debug(...) debug_func(0, #__VA_ARGS__, __VA_ARGS__)

template <typename T>
void debug_func(int i, T name) {
  cerr << endl;
}

template <typename T1, typename T2, typename... T3>
void debug_func(int i, const T1 &name, const T2 &a, const T3 &...b) {
  for ( ; name[i] != ',' && name[i] != '\0'; i++ ) cerr << name[i];
  cerr << ":" << a << " ";
  debug_func(i + 1, name, b...);
}
```
2つの関数とマクロを定義することで実現できた  
改行の手前に余分な空白が入るのはまあデバッグ用だし...
`\n`でなくて`endl`にしたのも甘え

**追記**:本ページ下部に修正を行い、改行前に空白が入らないコードが載せてあります。

<br>

## それぞれの説明
まずは

```cpp
#define debug(...) debug_func(0, #__VA_ARGS__, __VA_ARGS__)
```
から


これは可変長引数をマクロで読み取っている  
なんでマクロなのかというと変数名を表示するのに`#`演算子が必要だったから  
※`#`演算子で変数名を文字列として取得できる

例として、`debug(a,bb,ccc)`と呼び出すと、`"a,bb,ccc"`という文字列が取得できるので変数名を出力するときは`,`を読み飛ばす必要がある

<br>

次に

```cpp
template <typename T>
void debug_func(int cnt, T name) {
    cerr << endl;
}
```
これは`debug_func`の終了のための関数  
最後の関数と名前は同じだけど引数が違う  
再帰的に呼び出しながら出力していって、最後に改行するという流れ

<br>

最後に

```cpp
template <typename T1, typename T2, typename... T3>
void debug_func(int i, const T1 &name, const T2 &a, const T3 &...b) {
  for ( ; name[i] != ',' && name[i] != '\0'; i++ ) cerr << name[i];
  cerr << ":" << a << " ";
  debug_func(i + 1, name, b...);
}
```

この関数を見た人は全員次のような疑問です**え！？**ってなる（と思う）

Q.`#define debug(...) debug_func(0, #__VA_ARGS__, __VA_ARGS__)`だと引数が3つなのにこれは引数が4つだから間違ってるんじゃないか？  
A.間違ってないです

<br>

実は可変長引数を関数の引数に取るときは`...`(pack演算子)を使える  
pack演算子は前につけるか後につけるかで変わってくる

- 前につける  
  `int ...b`  
  可変長引数を一つの変数に受け取る
   
- 後につける  
  `b...`  
  変数から可変長引数を取り出す

可変長引数の内、一つを変数に入れて残りをpack演算子でもう一つの変数にまとめているイメージ


```cpp:例
int int_sum() {
    return 0;
}

template <typename T1, typename... T2>
int int_sum(const T1 &a, const T2&... b){
    return a + int_sum(b...);
}

int main(){
    cout<<int_sum(10,20,5)<<endl;
}
// 結果
35
```

また、下記のように`#define sum(...) int_sum(__VA_ARGS__)`のようにして呼び出しても良い

```cpp
#define sum(...) int_sum(__VA_ARGS__)

int int_sum() {
    return 0;
}

template <typename T1, typename... T2>
int int_sum(const T1 &a, const T2&... b){
    return a + int_sum(b...);
}

int main(){
    cout<<sum(10,20,5)<<endl;
}
```

<br>

##### 余談
```cpp
for ( ; name[i] != ',' && name[i] != '\0'; i++ ) cerr << name[i];
```
↑この部分は変数名が`○○,○○○,....`という風にコンマ区切りで渡されるのでコンマまで一文字ずつ出力している  
`name`の型を調べてみると`const char*`だったので最後に`\0`がある


```cpp:nameの型を調査するコード
#include <iostream>
#include <typeinfo>
using namespace std;

#define debug(...) debug_func(0, #__VA_ARGS__, __VA_ARGS__)

template <typename T>
void debug_func(int i, T name) {
  cerr << endl;
  cerr << "typename:" << typeid(name).name() << endl;
  cerr << "const char* same?:" << boolalpha << (typeid(name) == typeid(const char *)) << endl;
}

template <typename T1, typename T2, typename... T3>
void debug_func(int i, const T1 &name, const T2 &a, const T3 &...b) {
  for ( ; name[i] != ',' && name[i] != '\0'; i++ ) cerr << name[i];
  cerr << ":" << a << " ";
  debug_func(i + 1, name, b...);
}

int main() {
  int hoge = 10;
  int foo = 101391;
  long long hofoo = 101010;
  debug(hoge, foo, hofoo);
}
```



```:出力結果
hoge:10  foo:101391  hofoo:101010 
typename:PKc
const char* same?:true
```
`typename`が`PKc`となっているのは`P`がポインタ、`K`が`const`、`c`が`char`を表していて、mangled nameと呼ばれているらしい

<br>

## 追記
yumetodoさんからコメントをいただきましたので、勝手ながら記事に追記いたしました。

```cpp
#include <iostream>
#include <string_view>
#define debug(...) debug_func(#__VA_ARGS__, __VA_ARGS__)
template <typename T, typename... Rest>
void debug_func(std::string_view name, const T& a, Rest&&... rest)
{
  const auto end = name.find_first_of(',');
  std::cerr << name.substr(0, end) << ":" << a;
  if constexpr (sizeof...(rest) == 0) {
    std::cerr << std::endl;
  }
  else {
    std::cerr << ' ';
    debug_func(name.substr(name.find_first_not_of(' ', end + 1)), std::forward<Rest>(rest)...);
  }
}

int main()
{
  int a = 3;
  double b = 4.2;
  debug(a, b);
  debug(b, a);
}
```

<br>

私が書いたコードでは、`debug(std::min(3,7));`といった`,`を含むものを出力しようとした場合に`std::min(3:3`というような想定していない出力になってしまうことが確認できたので修正いたしました
具体的には、`(){}[]<>`で囲まれた`,`であればスルーするようなプログラムになっています
（最適な解決策とは思えませんが...）

```cpp:修正版
#include <iostream>
#include <stack>
#include <string_view>
using namespace std;

#define debug(...) debug_func(#__VA_ARGS__, __VA_ARGS__)

template <typename T, typename... Rest>
void debug_func(string_view name, const T &a, Rest &&...rest) {
  stack<char> brackets;
  string_view left_brackets = "({[<";
  string_view right_brackets = ")}]>";
  int end = name.size();
  for ( int i = 0; i < (int)name.size(); i++ ) {
    if ( left_brackets.find(name[i]) != string::npos ) {
      brackets.push(name[i]);
    }
    if ( right_brackets.find(name[i]) != string::npos ) {
      if ( !brackets.empty() ) brackets.pop();
    }
    if ( name[i] == ',' && brackets.empty() ) {
      end = i;
      break;
    }
  }
  cerr << name.substr(0, end) << ":" << a;
  if constexpr ( sizeof...(rest) == 0 ) {
    cerr << endl;
  } else {
    cerr << ' ';
    debug_func(name.substr(name.find_first_not_of(' ', end + 1)), forward<Rest>(rest)...);
  }
}

int main() {
  int aaa = 10;
  debug(10, aaa, max(10, 4), min(10, 1));
}
```

<br>

## 参考

詳しくはこちらをご覧ください

> - https://cpprefjp.github.io/lang/cpp11/variadic_templates.html

> - http://yohshiy.blog.fc2.com/blog-entry-300.html

> - https://www.creativ.xyz/dump-cpp-652/

> - https://stackoverflow.com/questions/65369710/what-does-pkc-as-the-output-of-typeidvar-name-mean