summaryrefslogblamecommitdiff
path: root/type_list.cc
blob: 5a22b1673da22a653bf3123015a99808b59fcb41 (plain) (tree)



































































































































































                                                                                
#if 0
set -xe
g++     -std=c++11 -fsyntax-only type_list.cc
clang++ -std=c++11 -fsyntax-only type_list.cc
exit 0
#endif

#include <type_traits>

// Simple type list.
//
// A type list, as the name suggests, is a type which holds a list of types.
// Type lists can be arbitrarily nested and contain other type lists.
//
// Given a type list one can apply different algorithms, like adding types,
// flattening type lists and so on, where some are implement here.
//
// This is merely for fun and serves as a brain jogging exercise.

namespace tl {
template <typename... T>
struct type_list;

// -- GET ----------------------------------------------------------------------

template <std::size_t Index, typename Head, typename... Tail>
struct get {
  static_assert(Index < sizeof...(Tail) + 1, "index out of bounds");
  using type = typename get<Index - 1, Tail...>::type;
};

template <typename Head, typename... Tail>
struct get<0, Head, Tail...> {
  using type = Head;
};

template <std::size_t I, typename... Ts>
using get_t = typename get<I, Ts...>::type;

// -- PREPEND ------------------------------------------------------------------

template <typename Head, typename TypeList>
struct prepend;

template <typename... Ts>
using prepend_t = typename prepend<Ts...>::type;

template <typename Head, typename... Inner>
struct prepend<Head, type_list<Inner...>> {
  using type = type_list<Head, Inner...>;
};

// -- FLATTEN ------------------------------------------------------------------

template <typename Head, typename... Tail>
struct flatten;

template <typename... Ts>
using flatten_t = typename flatten<Ts...>::type;

template <typename Head, typename... Tail>
struct flatten {
  using type = prepend_t<Head, flatten_t<Tail...>>;
};

// Specialization (1):
// Unpack inner types of LEADING type_list parameter and recursively flatten.
template <typename... Inner, typename... Tail>
struct flatten<type_list<Inner...>, Tail...> {
  using type = flatten_t<Inner..., Tail...>;
};

// Specialization (2):
// Unpack inner types of SINGLE type_list parameter and recursively flatten.
template <typename... Inner>
struct flatten<type_list<Inner...>> {
  using type = flatten_t<Inner...>;
};

// Specialization (3):
// Pack single non-type_list type into a type_list (recursive base case).
template <typename Head>
struct flatten<Head> {
  using type = type_list<Head>;
};

// -- TYPE_LIST ----------------------------------------------------------------

template <typename Head, typename... Tail>
struct type_list<Head, Tail...> {
  using self = type_list<Head, Tail...>;

  static constexpr std::size_t len = sizeof...(Tail) + 1 /* Head */;

  // Get type at specific INDEX.
  template <std::size_t Index>
  using get = get_t<Index, Head, Tail...>;

  // Prepend TYPE to the type list.
  template <typename Type>
  using prepend = prepend_t<Type, self>;

  // Flatten nested type lists.
  using flatten = flatten_t<self>;
};
}  // namespace tl

// -- TESTS --------------------------------------------------------------------

#if __cplusplus >= 201703L
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__);
#else
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, "test failed");
#endif

// Flat type list.
namespace test1 {
using list = tl::type_list<int, char, int>;
STATIC_ASSERT(list::len == 3);
STATIC_ASSERT(std::is_same<list::get<0>, int>::value);
STATIC_ASSERT(std::is_same<list::get<1>, char>::value);
STATIC_ASSERT(std::is_same<list::get<2>, int>::value);
STATIC_ASSERT(std::is_same<list::prepend<void>,
                           tl::type_list<void, int, char, int>>::value);
STATIC_ASSERT(std::is_same<list::flatten, list>::value);
}  // namespace test1

// Nested type lists.
namespace test2 {
using list = tl::type_list<int, tl::type_list<char>, void>;
STATIC_ASSERT(list::len == 3);
STATIC_ASSERT(std::is_same<list::get<0>, int>::value);
STATIC_ASSERT(std::is_same<list::get<1>, tl::type_list<char>>::value);
STATIC_ASSERT(std::is_same<list::get<2>, void>::value);

STATIC_ASSERT(
    std::is_same<
        list::prepend<tl::type_list<int>>,
        tl::type_list<tl::type_list<int>, int, tl::type_list<char>, void>>::
        value);

using flat = list::flatten;
STATIC_ASSERT(flat::len == 3);
STATIC_ASSERT(std::is_same<flat::get<0>, int>::value);
STATIC_ASSERT(std::is_same<flat::get<1>, char>::value);
STATIC_ASSERT(std::is_same<flat::get<2>, void>::value);
}  // namespace test2

// Deeply nested type lists.
namespace test3 {
using list = tl::type_list<tl::type_list<char, tl::type_list<void>, int>>;
STATIC_ASSERT(list::len == 1);
STATIC_ASSERT(
    std::is_same<list::get<0>,
                 tl::type_list<char, tl::type_list<void>, int>>::value);

using flat = list::flatten;
STATIC_ASSERT(flat::len == 3);
STATIC_ASSERT(std::is_same<flat::get<0>, char>::value);
STATIC_ASSERT(std::is_same<flat::get<1>, void>::value);
STATIC_ASSERT(std::is_same<flat::get<2>, int>::value);
}  // namespace test3

#undef STATIC_ASSERT