summaryrefslogtreecommitdiff
path: root/type_list.cc
blob: 5a22b1673da22a653bf3123015a99808b59fcb41 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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