C++11 でテンプレートメタプログラミングで静的な型を持つ半分汎用っぽい変数を作るメモ

## 背景

* グラフィックスや機械学習の C++ コードを書いている
* データ構造は, `float` や `std::vector<float>` など大体決まった型(取りうる型が既知)を利用している
  * `std::vector<float> vertices` とか.  
* numpy ndarray みたいに, 同じインターフェイスで異るデータ型を扱いたい.
* データ構造のシリアライズやデシリアライズを自前で行いたい.
  * 扱う型はそんなに多くないのでぺろっとお手軽にやりたい. 
  * 既存の(e.g. Cereal とか libnop とか)は汎用すぎたり実装でかすぎたりする. 

C や, テンプレートを使わない方法だと, たとえば取りうるデータを列挙しておいて

```
struct GenericValue
{
  enum ValueType type; // e.g. VALUE_FLOAT3, VALUE_DOUBLE, ...
  std::arary<float, 3> float3_value;
  double double_value;
  ...
};

GenericValue value;

if (value.type == VALUE_FLOAT3) {
  value.float3_value = LoadFloat3Data(...);
} else if (value.type == VALUE_DOUBLE) {
  value.double_value = LoadDoubleData(...);
} else if ...
```  

みたいな力技もあるが, このやりかただと struct に各型ごとの変数定義となり, ストレージも消費してしまいよくない.
(void ポインタで管理という手もあるが, いずれにせよめんどいし, 型安全でもない)

```
template<T>
struct GenericValue {
  T value;
};

GenericValue<float> gf;
gf = 1.3f;

GenericValue<std::vector<float>> gvf;
std::vector<float> y = ...;
gvf = y;
```

みたいにしたい.
(C++17 `std::variant` と `std::any` の中間みたいな感じ?)

## 方法

`type_traits` https://en.cppreference.com/w/cpp/header/type_traits  と SFINAE などを利用します.

### 型の情報を持たせる

まず, 型に対して情報(たとえば文字列でその情報が取得できる)とデバッグとかで便利です.
(C++ RTTI の機能は使わない)

```
#include <type_traits>
#include <iostream>
#include <vector>

template<typename T>
struct Traits;

template<>
struct Traits<float>
{
  static constexpr auto type_name = "float";
};

template<class T>
class Value
{
 private:
  T m_value;

 public:

  std::string type_name() {
    return std::string(Traits<T>::type_name);
  }
};

template<class T>
class Value<std::vector<T>>
{
 private:
  T m_value;

 public:

  std::string type_name() {
    return "std::vector<" + std::string(Traits<T>::type_name) + ">";
  }
};

int main(int argc, char **argv)
{
  (void)argc;
  (void)argv;

  Value<float> f;
  Value<std::vector<float>> vf;

  std::cout << Traits<float>::type_name << "\n";
  std::cout << f.type_name() << "\n";
  std::cout << vf.type_name() << "\n";
}
```

```
float
float
std::vector<float>
```

と表示されます.

### 代入(実行時型チェック)

`std::is_same` あたりを使い, 型が合っているかチェックします.

```
  template<class U>
  bool assign(const U &u) {
    if (!std::is_same<T, U>::value) {
      return false;
    }

    m_value = u;

    return true;
  }
```

```
Value<float> gf;

float x = 1.2f;
int y = 1;

gf.assign(x); // return true
gf.assign(y); // return false
```

### 代入(コンパイル時チェック)

ソースコード上で取りうる型が決まっているのであれば, `std::enable_if` を使ってコンパイル時に型チェックすることができます.

https://blog.feabhas.com/2017/08/making-things-stuff-part-9/

```
template<typename T>
class Value
{

  template<typename U, typename std::enable_if<std::is_same<T, U>::value>::type* = nullptr>
  Value<T> &operator=(const U &u) {
    m_value = u;

    return (*this);
  }
};


Value<float> gf;
gf = 1.2f; // OK
gf = 1; // NG
```

C++14 でしたら `std::enable_if_t` でもう少しキレイにかけるかもです.

ただ, テンプレートの場合はエラーの場合エラーメッセージがわかりにくい(エラーメッセージ自体は正しくはあるが, コードを利用するほかのユーザーが実装の意図を理解するのがめんどい)というのが難点ですね. たとえば上で int 型の値を代入すると以下のようなメッセージが出ます.


```
main.cc:82:5: error: no viable overloaded '='
  f = y;
  ~ ^ ~
main.cc:15:7: note: candidate function (the implicit copy assignment operator) not viable: no known conversion from 'int' to
      'const Value<float>' for 1st argument
class Value
      ^
```

## non-intrusive にやる

他のライブラリとかとの兼ね合いで, データ構造体は素直なものにしてきたいときがあります.

```
struct Mesh {
  std::vector<float> vertices;
  ...
};
```

StaticJSON https://qiita.com/syoyo/items/b49b13fdadd9b92a46b3 などのように, ポインタとして保持してこねこねするのがよいでしょう!


```
template<class T>
ValueHandler() {
 private:
   T *m_data;
 public:
   ValueHandler(T *ptr) : m_data(ptr) { }

 // よろしく実装する
 bool set(...);

};

Mesh mesh;
ValueHandler<std::vector<float>> handler(&mesh.vertices);

std::vector<float> x = ...;

handler.set(x);
```


## 実例

numpy ぽいものの C++ 実装 NumCpp https://github.com/dpilger26/NumCpp や, pybind11 https://github.com/pybind/pybind11 の `def` の実装とかみるとよさそうです!

StaticJSON のコードも参考になりますが, RapidJSON との兼ね合いか, これはクラス継承ベースで実装されています.


## 参考文献

C++ template metaprogramming のナウでヤング(C++11 or later)なまとまった書籍は洋書では無いようです. 「C++ テンプレートテクニック」が大変役に立ちました. ありがとうございます. 

* C++テンプレートテクニック 第2版. επιστημη,高橋 晶
* C++のTraitsの仕組みを理解する https://qiita.com/rita0222/items/09e49723e3339ba5de24