libdonut  2.3.2
Application framework for cross-platform game development in C++20
reflection.hpp
Go to the documentation of this file.
1 #ifndef DONUT_REFLECTION_HPP
2 #define DONUT_REFLECTION_HPP
3 
4 #include <cstddef> // std::size_t
5 #include <tuple> // std::tuple_size_v, std::make_tuple, std::tie, std::get(std::tuple)
6 #include <type_traits> // std::is_aggregate_v, std::integral_constant, std::remove_cvref_t
7 #include <utility> // std::index_sequence, std::make_index_sequence
8 
9 namespace donut::reflection {
10 
11 namespace detail {
12 
13 struct Init {
14  template <typename T>
15  operator T();
16 };
17 
18 template <std::size_t Index>
19 struct AggregateSizeTag : AggregateSizeTag<Index - 1> {};
20 
21 template <>
22 struct AggregateSizeTag<0> {};
23 
24 // clang-format off
25 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<26>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 26; }
26 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<25>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 25; }
27 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<24>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 24; }
28 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<23>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 23; }
29 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<22>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 22; }
30 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<21>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 21; }
31 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<20>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 20; }
32 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<19>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 19; }
33 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<18>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 18; }
34 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<17>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 17; }
35 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<16>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 16; }
36 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<15>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 15; }
37 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<14>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 14; }
38 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<13>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 13; }
39 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<12>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 12; }
40 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<11>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 11; }
41 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<10>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 10; }
42 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 9>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 9; }
43 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 8>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 8; }
44 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 7>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 7; }
45 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 6>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 6; }
46 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 5>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 5; }
47 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 4>) -> decltype(T{Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 4; }
48 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 3>) -> decltype(T{Init{}, Init{}, Init{}}, std::size_t{}) { return 3; }
49 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 2>) -> decltype(T{Init{}, Init{}}, std::size_t{}) { return 2; }
50 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 1>) -> decltype(T{Init{}}, std::size_t{}) { return 1; }
51 template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 0>) -> decltype(T{}, std::size_t{}) { return 0; }
52 // clang-format on
53 
54 } // namespace detail
55 
57 template <typename T>
58 requires std::is_aggregate_v<T> //
59 struct aggregate_size : std::integral_constant<std::size_t, detail::aggregateSizeImpl<T>(detail::AggregateSizeTag<26>{})> {};
61 
71 template <typename T>
72 inline constexpr std::size_t aggregate_size_v = aggregate_size<T>::value;
73 
86 [[nodiscard]] constexpr auto fields(auto&& aggregate) noexcept {
87  using T = std::remove_cvref_t<decltype(aggregate)>;
88  static_assert(std::is_aggregate_v<T>);
89  constexpr std::size_t FIELD_COUNT = aggregate_size_v<T>;
90  if constexpr (FIELD_COUNT == 26) {
91  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z] = aggregate;
92  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z);
93  } else if constexpr (FIELD_COUNT == 25) {
94  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y] = aggregate;
95  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y);
96  } else if constexpr (FIELD_COUNT == 24) {
97  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x] = aggregate;
98  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x);
99  } else if constexpr (FIELD_COUNT == 23) {
100  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w] = aggregate;
101  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w);
102  } else if constexpr (FIELD_COUNT == 22) {
103  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v] = aggregate;
104  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v);
105  } else if constexpr (FIELD_COUNT == 21) {
106  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u] = aggregate;
107  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u);
108  } else if constexpr (FIELD_COUNT == 20) {
109  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t] = aggregate;
110  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t);
111  } else if constexpr (FIELD_COUNT == 19) {
112  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s] = aggregate;
113  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s);
114  } else if constexpr (FIELD_COUNT == 18) {
115  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r] = aggregate;
116  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r);
117  } else if constexpr (FIELD_COUNT == 17) {
118  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q] = aggregate;
119  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q);
120  } else if constexpr (FIELD_COUNT == 16) {
121  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = aggregate;
122  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
123  } else if constexpr (FIELD_COUNT == 15) {
124  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = aggregate;
125  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
126  } else if constexpr (FIELD_COUNT == 14) {
127  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = aggregate;
128  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
129  } else if constexpr (FIELD_COUNT == 13) {
130  auto&& [a, b, c, d, e, f, g, h, i, j, k, l, m] = aggregate;
131  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m);
132  } else if constexpr (FIELD_COUNT == 12) {
133  auto&& [a, b, c, d, e, f, g, h, i, j, k, l] = aggregate;
134  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l);
135  } else if constexpr (FIELD_COUNT == 11) {
136  auto&& [a, b, c, d, e, f, g, h, i, j, k] = aggregate;
137  return std::tie(a, b, c, d, e, f, g, h, i, j, k);
138  } else if constexpr (FIELD_COUNT == 10) {
139  auto&& [a, b, c, d, e, f, g, h, i, j] = aggregate;
140  return std::tie(a, b, c, d, e, f, g, h, i, j);
141  } else if constexpr (FIELD_COUNT == 9) {
142  auto&& [a, b, c, d, e, f, g, h, i] = aggregate;
143  return std::tie(a, b, c, d, e, f, g, h, i);
144  } else if constexpr (FIELD_COUNT == 8) {
145  auto&& [a, b, c, d, e, f, g, h] = aggregate;
146  return std::tie(a, b, c, d, e, f, g, h);
147  } else if constexpr (FIELD_COUNT == 7) {
148  auto&& [a, b, c, d, e, f, g] = aggregate;
149  return std::tie(a, b, c, d, e, f, g);
150  } else if constexpr (FIELD_COUNT == 6) {
151  auto&& [a, b, c, d, e, f] = aggregate;
152  return std::tie(a, b, c, d, e, f);
153  } else if constexpr (FIELD_COUNT == 5) {
154  auto&& [a, b, c, d, e] = aggregate;
155  return std::tie(a, b, c, d, e);
156  } else if constexpr (FIELD_COUNT == 4) {
157  auto&& [a, b, c, d] = aggregate;
158  return std::tie(a, b, c, d);
159  } else if constexpr (FIELD_COUNT == 3) {
160  auto&& [a, b, c] = aggregate;
161  return std::tie(a, b, c);
162  } else if constexpr (FIELD_COUNT == 2) {
163  auto&& [a, b] = aggregate;
164  return std::tie(a, b);
165  } else if constexpr (FIELD_COUNT == 1) {
166  auto&& [a] = aggregate;
167  return std::tie(a);
168  } else {
169  return std::tie();
170  }
171 }
172 
188 template <std::size_t N>
189 constexpr void forEachIndex(auto fn) {
190  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) -> void {
191  (fn(std::integral_constant<std::size_t, Indices>{}), ...);
192  }(std::make_index_sequence<N>{});
193 }
194 
207 constexpr void forEach(auto&& tuple, auto fn) {
208  [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
209  (fn(std::get<Indices>(tuple)), ...);
210  }(std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<decltype(tuple)>>>{});
211 }
212 
226 [[nodiscard]] constexpr auto transform(auto&& tuple, auto fn) {
227  return [&]<std::size_t... Indices>(std::index_sequence<Indices...>) {
228  return std::make_tuple(fn(std::get<Indices>(tuple))...);
229  }(std::make_index_sequence<std::tuple_size_v<std::remove_cvref_t<decltype(tuple)>>>{});
230 }
231 
232 } // namespace donut::reflection
233 
234 #endif
constexpr float e
Definition: math.hpp:34
Definition: utilities.hpp:142
constexpr auto transform(auto &&tuple, auto fn)
Execute a function for each element in a given tuple and return a tuple containing the results.
Definition: reflection.hpp:226
constexpr auto fields(auto &&aggregate) noexcept
Get a tuple of references to each of the fields of an aggregate.
Definition: reflection.hpp:86
constexpr void forEachIndex(auto fn)
Execute a function once for each index in the sequence from 0 up to, but not including,...
Definition: reflection.hpp:189
constexpr void forEach(auto &&tuple, auto fn)
Execute a function once for each element in a given tuple, sequentially.
Definition: reflection.hpp:207
constexpr std::size_t aggregate_size_v
The number of fields in a given aggregate type.
Definition: reflection.hpp:72