Loading...
Searching...
No Matches
reflection.h
Go to the documentation of this file.
1#ifndef _BBM_REFLECTION_H_
2#define _BBM_REFLECTION_H_
3
4#include "util/tuple.h"
5#include "util/named.h"
6#include "util/macro_util.h"
8
10
11/************************************************************************/
12/*! \file reflection.h
13 \brief Compile-time reflection of:
14
15 + class attributes (types and run-time values)
16 + base classes
17 + enum
18
19 To do:
20 + member functions
21
22 Usage:
23
24 To add attribute reflection, add 'BBM_ATTRIBUTES(<attribute names...>)' after
25 the declaration of the class attributes. For example:
26
27 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
28 struct foo
29 {
30 floar bar;
31 char var;
32
33 BBM_ATTRIBUTES(bar, var);
34 };
35 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
36
37 We can then query the attributes, types, and number of attributes:
38
39 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
40 foo f{1,'a'};
41 std::cout << attributes(f) << std::endl; // (bar = 1, var = 'a')
42 std::cout << typestring< attributes_t<foo> > << std::endl; // named< std::tuple<float&, char&>, "bar", "var" >
43 std::cout << attributes_size<foo> << std::endl; // 2
44 std::cout << typestring< std::tuple_element_t<0, attributes_t<foo>>> << std::endl; // float
45 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
46
47 Note: we can also pass the attributes of an attribute; the name will be
48 the names of the attributes of of the attribue:
49
50 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
51 struct foo2
52 {
53 float a;
54 foo b;
55 BBM_ATTRIBUTES(a, attributes(b));
56 };
57 foo2 f2;
58 std::cout << attributes(f2) << std::endl; // (a=0, bar=0, var=0)
59 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60
61 To add public base class reflection, add 'BBM_BASETYPES(<base class names...>)'
62 at the start of the class. For example:
63
64 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
65 struct foo2 : public foo
66 {
67 BBM_BASETYPES(foo);
68 foo2(float a, float b, char c) : bar2(a), bar(b), var(c) {}
69 float bar2;
70 BBM_ATTRIBUTES(bar2);
71 }
72 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
73
74 To querry the type of the return type of attributes(...):
75
76 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
77 struct foo3
78 {
79 float a, b;
80 BBM_ATTRIBUTES(a,b);
81
82 using A = BBM_ATTRIBUTES_T; // bbm::named<std::tuple<float,float>, "a", "b">
83 };
84
85 using B = attributes_t<foo3>; // bbm::named<std::tuple<float,float>, "a", "b">
86 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
87
88 The difference between both (A and B) is that B requires an evaluated
89 context (i.e., the type of the class must be known), whereas A is robust in
90 unevaluated contexts (i.e., from inside the class before the class is
91 complete).
92
93 We can query the base classes:
94
95 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
96 std::cout << typestring< std::tuple_element_t< 0, basetypes_t<foo2> > > << std::endl; // foo
97 std::cout << basetypes_size<foo2> << std::endl; // 1
98 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
99
100 Querying the attributes will include the attributes of the base class too:
101
102 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
103 std::cout << attributes(f2) << std::endl; // (bar2 = 3, bar = 1, var = 'a')
104 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105
106 In some cases it might be desirable to prefix the attribute names of a base class:
107
108 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
109 struct foo3 : public foo, foo2
110 {
111 BBM_BASETYPES(prefix<"foo::", foo>, foo2);
112 foo3(float a, float b, char c) : foo(13,'b'), foo2(a,b,c) {}
113 };
114
115 foo3 f3(3,1,'a');
116 std::cout << attributes(f3) << std::endl; // (foo::bar = 13, foo::var = 'b', bar2 = 3, bar = 1, var = 'a')
117 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
118
119 BBM also support reflection for enum struct/classes.
120
121 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
122 enum struct foo { A, B, C };
123 BBM_ENUM(foo, A, B, C);
124
125 std::cout << bbm::get<"B">(enum_v<foo>) << std::endl; // 1
126 std::cout << bbm::get<1>(enum_v<foo>) << std::endl; // 1
127 std::cout << enum_v<foo>[1] << std::endl; // 1
128 std::cout << enum_v<foo>.template name<1> << std::endl; // "B"
129 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
130
131 See concepts/reflection.h for reflection related concepts.
132
133*************************************************************************/
134
135namespace bbm {
136 namespace reflection {
137
138 /********************************************************************/
139 /*! \brief BBM_ATTRIBUTES macro for declaring the attriute of a class
140 ********************************************************************/
141#define BBM_ATTRIBUTES(...) \
142 using attribute_tuple_t = bbm::named_cat_t< decltype(bbm::named_flatten(bbm::make_named<BBM_STRINGIFY_EACH(__VA_ARGS__)>(bbm::make_ref_tuple(__VA_ARGS__)))), typename reflection_base_t::attribute_tuple_t>; \
143 \
144 inline constexpr auto attribute_tuple(void) \
145 { \
146 auto attr = bbm::named_flatten(bbm::make_named<BBM_STRINGIFY_EACH(__VA_ARGS__)>(bbm::make_ref_tuple(__VA_ARGS__))); \
147 return bbm::named_cat(attr, reflection_base_t::attribute_tuple(*this)); \
148 } \
149 \
150 inline constexpr auto attribute_tuple(void) const \
151 { \
152 auto attr = bbm::named_flatten(bbm::make_named<BBM_STRINGIFY_EACH(__VA_ARGS__)>(bbm::make_ref_tuple(__VA_ARGS__))); \
153 return bbm::named_cat(attr, reflection_base_t::attribute_tuple(*this)); \
154 } \
155
156
157 /*******************************************************************/
158 /*! \brief BBM_ATTRIBUTES_T macro for robustly obtaining the type of the
159 attributes tuple in _unevaluated_ context.
160 *******************************************************************/
161#define BBM_ATTRIBUTES_T std::conditional_t<attribute_tuple_t::size == 0, typename reflection_base_t::attribute_tuple_t, attribute_tuple_t>
162
163
164 /*** Implementationd details ***/
165 namespace detail {
166 template<typename T> struct attributes_impl
167 {
168 using type = bbm::named<std::tuple<>>;
169 static inline constexpr auto value(T&&) { return type{}; }
170 };
171
172 //! \brief void specialization
173 template<> struct attributes_impl<void>
174 {
175 using type = bbm::named<std::tuple<>>;
176 };
177
178 //! \brief Specialization: has attributes => attribute_tuple
179 template<typename T> requires concepts::reflection::attributes<T>
180 struct attributes_impl<T>
181 {
182 using type = typename std::decay_t<T>::attribute_tuple_t;
183 static inline constexpr auto value(T&& t) { return t.attribute_tuple(); }
184 };
185
186 //! \brief Specialization: has basetype, but no attributes => reflection_base::attribute_tuple
187 template<typename T> requires (concepts::reflection::basetypes<T> && !concepts::reflection::attributes<T>)
188 struct attributes_impl<T>
189 {
190 using type = typename std::decay_t<T>::reflection_base_t::attribute_tuple_t;
191 static inline constexpr auto value(T&& t) { return std::decay_t<T>::reflection_base_t::attribute_tuple(std::forward<T>(t)); }
192 };
193 } // end detail namespace
194
195
196 /********************************************************************/
197 /* @{ \name Attribute relfection methods
198 *********************************************************************/
199 template<typename T>
200 inline constexpr decltype(auto) attributes(T&& t)
201 {
202 return bbm::reflection::detail::attributes_impl<T>::value(std::forward<T>(t));
203 }
204
205 template<typename T>
207
208 template<typename T>
209 static constexpr size_t attributes_size = std::tuple_size< attributes_t<T> >::value;
210 //! @}
211
212 /********************************************************************/
213 /*! \brief BaseTypes macro for declaring the base types/classes of a class
214 ********************************************************************/
215#define BBM_BASETYPES(...) using reflection_base_t = bbm::reflection::detail::base_types<__VA_ARGS__>;
216
217
218 /********************************************************************/
219 /*! \brief Wrapper for adding a prefix to the attribute names of a base type.
220 ********************************************************************/
221 template<string_literal N, typename C> struct prefix {};
222
223
224 /*** Implementation detail for reflection_base_t ***/
225 namespace detail {
226
227 //! \brief base_type wrapper; default case base type/class has no attribute reflection
228 template<typename Cs>
229 struct base_type
230 {
231 using type = std::tuple<Cs>;
233
234 template<typename C> static inline constexpr auto attribute_tuple(C&) { return attribute_tuple_t{}; }
235 };
236
237 //! \brief base_type specialization when a base class does not have attribute reflection, but its parent might have
238 template<typename Cs> requires (!concepts::reflection::attributes<Cs> && concepts::reflection::basetypes<Cs>)
239 struct base_type<Cs>
240 {
241 using type = std::tuple<Cs>;
242 using attribute_tuple_t = typename std::decay_t<Cs>::reflection_base_t::attribute_tuple_t;
243
244 template<typename C>
245 static inline constexpr auto attribute_tuple(C& This)
246 {
247 return std::decay_t<Cs>::reflection_base::attribute_tuple(This);
248 }
249 };
250
251 //! \brief base_type specialization for when the base class also has attribute reflection
252 template<typename Cs> requires concepts::reflection::attributes<Cs>
253 struct base_type<Cs>
254 {
255 using type = std::tuple<Cs>;
256 using attribute_tuple_t = typename std::decay_t<Cs>::attribute_tuple_t;
257
258 template<typename C>
259 static inline constexpr auto attribute_tuple(C& This)
260 {
261 if constexpr (is_const_v<C>) return bbm::reflection::attributes( static_cast<const Cs&>(This) );
262 else return bbm::reflection::attributes( static_cast<Cs&>(This) );
263 }
264 };
265
266 //! \brief base_type specialization for when a prefix is added to the base type's attributes
267 template<string_literal N, typename Cs>
268 struct base_type<prefix<N, Cs>>
269 {
270 using type = std::tuple<typename base_type<Cs>::type>;
272
273 template<typename C>
274 static inline constexpr auto attribute_tuple(C& This)
275 {
276 return bbm::prefix_names<N>( base_type<Cs>::attribute_tuple(This) );
277 }
278 };
279
280 //! \brief Container class for multiple base_type
281 template<typename... Cs>
282 struct base_types
283 {
286
287 template<typename C>
288 static inline constexpr auto attribute_tuple(C& This)
289 {
290 return bbm::named_cat( base_type<Cs>::attribute_tuple(This)... );
291 }
292 };
293
294 //! \brief Container specialization for classes without base types.
295 template<>
296 struct base_types<>
297 {
298 using type = std::tuple<>;
300 template<typename C> static inline constexpr auto attribute_tuple(C&) { return attribute_tuple_t{}; }
301 };
302 } // end detail
303
304
305 /*** Implementation Details ***/
306 namespace detail {
307
308 template<typename C> struct basetypes_impl { using type = std::tuple<>; };
309
310 template<typename C> requires bbm::concepts::reflection::basetypes<C>
311 struct basetypes_impl<C>
312 {
313 using type = typename std::decay_t<C>::reflection_base_t::type;
314 };
315
316 } // end detail namespace
317
318 /********************************************************************/
319 /*! @{ \name BaseTypes reflection methods
320 ********************************************************************/
321 template<typename C>
322 using basetypes_t = typename bbm::reflection::detail::basetypes_impl<C>::type;
323
324 template<typename C>
325 static constexpr size_t basetypes_size = std::tuple_size< basetypes_t<C> >::value;
326 //! @}
327
328
329 /********************************************************************/
330 /*! \brief BBM_ENUM macro for declaring the types of an enum
331 ********************************************************************/
332#define BBM_ENUM(FlagName, ...) \
333 template<> struct bbm::reflection::bbm_enum<FlagName> \
334 { \
335 static constexpr auto values(void) \
336 { \
337 using enum FlagName; \
338 return bbm::make_named<BBM_STRINGIFY_EACH(__VA_ARGS__)>( std::array{ __VA_ARGS__ } ); \
339 } \
340 }; \
341
342 /********************************************************************/
343 /*! \brief Querry the names and values of an enum
344 ********************************************************************/
345 template<typename T> static constexpr auto enum_v = bbm_enum<std::decay_t<T>>::values();
346
347 } // end reflection namespace
348} // end bbm namespace
349
350//! @{ \name Global aliases.
351//! Fallback for classes when no BaseTypes or Attributes are defined. The
352//! reason for a global alias is because decltype(*this) cannot be used in
353//! an unevaluated context (i.e., attribute_tuple_t in Attributes(...).
354using reflection_base_t = bbm::reflection::detail::base_types<>;
356//! @}
357
358#endif /* _BBM_REFLECTION_H_ */
basetypes reflection allows to enumerate all base classes. The class has the following typename:
Definition: reflection.h:49
General macro utilities.
constexpr decltype(auto) attributes(T &&t)
Definition: reflection.h:200
static constexpr auto enum_v
Querry the names and values of an enum.
Definition: reflection.h:345
static constexpr size_t attributes_size
Definition: reflection.h:209
typename bbm::reflection::detail::attributes_impl< T >::type attributes_t
Definition: reflection.h:206
typename bbm::reflection::detail::basetypes_impl< C >::type basetypes_t
Definition: reflection.h:322
static constexpr size_t basetypes_size
Definition: reflection.h:325
Definition: reflection.h:18
Wrapper for adding a prefix to the attribute names of a base type.
Definition: reflection.h:221
Definition: aggregatebsdf.h:29
constexpr auto named_cat(T &&... t)
cat named types
Definition: named_util.h:40
decltype(prefix_names< PREFIX >(std::declval< T >())) prefix_names_t
type of named tuple with pre-fixed name.
Definition: named_util.h:108
decltype(named_cat(std::declval< T >()...)) named_cat_t
type of concatting multiple named tuples.
Definition: named_util.h:56
decltype(auto) value(T &&t)
return the value of an attribute, or if not an attribute the object
Definition: attribute_value.h:20
decltype(std::tuple_cat(std::declval< Ts >()...)) tuple_cat_t
tuple_cat_t
Definition: tuple.h:81
named container
Definition: named.h:131
Extensions for the STL tuple class.
A wrapper for STL containers such as tuple, pair, and array. These containers force the programmer to...
bbm::reflection::detail::base_types<> reflection_base_t
Definition: reflection.h:354