茂加部珈琲店

主にtech関連のメモ置き場です

テンプレートで型情報を作る

C++勉強ネタです

テンプレートを使って、関数型言語の型情報を生成してみます(やっつけなのでバグがあるかも)

値型はValue, λ抽象はArrowになっています。 とりあえずValueには設定された文字列を格納し、Arrowは単にリストを作ってるだけです。

型Tが関数型を持つ場合は、std::tupleシグネチャを格納しておきます

静的変数のアドレスはconstexprとして、初期化前に取得することができます。
依存関係のある静的変数を初期化するのって大変ですよね…
staticローカル変数を使っても良いのですが、そうすると初期化が再帰してUBになるかもです

#include <tuple>
#include <iostream>
#include <variant>
#include <cassert>

template <class T>
struct traits;

// value type
struct Value;
// arrow type
struct Arrow;
// union
using Type = std::variant<Arrow,Value>;

struct Arrow{
  Type* captured;
  Type* returns;
};
struct Value{
  const char* name;
};

template <class T>
struct value_type{
  static Type type;
};

// arrow_type impl
template <class T, class ...Ts>
struct arrow_type_{
  static Type type;
};
// arrow_type impl
template <class T1, class T2>
struct arrow_type_<T1,T2>{
  static Type type;
};

template <class T, class ...Ts>
struct arrow_type;

template <class T, class ...Ts>
struct arrow_type<T, std::tuple<Ts...>>{
  static constexpr Type* type = &arrow_type_<Ts...>::type;
};

template <class T, class = void>
struct object_type {
  static constexpr Type* type = arrow_type<T,typename T::type>::type;
};

template <class T>
struct object_type<T, std::void_t<decltype(traits<T>::name)>>{
  static constexpr Type* type = &value_type<T>::type;
};

template <class T>
Type value_type<T>::type {Value{traits<T>::name}};

template <class T, class ...Ts>
Type arrow_type_<T,Ts...>::type {Arrow{ object_type<T>::type, &arrow_type<Ts...>::type }};

template <class T1, class T2>
Type arrow_type_<T1,T2>::type {Arrow{object_type<T1>::type, object_type<T2>::type}};

// define int type
template <>
struct traits<int>{
  static constexpr const char* name = "int";
};
int main () {    
  struct foo{
    // basic closure type (int->int)
    using type = std::tuple<int,int>;
  };
  struct bar{
    // recursive closure type (bar->int) 
    using type = std::tuple<bar,int>;
  };
  // value
  auto i = object_type<int>::type;
  std::cout << std::get<Value>(*i).name << std::endl; // int
  // closure
  auto f = object_type<foo>::type;
  assert(std::get<Arrow>(*f).captured == i); 
  assert(std::get<Arrow>(*f).returns == i); 
  // recursive
  auto b = object_type<bar>::type;
  assert(b == std::get<Arrow>(*b).captured);
  return 0;
}