summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--type_list.cc164
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