#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