可変引数テンプレートのＮ番目のパラメータを除いたものをstd::tupleで定義したい

## ことの発端

先日、こんな記事を投稿したのですが

https://qiita.com/terukazu/items/a2690cd7ed9bd21b146a

その振り返りで

> ラムダ式にrefcon無くしたいかも

というものを実現しようかという話です。

## やりたいこと

* 可変引数テンプレートの```N```番目のパラメータを除いた```std::tuple```を作りたい。
* ```N```番目は非型テンプレートパラメータとしたい。
* ```std::tie``` と ```std::ignore``` で「値」を除去できるが、欲しいのは除去後の「型」なので今回はスコープとしよう。（```declval```でゴニョゴニョできるかも？？）

結論、こんな感じにしたい。

```cpp
using t = omit<1, void*, int>;
/* t = std::tuple<int> */
```

## やってみた

### 1. まず最初に

いきなり```N```番目とか作れんので、まず最初の１個を除外するコードを書いてみる。

```cpp
#include <tuple>
template <typename T1, typename ...ARGS> struct omit_1 {
  using type = std::tuple<ARGS...>;
};
int main(void){
  using t = omit_1<void*, int, int, float>::type;
  /* t = std::tuple<int, int, float> */
}
```

ふむ、良い感じ。

### 2. ```N```を0～3とするとどんな感じになるか試してみる

やはり、ここでも```N```番目とか作るんじゃなくて、```N```が変化するとどんな感じのコードになるかをやってみるのが良いと思うわけで。。。

```cpp
#include <tuple>
template <typename ...ARGS> struct omit_0 {
  using type = std::tuple<ARGS...>;
};

template <typename T1, typename ...ARGS> struct omit_1 {
  using type = std::tuple<ARGS...>;
};

template <typename T1, typename T2, typename ...ARGS> struct omit_2 {
  using type = std::tuple<T1, ARGS...>;
};

template <typename T1, typename T2, typename T3, typename ...ARGS> struct omit_3 {
  using type = std::tuple<T1, T2, ARGS...>;
};

int main(void){
  using t0 = omit_0<void*, int, int, float>::type;
  /* t0 = std::tuple<void *, int, int, float> */

  using t1 = omit_1<void*, int, int, float>::type;
  /* t1 = std::tuple<int, int, float> */

  using t2 = omit_2<void*, int, int, float>::type;
  /* t2 = std::tuple<void *, int, float> */

  using t3 = omit_3<void*, int, int, float>::type;
  /* t3 = std::tuple<void *, int, float> */
}
```

ちゃんと、動作確認もしつつ。。。
で、こうすると徐々にパターンが見えてくるので。。。

### 3. 特殊化使って```N```番目を表現してみる

私のような凡人には、命題を一般化することが難しいので、せめて```N```を非型パラメータにできないかってことで、```omit```という１次テンプレートを宣言して、```omit``` の特殊化を定義していく方針にする。
当然、```N```個定義しなきゃならんけど、関数の引数で10個超えるなんてなかなか拝めるものじゃないので、10個くらい用意しとけば良いんじゃないかしら？（そこまで引数が増えたら構造体を渡すのが一般的じゃないかなぁ）

という、ことでまずは 0 ～ 3 を特殊化定義していみる。

```cpp
template <int N, typename ...ARGS> struct omit;
template <int N, typename ...ARGS> struct omit<0, ARGS...> : public omit_0<ARGS...> {};
template <int N, typename ...ARGS> struct omit<1, ARGS...> : public omit_1<ARGS...> {};
template <int N, typename ...ARGS> struct omit<2, ARGS...> : public omit_2<ARGS...> {};
template <int N, typename ...ARGS> struct omit<3, ARGS...> : public omit_3<ARGS...> {};
```


### 4. エラーを潰す

すると。。。

```
error: template parameters not deducible in partial specialization:
[build]  template <int N, typename ...ARGS> struct omit<0, ARGS...> : public omit_0<ARGS...> {};
[build]                                            ^~~~~~~~~~~~~~~~
```

ふむ。。。
英語力が低いのでgoogle先生に翻訳してもらうと

```
部分的な特殊化で推論できないテンプレートパラメータ
```

メタプログラミングに慣れていないと、なかなかミスを見つけられない。
悩むこと数分、「はっ」と気付く。

```cpp
template <int N, typename ...ARGS> struct omit;
template <typename ...ARGS> struct omit<0, ARGS...> : public omit_0<ARGS...> {};
template <typename ...ARGS> struct omit<1, ARGS...> : public omit_1<ARGS...> {};
template <typename ...ARGS> struct omit<2, ARGS...> : public omit_2<ARGS...> {};
template <typename ...ARGS> struct omit<3, ARGS...> : public omit_3<ARGS...> {};
```

そりゃそうだと。。。

### 5. 最後から ```N```番目もやってみる

これで、非型パラメータを使えるようになったので、最後から ```N```番目というのが書けるようになる。

```cpp
template <int N, typename ...ARGS> struct romit {
  using type = typename omit<std::tuple_size<std::tuple<ARGS...>>::value - N, ARGS...>::type;
};
```

### 6. できたものをテストしてみる

テストコードと言っても、型を取得するだけなので、今回は開発環境（VSCode）で定義にカーソル当てて型を確認することにした。

```cpp
#include <tuple>
template <typename ...ARGS> struct omit_0 {
  using type = std::tuple<ARGS...>;
};

template <typename T1, typename ...ARGS> struct omit_1 {
  using type = std::tuple<ARGS...>;
};

template <typename T1, typename T2, typename ...ARGS> struct omit_2 {
  using type = std::tuple<T1, ARGS...>;
};

template <typename T1, typename T2, typename T3, typename ...ARGS> struct omit_3 {
  using type = std::tuple<T1, T2, ARGS...>;
};

template <int N, typename ...ARGS> struct omit;
template <typename ...ARGS> struct omit<0, ARGS...> : public omit_0<ARGS...> {};
template <typename ...ARGS> struct omit<1, ARGS...> : public omit_1<ARGS...> {};
template <typename ...ARGS> struct omit<2, ARGS...> : public omit_2<ARGS...> {};
template <typename ...ARGS> struct omit<3, ARGS...> : public omit_3<ARGS...> {};

template <int N, typename ...ARGS> struct romit {
  using type = typename omit<std::tuple_size<std::tuple<ARGS...>>::value - N, ARGS...>::type;
};

int main(void){
  using t0 = omit<0, void*, int, int*, float>::type;
  /* t0 = std::tuple<void*, int, int*, float> */

  using t1 = omit<1, void*, int, int*, float>::type;
  /* t1 = std::tuple<int, int*, float> */

  using t2 = omit<2, void*, int, int*, float>::type;
  /* t2 = std::tuple<void*, int*, float> */

  using t3 = omit<3, void*, int, int*, float>::type;
  /* t3 = std::tuple<void*, int, float> */

  // using t0r = romit<0, void*, int, int*, float>::type;
  /* omit<4, ...> は特殊化されていないのでomitは不完全型（incomplete type）というエラー */

  using t1r = romit<1, void*, int, int*, float>::type;
  /* t1 = std::tuple<void*, int, float> */

  using t2r = romit<2, void*, int, int*, float>::type;
  /* t2 = std::tuple<void*, int*, float> */

  using t3r = romit<3, void*, int, int*, float>::type;
  /* t3 = std::tuple<int, int*, float> */
}
```

### 7.```N``` が 0 ～ 10 まで作りましょう

大丈夫そうなので、これを10個作りますか。。。
コピペで大量に作っても良いけど、効率を考えてマクロを作ることにしました。
（諸事情がありマクロを書くことが罪悪感しかないのだけど、堪えることにした）

0 と 1 だけは特殊なので、そのまま定義しちゃって、2 以降をマクロで作る感じにしました。

```cpp
#include <tuple>

template <int N, typename ...ARGS> struct omit;

template <typename ...ARGS> struct omit_0 {
  using type = std::tuple<ARGS...>;
};
template <typename ...ARGS> struct omit<0, ARGS...> : public omit_0<ARGS...> {};

template <typename T1, typename ...ARGS> struct omit_1 {
  using type = std::tuple<ARGS...>;
};
template <typename ...ARGS> struct omit<1, ARGS...> : public omit_1<ARGS...> {};

#define __(V, TN, N) template <TN, typename ...ARGS> struct omit_##V { \
  using type = std::tuple<N, ARGS...>; \
}; \
template <typename ...ARGS> struct omit<V, ARGS...> : public omit_##V<ARGS...> {};
#define _(...) __VA_ARGS__

__( 2, _(typename T1, typename T2), _(T1))
__( 3, _(typename T1, typename T2, typename T3), _(T1, T2))
__( 4, _(typename T1, typename T2, typename T3, typename T4), _(T1, T2, T3))
__( 5, _(typename T1, typename T2, typename T3, typename T4, typename T5), _(T1, T2, T3, T4))
__( 6, _(typename T1, typename T2, typename T3, typename T4, typename T5, typename T6), _(T1, T2, T3, T4, T5))
__( 7, _(typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7), _(T1, T2, T3, T4, T5, T6))
__( 8, _(typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8), _(T1, T2, T3, T4, T5, T6, T7))
__( 9, _(typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9), _(T1, T2, T3, T4, T5, T6, T7, T8))
__(10, _(typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9, typename T10), _(T1, T2, T3, T4, T5, T6, T7, T8, T9))

#undef _
#undef __

template <int N, typename ...ARGS> struct romit {
  using type = typename omit<std::tuple_size<std::tuple<ARGS...>>::value - N, ARGS...>::type;
};
```

## 所感

多分、こんなのSTLやboostを探せば出てくるのかもね～
まぁ、この手の簡単な機能だったら、（時間があれば）解決方法を模索する（ライブラリを探すじゃなく）のもプログラムの醍醐味だったりする。

とはいえ、品質で考えたらSTLやboostの方が断然上（テスト量が桁違うし）なので、そのうち今回と同機能を見つけたら、このコードは破棄するつもり。更にドキュメントとか仕様とかしっかりしているので、チーム開発における「情報伝搬」・「認識合わせ」の手間が省けて効率的。

ただ、自分であれやこれやと実装方法を模索した後に、ライブラリに出会えれば、答え合わせが楽しいし。:grinning:
（ある意味、贅沢な方法かもですね）

さて。。。　こいつと同機能なライブラリを探してみようかしら。。。

## 神が降臨しました（2021/03/08 追記）

哀れな迷える子羊に神が降臨しました。
@myoukaku さんありがとうございます！
@myoukaku さんのコードには本質的な手法以外にも情報が詰まっておりまして、神様のお告げにはこう書かれていました。

* template における再帰の手法はこうだよ
* ```int``` じゃなく ```std::size_t``` 使おうね
* ```static_assert``` でテストコード書けるよ
* ```struct```はアクセス指定子を指定しなければ```public```継承になるよ

神様ごめんなさい。。。
いや、ありがとうございます。

神様のコードを理解するまで眺めて。。。
コードを閉じる　→　頭でイメージ　→　シャワーを浴びる　→　ごはんを食べる
そして、書いてみました！[^1]

[^1]: 実は当初は```N```番目が```1```始まりでしたが、神様の仕様（```0```始まり）の方が直感的なので仕様変更しました。

```cpp
#include <tuple>

template<std::size_t, std::size_t, typename ...> struct omit_impl;

template <std::size_t N, std::size_t I, typename ...TPARGS, typename T, typename ...ARGS>
  struct omit_impl<N, I, std::tuple<TPARGS...>, T, ARGS...> :
    omit_impl<N, I + 1, std::tuple<TPARGS..., T>, ARGS...> {};

template <std::size_t N, typename ...TPARGS, typename T, typename ...ARGS>
  struct omit_impl<N, N, std::tuple<TPARGS...>, T, ARGS...> :
    omit_impl<N, N + 1, std::tuple<TPARGS... /*, T */>, ARGS...> {};

template <std::size_t N, std::size_t I,typename ...TPARGS>
  struct omit_impl<N, I, std::tuple<TPARGS...>>
{
  using type = std::tuple<TPARGS...>;
};

template <std::size_t N, typename ...ARGS>
  struct omit :
    omit_impl<N, 0, std::tuple<>, ARGS...> {};

int main(void){
  using T0 = int;
  using T1 = float;
  using T2 = double;
  using T3 = int*;
  using T4 = float*;

  static_assert(std::is_same<omit<0>::type, std::tuple<>>::value);

  static_assert(std::is_same<omit<0, T0>::type, std::tuple<>>::value);

  static_assert(std::is_same<omit<0, T0, T1>::type, std::tuple<T1>>::value);
  static_assert(std::is_same<omit<1, T0, T1>::type, std::tuple<T0>>::value);
  
  static_assert(std::is_same<omit<0, T0, T1, T2>::type, std::tuple<T1, T2>>::value);
  static_assert(std::is_same<omit<1, T0, T1, T2>::type, std::tuple<T0, T2>>::value);
  static_assert(std::is_same<omit<2, T0, T1, T2>::type, std::tuple<T0, T1>>::value);

  static_assert(std::is_same<omit<0, T0, T1, T2, T3>::type, std::tuple<T1, T2, T3>>::value);
  static_assert(std::is_same<omit<1, T0, T1, T2, T3>::type, std::tuple<T0, T2, T3>>::value);
  static_assert(std::is_same<omit<2, T0, T1, T2, T3>::type, std::tuple<T0, T1, T3>>::value);
  static_assert(std::is_same<omit<3, T0, T1, T2, T3>::type, std::tuple<T0, T1, T2>>::value);

  static_assert(std::is_same<omit<0, T0, T1, T2, T3, T4>::type, std::tuple<T1, T2, T3, T4>>::value);
  static_assert(std::is_same<omit<1, T0, T1, T2, T3, T4>::type, std::tuple<T0, T2, T3, T4>>::value);
  static_assert(std::is_same<omit<2, T0, T1, T2, T3, T4>::type, std::tuple<T0, T1, T3, T4>>::value);
  static_assert(std::is_same<omit<3, T0, T1, T2, T3, T4>::type, std::tuple<T0, T1, T2, T4>>::value);
  static_assert(std::is_same<omit<4, T0, T1, T2, T3, T4>::type, std::tuple<T0, T1, T2, T3>>::value);
}
```

今回の神様のお言葉で特に感銘を受けたのは特殊化のパラメータで ```std::tuple<TPARGS...>``` で ```TPARGS```をキャプチャする箇所と、```omit_impl<N, N, ```で 値が同一という表現をする箇所が、グッときました。
言われれば、そりゃそうなんだけど、何と言うか。。。特殊化の面白しろさというか奥の深さを知った感じです。

@myoukaku さんのお陰で楽しい週末になりました。
ありがとうございます！

## 新しい技を覚えたので（2021/03/09 追記）

その後、@myoukaku さんから ```std::conditional``` という新しい技を伝授いただいたので、リファクタリングしました。

```cpp
template<std::size_t, std::size_t, typename ...> struct omitargs_impl;

template <std::size_t N, std::size_t I, typename ...TPARGS, typename T, typename ...ARGS>
  struct omitargs_impl<N, I, std::tuple<TPARGS...>, T, ARGS...> :
    std::conditional<
      I == N,
      omitargs_impl<N, I + 1, std::tuple<TPARGS...>, ARGS...>,
      omitargs_impl<N, I + 1, std::tuple<TPARGS..., T>, ARGS...>
    >::type {};

template <std::size_t N, std::size_t I,typename ...TPARGS>
  struct omitargs_impl<N, I, std::tuple<TPARGS...>>
{
  using type = std::tuple<TPARGS...>;
};

template <std::size_t N, typename ...ARGS> struct omitargs {
  static_assert(N >= 0, "N < 0");
  static_assert(N < std::tuple_size<std::tuple<ARGS...>>::value, "N >= ARGS");
  using type = typename omitargs_impl<N, 0, std::tuple<>, ARGS...>::type;
};

template <std::size_t N, typename ...ARGS> struct omitargs_r {
  static_assert(N >= 0, "N < 0");
  static_assert(N < std::tuple_size<std::tuple<ARGS...>>::value, "N >= ARGS");
  using type = typename omitargs<std::tuple_size<std::tuple<ARGS...>>::value - N - 1, ARGS...>::type;
};
```

この前のコードでは```<N, N``` のあたりが「技使ってるで～カッコエエ～」的な感じ（主観です）でしたが、こちらは地味ながらも（主観です）条件分岐が明確なので、後から読んで理解しやすいなぁ～と思います。