libdonut 2.3.6
Application framework for cross-platform game development in C++20
Loading...
Searching...
No Matches
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
9namespace donut::reflection {
10
11namespace detail {
12
13struct Init {
14 template <typename T>
15 operator T();
16};
17
18template <std::size_t Index>
19struct AggregateSizeTag : AggregateSizeTag<Index - 1> {};
20
21template <>
22struct AggregateSizeTag<0> {};
23
24// clang-format off
25template <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; }
26template <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; }
27template <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; }
28template <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; }
29template <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; }
30template <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; }
31template <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; }
32template <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; }
33template <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; }
34template <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; }
35template <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; }
36template <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; }
37template <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; }
38template <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; }
39template <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; }
40template <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; }
41template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag<10>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 10; }
42template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 9>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 9; }
43template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 8>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 8; }
44template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 7>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 7; }
45template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 6>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 6; }
46template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 5>) -> decltype(T{Init{}, Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 5; }
47template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 4>) -> decltype(T{Init{}, Init{}, Init{}, Init{}}, std::size_t{}) { return 4; }
48template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 3>) -> decltype(T{Init{}, Init{}, Init{}}, std::size_t{}) { return 3; }
49template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 2>) -> decltype(T{Init{}, Init{}}, std::size_t{}) { return 2; }
50template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 1>) -> decltype(T{Init{}}, std::size_t{}) { return 1; }
51template <typename T> constexpr auto aggregateSizeImpl(AggregateSizeTag< 0>) -> decltype(T{}, std::size_t{}) { return 0; }
52// clang-format on
53
54} // namespace detail
55
57template <typename T>
58requires std::is_aggregate_v<T> //
59struct aggregate_size : std::integral_constant<std::size_t, detail::aggregateSizeImpl<T>(detail::AggregateSizeTag<26>{})> {};
61
71template <typename T>
72inline 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
188template <std::size_t N>
189constexpr 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
207constexpr 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
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