diff options
-rw-r--r-- | type_list.cc | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/type_list.cc b/type_list.cc new file mode 100644 index 0000000..5a22b16 --- /dev/null +++ b/type_list.cc @@ -0,0 +1,164 @@ +#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 |