Motorcortex Core  version: 2.7.6
visit_struct_intrusive.h
1 // (C) Copyright 2015 - 2018 Christopher Beck
2 
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef VISIT_STRUCT_INTRUSIVE_HPP_INCLUDED
7 #define VISIT_STRUCT_INTRUSIVE_HPP_INCLUDED
8 
9 /***
10  * A collection of templates and macros supporting a second form of VISIT_STRUCT
11  * mechanism.
12  *
13  * In this version, the visitable members are declared *within* the body of the
14  * struct, at the same time that they are actually declared.
15  *
16  * This version uses templates for iteration rather than macros, so it's really
17  * fairly different. It is more DRY and less likely to produce gross error
18  * messages than the other, at the cost of being invasive to your structure
19  * definition.
20  *
21  * This version adds some typedefs to your class, and it invisibly adds some
22  * declarations of obscure member functions to your class. These declarations
23  * do not have corresponding definitions and generate no object code, they are
24  * merely a device for metaprogramming, exploiting overload resolution rules to
25  * create "state". In normal code, you won't be able to detect any of this.
26  *
27  * This sounds a lot more evil than it really is -- it is morally equivalent to
28  * `std::declval`, I would say, which is also specified to be a declaration with
29  * no definition, which you simply aren't permitted to odr-use.
30  */
31 
32 #include "visit_struct.h"
33 
34 namespace visit_struct {
35 
36 namespace detail {
37 
38 /***
39  * Poor man's mpl vector
40  */
41 
42 template <class... Ts>
43 struct TypeList {
44  static VISIT_STRUCT_CONSTEXPR const unsigned int size = sizeof...(Ts);
45 };
46 
47 // Append metafunction
48 template <class List, class T>
49 struct Append;
50 
51 template <class... Ts, class T>
52 struct Append<TypeList<Ts...>, T> {
53  typedef TypeList<Ts..., T> type;
54 };
55 
56 template<class L, class T>
57 using Append_t = typename Append<L, T>::type;
58 
59 // Cdr metafunction (cdr is a lisp function which returns the tail of a list)
60 template <class List>
61 struct Cdr;
62 
63 template <typename T, typename... Ts>
64 struct Cdr<TypeList<T, Ts...>> {
65  typedef TypeList<Ts...> type;
66 };
67 
68 template <class List>
69 using Cdr_t = typename Cdr<List>::type;
70 
71 // Find metafunction (get the idx'th element)
72 template <class List, unsigned idx>
73 struct Find : Find<Cdr_t<List>, idx - 1> {};
74 
75 template <typename T, typename... Ts>
76 struct Find<TypeList<T, Ts...>, 0> {
77  typedef T type;
78 };
79 
80 template <class List, unsigned idx>
81 using Find_t = typename Find<List, idx>::type;
82 
83 // Alias used when capturing references to string literals
84 
85 template <int N>
86 using char_array = const char [N];
87 
88 /***
89  * The "rank" template is a trick which can be used for
90  * certain metaprogramming techniques. It creates
91  * an inheritance hierarchy of trivial classes.
92  */
93 
94 template <int N>
95 struct Rank : Rank<N - 1> {};
96 
97 template <>
98 struct Rank<0> {};
99 
100 static VISIT_STRUCT_CONSTEXPR const int max_visitable_members_intrusive = 100;
101 
102 /***
103  * To create a "compile-time" TypeList whose members are accumulated one-by-one,
104  * the basic idea is to define a function, which takes a `Rank` object, and
105  * whose return type is the type representing the current value of the list.
106  *
107  * That function is not a template function -- it is defined as taking a
108  * particular rank object. Initially, it is defined only for `Rank<0>`.
109  *
110  * To add an element to the list, we define an overload of the function, which
111  * takes the next higher `Rank` as it's argument. It's return value is,
112  * the new value of the list, formed by using `Append_t` with the old value.
113  *
114  * To obtain the current value of the list, we use decltype with the name of the
115  * function, and `Rank<100>`, or some suitably large integer. The C++ standard
116  * specifies that overload resolution is in this case unambiguous and must
117  * select the overload for the "most-derived" type which matches.
118  *
119  * The upshot is that `decltype(my_function(Rank<100>{}))` is a single well-formed
120  * expression, which, because of C++ overload resolution rules, can be a
121  * "mutable" value from the point of view of metaprogramming.
122  *
123  *
124  * Attribution:
125  * I first learned this trick from a stackoverflow post by Roman Perepelitsa:
126  * http://stackoverflow.com/questions/4790721/c-type-registration-at-compile-time-trick
127  *
128  * He attributes it to a talk from Matt Calabrese at BoostCon 2011.
129  *
130  *
131  * The expression is inherently dangerous if you are using it inside the body
132  * of a struct -- obviously, it has different values at different points of the
133  * structure definition. The "END_VISITABLES" macro is important in that this
134  * finalizes the list, typedeffing `decltype(my_function(Rank<100>{}))` to some
135  * fixed name in your struct at a specific point in the definition. That
136  * typedef can only ultimately have one meaning, no matter where else the name
137  * may be used (even implicitly) in your structure definition. That typedef is
138  * what the trait defined in this header ultimately hooks into to find the
139  * visitable members.
140  */
141 
142 // A tag inserted into a structure to mark it as visitable
143 
144 struct intrusive_tag{};
145 
146 /***
147  * Helper structures which perform pack expansion in order to visit a structure.
148  */
149 
150 // In MSVC 2015, sometimes a pointer to member cannot be constexpr, for instance
151 // I had trouble with code like this:
152 //
153 // struct S {
154 // int a;
155 // static constexpr auto a_ptr = &S::a;
156 // };
157 //
158 // This works fine in gcc and clang.
159 // MSVC is okay with it if instead it is a template parameter it seems, so we
160 // use `member_ptr_helper` below as a workaround, a bit like so:
161 //
162 // struct S {
163 // int a;
164 // using a_helper = member_ptr_helper<S, int, &S::a>;
165 // };
166 
167 template <typename S, typename T, T S::*member_ptr>
168 struct member_ptr_helper {
169  static VISIT_STRUCT_CONSTEXPR T S::* get_ptr() { return member_ptr; }
170  using value_type = T;
171 
173 };
174 
175 // M should be derived from a member_ptr_helper
176 template <typename M>
177 struct member_helper {
178  template <typename V, typename S>
179  VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S && structure_instance) {
180  std::forward<V>(visitor)(M::member_name(), std::forward<S>(structure_instance).*M::get_ptr());
181  }
182 
183  template <typename V, typename S1, typename S2>
184  VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S1 && s1, S2 && s2) {
185  std::forward<V>(visitor)(M::member_name(),
186  std::forward<S1>(s1).*M::get_ptr(),
187  std::forward<S2>(s2).*M::get_ptr());
188  }
189 
190  template <typename V>
191  VISIT_STRUCT_CXX14_CONSTEXPR static void visit_pointers(V && visitor) {
192  std::forward<V>(visitor)(M::member_name(), M::get_ptr());
193  }
194 
195  template <typename V>
196  VISIT_STRUCT_CXX14_CONSTEXPR static void visit_accessors(V && visitor) {
197  std::forward<V>(visitor)(M::member_name(), typename M::accessor_t());
198  }
199 
200  template <typename V>
201  VISIT_STRUCT_CXX14_CONSTEXPR static void visit_types(V && visitor) {
202  std::forward<V>(visitor)(M::member_name(), visit_struct::type_c<typename M::value_type>{});
203  }
204 
205 };
206 
207 template <typename Mlist>
208 struct structure_helper;
209 
210 template <typename... Ms>
211 struct structure_helper<TypeList<Ms...>> {
212  template <typename V, typename S>
213  VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S && structure_instance) {
214  // Use parameter pack expansion to force evaluation of the member helper for each member in the list.
215  // Inside parens, a comma operator is being used to discard the void value and produce an integer, while
216  // not being an unevaluated context. The order of evaluation here is enforced by the compiler.
217  // Extra zero at the end is to avoid UB for having a zero-size array.
218  int dummy[] = {(member_helper<Ms>::apply_visitor(std::forward<V>(visitor), std::forward<S>(structure_instance)), 0)..., 0};
219  // Suppress unused warnings, even in case of empty parameter pack
220  static_cast<void>(dummy);
221  static_cast<void>(visitor);
222  static_cast<void>(structure_instance);
223  }
224 
225  template <typename V, typename S1, typename S2>
226  VISIT_STRUCT_CXX14_CONSTEXPR static void apply_visitor(V && visitor, S1 && s1, S2 && s2) {
227  int dummy[] = {(member_helper<Ms>::apply_visitor(std::forward<V>(visitor), std::forward<S1>(s1), std::forward<S2>(s2)), 0)..., 0};
228  static_cast<void>(dummy);
229  static_cast<void>(visitor);
230  static_cast<void>(s1);
231  static_cast<void>(s2);
232  }
233 
234  template <typename V>
235  VISIT_STRUCT_CXX14_CONSTEXPR static void visit_pointers(V && visitor) {
236  int dummy[] = {(member_helper<Ms>::visit_pointers(std::forward<V>(visitor)), 0)..., 0};
237  static_cast<void>(dummy);
238  static_cast<void>(visitor);
239  }
240 
241  template <typename V>
242  VISIT_STRUCT_CXX14_CONSTEXPR static void visit_accessors(V && visitor) {
243  int dummy[] = {(member_helper<Ms>::visit_accessors(std::forward<V>(visitor)), 0)..., 0};
244  static_cast<void>(dummy);
245  static_cast<void>(visitor);
246  }
247 
248  template <typename V>
249  VISIT_STRUCT_CXX14_CONSTEXPR static void visit_types(V && visitor) {
250  int dummy[] = {(member_helper<Ms>::visit_types(std::forward<V>(visitor)), 0)..., 0};
251  static_cast<void>(dummy);
252  static_cast<void>(visitor);
253  }
254 };
255 
256 
257 } // end namespace detail
258 
259 
260 /***
261  * Implement trait
262  */
263 
264 namespace traits {
265 
266 template <typename T>
267 struct visitable <T,
268  typename std::enable_if<
269  std::is_same<typename T::Visit_Struct_Visitable_Structure_Tag__,
270  ::visit_struct::detail::intrusive_tag
271  >::value
272  >::type
273  >
274 {
275  static VISIT_STRUCT_CONSTEXPR const std::size_t field_count = T::Visit_Struct_Registered_Members_List__::size;
276 
277  // Apply to an instance
278  // S should be the same type as T modulo const and reference
279  template <typename V, typename S>
280  static VISIT_STRUCT_CXX14_CONSTEXPR void apply(V && v, S && s) {
281  detail::structure_helper<typename T::Visit_Struct_Registered_Members_List__>::apply_visitor(std::forward<V>(v), std::forward<S>(s));
282  }
283 
284  // Apply with two instances
285  template <typename V, typename S1, typename S2>
286  static VISIT_STRUCT_CXX14_CONSTEXPR void apply(V && v, S1 && s1, S2 && s2) {
287  detail::structure_helper<typename T::Visit_Struct_Registered_Members_List__>::apply_visitor(std::forward<V>(v), std::forward<S1>(s1), std::forward<S2>(s2));
288  }
289 
290  // Apply with no instance
291  template <typename V>
292  static VISIT_STRUCT_CXX14_CONSTEXPR void visit_pointers(V && v) {
294  }
295 
296  template <typename V>
297  static VISIT_STRUCT_CXX14_CONSTEXPR void visit_types(V && v) {
299  }
300 
301  template <typename V>
302  static VISIT_STRUCT_CXX14_CONSTEXPR void visit_accessors(V && v) {
304  }
305 
306  // Get pointer
307  template <int idx>
308  static VISIT_STRUCT_CONSTEXPR auto get_pointer(std::integral_constant<int, idx>)
309  -> decltype(detail::Find_t<typename T::Visit_Struct_Registered_Members_List__, idx>::get_ptr())
310  {
311  return detail::Find_t<typename T::Visit_Struct_Registered_Members_List__, idx>::get_ptr();
312  }
313 
314  // Get accessor
315  template <int idx>
316  static VISIT_STRUCT_CONSTEXPR auto get_accessor(std::integral_constant<int, idx>)
317  -> typename detail::Find_t<typename T::Visit_Struct_Registered_Members_List__, idx>::accessor_t
318  {
319  return {};
320  }
321 
322  // Get value
323  template <int idx, typename S>
324  static VISIT_STRUCT_CONSTEXPR auto get_value(std::integral_constant<int, idx> tag, S && s)
325  -> decltype(std::forward<S>(s).*get_pointer(tag))
326  {
327  return std::forward<S>(s).*get_pointer(tag);
328  }
329 
330  // Get name
331  template <int idx>
332  static VISIT_STRUCT_CONSTEXPR auto get_name(std::integral_constant<int, idx>)
333  -> decltype(detail::Find_t<typename T::Visit_Struct_Registered_Members_List__, idx>::member_name())
334  {
335  return detail::Find_t<typename T::Visit_Struct_Registered_Members_List__, idx>::member_name();
336  }
337 
338  // Get type
339  template <int idx>
340  static auto type_at(std::integral_constant<int, idx>)
342 
343  // Get name of structure
344  static VISIT_STRUCT_CONSTEXPR decltype(T::Visit_Struct_Get_Name__()) get_name() {
345  return T::Visit_Struct_Get_Name__();
346  }
347 
348  static VISIT_STRUCT_CONSTEXPR const bool value = true;
349 };
350 
351 } // end namespace trait
352 
353 } // end namespace visit_struct
354 
355 // Macros to be used within a structure definition
356 
357 #define VISIT_STRUCT_GET_REGISTERED_MEMBERS decltype(Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank<visit_struct::detail::max_visitable_members_intrusive>{}))
358 
359 #define VISIT_STRUCT_MAKE_MEMBER_NAME(NAME) Visit_Struct_Member_Record__##NAME
360 
361 #define BEGIN_VISITABLES(NAME) \
362 typedef NAME VISIT_STRUCT_CURRENT_TYPE; \
363 static VISIT_STRUCT_CONSTEXPR decltype(#NAME) Visit_Struct_Get_Name__() { \
364  return #NAME; \
365 } \
366 ::visit_struct::detail::TypeList<> static inline Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank<0>); \
367 static_assert(true, "")
368 
369 #define VISITABLE(TYPE, NAME) \
370 TYPE NAME; \
371 struct VISIT_STRUCT_MAKE_MEMBER_NAME(NAME) : \
372  visit_struct::detail::member_ptr_helper<VISIT_STRUCT_CURRENT_TYPE, \
373  TYPE, \
374  &VISIT_STRUCT_CURRENT_TYPE::NAME> \
375 { \
376  static VISIT_STRUCT_CONSTEXPR const ::visit_struct::detail::char_array<sizeof(#NAME)> & member_name() { \
377  return #NAME; \
378  } \
379 }; \
380 static inline ::visit_struct::detail::Append_t<VISIT_STRUCT_GET_REGISTERED_MEMBERS, \
381  VISIT_STRUCT_MAKE_MEMBER_NAME(NAME)> \
382  Visit_Struct_Get_Visitables__(::visit_struct::detail::Rank<VISIT_STRUCT_GET_REGISTERED_MEMBERS::size + 1>); \
383 static_assert(true, "")
384 
385 #define END_VISITABLES \
386 typedef VISIT_STRUCT_GET_REGISTERED_MEMBERS Visit_Struct_Registered_Members_List__; \
387 typedef ::visit_struct::detail::intrusive_tag Visit_Struct_Visitable_Structure_Tag__; \
388 static_assert(true, "")
389 
390 
391 #endif // VISIT_STRUCT_INTRUSIVE_HPP_INCLUDED
visit_struct::detail::member_ptr_helper
Definition: visit_struct_intrusive.h:210
visit_struct::type_c
Definition: visit_struct.h:86
visit_struct::detail::member_helper
Definition: visit_struct_intrusive.h:219
visit_struct::detail::TypeList
Definition: visit_struct_intrusive.h:85
visit_struct::detail::structure_helper
Definition: visit_struct_intrusive.h:250
visit_struct::detail::Rank
Definition: visit_struct_intrusive.h:137
visit_struct::accessor
Definition: visit_struct.h:92
visit_struct::detail::intrusive_tag
Definition: visit_struct_intrusive.h:186