関数型や関数ポインタ型から返却値と引数を得る

## ことの発端

OSのAPIなんかでコールバック関数を受ける所謂「非同期関数」があって、こいつをC++のラムダ式使って書こうとするとキャプチャ演算子が空```[]```じゃないと使えないんで結構面倒だったりする。

```cpp
extern "C" int func(int, int, void*, void(*)(void*, int));
```

非同期関数は、大概、最後の引数がコールバック関数ポインタ型なので、うまい具合に非同期関数を Reactive Extensionsの ```observable``` にしたい。
んで、色々やっていると、乗り越えなきゃいけない壁が結構あり、そう簡単にはできない。
そして、まず最初に「非同期関数」がどんな引数や返却値なのか、つまり「関数型」から「引数」と「返却値」に分解しなきゃって事で調べてみた。

## stack_overflow で発見

手法となるといつもの愛読書[^1]では見つからず、ネットで調べると、いつもお世話になっている stack_overflow で見つけました。

https://stackoverflow.com/questions/57622162/get-function-arguments-type-as-tuple

他にも調べると boost でも function_traits なるものがあるみたいだけど、引数を```std::tuple``` で受けられると後々便利かと思い、この方法を採用してみた。

[^1]: ここでの「愛読書」とはストラウストラップ氏著の「プログラミング言語C++」です。

そして、コードを書いてみました。

```cpp
#include <iostream>
#include <tuple>

template<typename FUNCTION> struct function_traits;

template<typename RESULT, typename ...ARGS>
  struct function_traits<RESULT (ARGS...)>
{
  using result = RESULT;
  using argments = std::tuple<ARGS...>;
};

template<typename RESULT, typename ...ARGS>
  struct function_traits<RESULT (&)(ARGS...)>
{
  using result = RESULT;
  using argments = std::tuple<ARGS...>;
};

extern "C" int func(int, int, void*, void(*)(void*, int));

int main(void) {
  using result_t = function_traits<decltype(func)>::result;
  /* result_t = int */ 

  using args_t   = function_traits<decltype(func)>::argments;
  /* args_t = std::tuple<int, int, void *, std::tuple_element<0UL, std::tuple<void (*)(void *, int)>>::type)(void *, int)> */

  constexpr auto count = std::tuple_size<args_t>::value;
  /* count = 4UL */

  using cb_func_t   = std::tuple_element<count -1, args_t>::type;
  /* cb_func_t = void(*)(void*, int) */

  using cb_result_t = function_traits<decltype(*std::declval<cb_func_t>())>::result;
  /* cb_result_t = int */

  using cb_args_t   = function_traits<decltype(*std::declval<cb_func_t>())>::argments;
  /* cb_args_t = std::tuple<void *, int> */
}
```

素晴らしい、ちゃんと得たい情報は取れている。
ちゃんと、コールバック関数の型情報も取得できている！

と、ここまでは順調だったんだけど、次の一行で躓いて、１０時間潰れました。。。orz

```cpp
template<typename FUNCTION> struct function_traits;
```

これは何だ？

## 一次テンプレート

で、この宣言は一次テンプレートと呼ばれるもので、以降の特殊化のインターフェースを定義するもの。（§iso.14.5.5）

```cpp
template<typename FUNCTION> struct function_traits;
```

ここでの一次テンプレートは```宣言```で足りるとのこと。（```定義```である必要がない。）
「定義である必要がない」だけで、下記のように定義でも良い。（```static_assert```のような制約判定時には定義が必要になる）

```cpp
template<typename FUNCTION> struct function_traits {};
```

## テンプレートの特殊化

あくまで、一次テンプレートが基本です。というか、全てです。（ちょっとニュアンス違うかも？）
次の２つの定義は一次テンプレートの特殊化を定義しているものであって、オーバーロードみたいな事をやっている訳じゃない。

```cpp:関数型
template<typename RESULT, typename ...ARGS>
  struct function_traits<RESULT (ARGS...)>
```

```cpp:関数ポインタ型
template<typename RESULT, typename ...ARGS>
  struct function_traits<RESULT (&)(ARGS...)>
```

で、```function_traits<FUNCTION>```を使用する際の ```FUNCTION``` の型によって採用される定義が異なる。
仮に ```function_traits<void>::result```とかやると、特殊化されている何れの定義にも該当しないので、一次テンプレートが採用されて、一次テンプレートは```result```なんか定義されていないのでエラーとなる。

次に、特殊化した際の型を```template<typename RESULT, typename ...ARGS>``` にキャプチャして、定義内で使用することができるのだけど、個人的にはココが一番難解なところだと思う。
ちゅうのも、```void (int)```という関数型も```void (*)(int)``` って関数ポインタ型も何れも「１つの型」なので、いきなり```<typename RESULT, typename ...ARGS>```に適用される訳ない。
なので、順序としては特殊化の型```<RESULT (ARGS...)>```と```<RESULT (&)(ARGS...)>```に反応して、これが```template<typename RESULT, typename ...ARGS>``` に反映されるって感じかな。[^2]
この辺、愛読書[^1]の「§25.3 特殊化」で述べられていると思うのだけど、何度読み返してもスッと頭に入ってこない。

というか、その章に付箋がたんまり貼ってあって、以前も同じこと調べていたらしい。。。orz

う〜ん、メタプログラミングって難しい。。。

[^2]: 分かったように話しているが```<RESULT (*)(ARGS...)>```じゃなく```<RESULT (&)(ARGS...)>```の説明がまだできないので要調査。