EnTT  3.5.0
factory.hpp
1 #ifndef ENTT_META_FACTORY_HPP
2 #define ENTT_META_FACTORY_HPP
3 
4 
5 #include <array>
6 #include <cstddef>
7 #include <functional>
8 #include <tuple>
9 #include <type_traits>
10 #include <utility>
11 #include "../config/config.h"
12 #include "../core/fwd.hpp"
13 #include "../core/type_info.hpp"
14 #include "../core/type_traits.hpp"
15 #include "internal.hpp"
16 #include "meta.hpp"
17 #include "policy.hpp"
18 
19 
20 namespace entt {
21 
22 
29 namespace internal {
30 
31 
32 template<typename, bool = false>
33 struct meta_function_helper;
34 
35 
36 template<typename Ret, typename... Args, bool Const>
37 struct meta_function_helper<Ret(Args...), Const> {
38  using return_type = std::remove_cv_t<std::remove_reference_t<Ret>>;
39  using args_type = std::tuple<std::remove_cv_t<std::remove_reference_t<Args>>...>;
40 
41  static constexpr auto is_const = Const;
42 
43  [[nodiscard]] static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT {
44  return std::array<meta_type_node *, sizeof...(Args)>{{meta_info<Args>::resolve()...}}[index];
45  }
46 };
47 
48 
49 template<typename Ret, typename... Args, typename Class>
50 constexpr meta_function_helper<Ret(Args...), true>
51 to_meta_function_helper(Ret(Class:: *)(Args...) const);
52 
53 
54 template<typename Ret, typename... Args, typename Class>
55 constexpr meta_function_helper<Ret(Args...)>
56 to_meta_function_helper(Ret(Class:: *)(Args...));
57 
58 
59 template<typename Ret, typename... Args>
60 constexpr meta_function_helper<Ret(Args...)>
61 to_meta_function_helper(Ret(*)(Args...));
62 
63 
64 constexpr void to_meta_function_helper(...);
65 
66 
67 template<typename Candidate>
68 using meta_function_helper_t = decltype(to_meta_function_helper(std::declval<Candidate>()));
69 
70 
71 template<typename Type, typename... Args, std::size_t... Indexes>
72 [[nodiscard]] meta_any construct(meta_any * const args, std::index_sequence<Indexes...>) {
73  [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast<Args>()...);
74  return ((std::get<Indexes>(direct) || (args+Indexes)->convert<Args>()) && ...)
75  ? Type{(std::get<Indexes>(direct) ? *std::get<Indexes>(direct) : (args+Indexes)->cast<Args>())...}
76  : meta_any{};
77 }
78 
79 
80 template<typename Type, auto Data>
81 [[nodiscard]] bool setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) {
82  bool accepted = false;
83 
84  if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
85  using helper_type = meta_function_helper_t<decltype(Data)>;
86  using data_type = std::tuple_element_t<!std::is_member_function_pointer_v<decltype(Data)>, typename helper_type::args_type>;
87 
88  if(auto * const clazz = instance->try_cast<Type>(); clazz) {
89  if(auto * const direct = value.try_cast<data_type>(); direct || value.convert<data_type>()) {
90  std::invoke(Data, *clazz, direct ? *direct : value.cast<data_type>());
91  accepted = true;
92  }
93  }
94  } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
95  using data_type = std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>;
96 
97  if constexpr(!std::is_array_v<data_type>) {
98  if(auto * const clazz = instance->try_cast<Type>(); clazz) {
99  if(auto * const direct = value.try_cast<data_type>(); direct || value.convert<data_type>()) {
100  std::invoke(Data, clazz) = (direct ? *direct : value.cast<data_type>());
101  accepted = true;
102  }
103  }
104  }
105  } else {
106  using data_type = std::remove_cv_t<std::remove_reference_t<decltype(*Data)>>;
107 
108  if constexpr(!std::is_array_v<data_type>) {
109  if(auto * const direct = value.try_cast<data_type>(); direct || value.convert<data_type>()) {
110  *Data = (direct ? *direct : value.cast<data_type>());
111  accepted = true;
112  }
113  }
114  }
115 
116  return accepted;
117 }
118 
119 
120 template<typename Type, auto Data, typename Policy>
121 [[nodiscard]] meta_any getter([[maybe_unused]] meta_handle instance) {
122  [[maybe_unused]] auto dispatch = [](auto &&value) {
123  if constexpr(std::is_same_v<Policy, as_void_t>) {
124  return meta_any{std::in_place_type<void>, std::forward<decltype(value)>(value)};
125  } else if constexpr(std::is_same_v<Policy, as_ref_t>) {
126  return meta_any{std::ref(std::forward<decltype(value)>(value))};
127  } else {
128  static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
129  return meta_any{std::forward<decltype(value)>(value)};
130  }
131  };
132 
133  if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>> || std::is_member_function_pointer_v<decltype(Data)>) {
134  auto * const clazz = instance->try_cast<Type>();
135  return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{};
136  } else if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
137  if constexpr(std::is_array_v<std::remove_cv_t<std::remove_reference_t<decltype(std::declval<Type>().*Data)>>>) {
138  return meta_any{};
139  } else {
140  auto * const clazz = instance->try_cast<Type>();
141  return clazz ? dispatch(std::invoke(Data, clazz)) : meta_any{};
142  }
143  } else if constexpr(std::is_pointer_v<std::decay_t<decltype(Data)>>) {
144  if constexpr(std::is_array_v<std::remove_pointer_t<decltype(Data)>>) {
145  return meta_any{};
146  } else {
147  return dispatch(*Data);
148  }
149  } else {
150  return dispatch(Data);
151  }
152 }
153 
154 
155 template<typename Type, auto Candidate, typename Policy, std::size_t... Indexes>
156 [[nodiscard]] meta_any invoke([[maybe_unused]] meta_handle instance, meta_any *args, std::index_sequence<Indexes...>) {
157  using helper_type = meta_function_helper_t<decltype(Candidate)>;
158 
159  auto dispatch = [](auto *... params) {
160  if constexpr(std::is_void_v<typename helper_type::return_type> || std::is_same_v<Policy, as_void_t>) {
161  std::invoke(Candidate, *params...);
162  return meta_any{std::in_place_type<void>};
163  } else if constexpr(std::is_same_v<Policy, as_ref_t>) {
164  return meta_any{std::ref(std::invoke(Candidate, *params...))};
165  } else {
166  static_assert(std::is_same_v<Policy, as_is_t>, "Policy not supported");
167  return meta_any{std::invoke(Candidate, *params...)};
168  }
169  };
170 
171  [[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *value) {
172  using arg_type = std::remove_reference_t<decltype(*value)>;
173 
174  if(!value && any->convert<arg_type>()) {
175  value = any->try_cast<arg_type>();
176  }
177 
178  return value;
179  }(args+Indexes, (args+Indexes)->try_cast<std::tuple_element_t<Indexes, typename helper_type::args_type>>())...);
180 
181  if constexpr(std::is_function_v<std::remove_reference_t<std::remove_pointer_t<decltype(Candidate)>>>) {
182  return (std::get<Indexes>(direct) && ...) ? dispatch(std::get<Indexes>(direct)...) : meta_any{};
183  } else {
184  auto * const clazz = instance->try_cast<Type>();
185  return (clazz && (std::get<Indexes>(direct) && ...)) ? dispatch(clazz, std::get<Indexes>(direct)...) : meta_any{};
186  }
187 }
188 
189 
190 }
191 
192 
207 template<typename...>
209 
210 
216 template<typename Type, typename... Spec>
217 class meta_factory<Type, Spec...>: public meta_factory<Type> {
218  [[nodiscard]] bool exists(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT {
219  return node && (node->key() == key || exists(key, node->next));
220  }
221 
222  template<std::size_t Step = 0, std::size_t... Index, typename... Property, typename... Other>
223  void unpack(std::index_sequence<Index...>, std::tuple<Property...> property, Other &&... other) {
224  unroll<Step>(choice<3>, std::move(std::get<Index>(property))..., std::forward<Other>(other)...);
225  }
226 
227  template<std::size_t Step = 0, typename... Property, typename... Other>
228  void unroll(choice_t<3>, std::tuple<Property...> property, Other &&... other) {
229  unpack<Step>(std::index_sequence_for<Property...>{}, std::move(property), std::forward<Other>(other)...);
230  }
231 
232  template<std::size_t Step = 0, typename... Property, typename... Other>
233  void unroll(choice_t<2>, std::pair<Property...> property, Other &&... other) {
234  assign<Step>(std::move(property.first), std::move(property.second));
235  unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
236  }
237 
238  template<std::size_t Step = 0, typename Property, typename... Other>
239  std::enable_if_t<!std::is_invocable_v<Property>>
240  unroll(choice_t<1>, Property &&property, Other &&... other) {
241  assign<Step>(std::forward<Property>(property));
242  unroll<Step+1>(choice<3>, std::forward<Other>(other)...);
243  }
244 
245  template<std::size_t Step = 0, typename Func, typename... Other>
246  void unroll(choice_t<0>, Func &&invocable, Other &&... other) {
247  unroll<Step>(choice<3>, std::forward<Func>(invocable)(), std::forward<Other>(other)...);
248  }
249 
250  template<std::size_t>
251  void unroll(choice_t<0>) {}
252 
253  template<std::size_t = 0, typename Key, typename... Value>
254  void assign(Key &&key, Value &&... value) {
255  static const auto property{std::make_tuple(std::forward<Key>(key), std::forward<Value>(value)...)};
256 
257  static internal::meta_prop_node node{
258  nullptr,
259  []() -> meta_any {
260  return std::get<0>(property);
261  },
262  []() -> meta_any {
263  if constexpr(sizeof...(Value) == 0) {
264  return {};
265  } else {
266  return std::get<1>(property);
267  }
268  }
269  };
270 
271  ENTT_ASSERT(!exists(node.key(), *curr));
272  node.next = *curr;
273  *curr = &node;
274  }
275 
276 public:
281  meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT
282  : curr{target}
283  {}
284 
296  template<typename PropertyOrKey, typename... Value>
297  auto prop(PropertyOrKey &&property_or_key, Value &&... value) && {
298  if constexpr(sizeof...(Value) == 0) {
299  unroll(choice<3>, std::forward<PropertyOrKey>(property_or_key));
300  } else {
301  assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
302  }
303 
304  return meta_factory<Type, Spec..., PropertyOrKey, Value...>{curr};
305  }
306 
317  template <typename... Property>
318  auto props(Property... property) && {
319  unroll(choice<3>, std::forward<Property>(property)...);
320  return meta_factory<Type, Spec..., Property...>{curr};
321  }
322 
323 private:
324  internal::meta_prop_node **curr;
325 };
326 
327 
332 template<typename Type>
333 class meta_factory<Type> {
334  template<typename Node>
335  bool exists(const Node *candidate, const Node *node) ENTT_NOEXCEPT {
336  return node && (node == candidate || exists(candidate, node->next));
337  }
338 
339  template<typename Node>
340  bool exists(const id_type id, const Node *node) ENTT_NOEXCEPT {
341  return node && (node->id == id || exists(id, node->next));
342  }
343 
344 public:
350  auto type(const id_type id = type_info<Type>::id()) {
351  auto * const node = internal::meta_info<Type>::resolve();
352 
353  ENTT_ASSERT(!exists(id, *internal::meta_context::global()));
354  ENTT_ASSERT(!exists(node, *internal::meta_context::global()));
355  node->id = id;
356  node->next = *internal::meta_context::global();
357  *internal::meta_context::global() = node;
358 
359  return meta_factory<Type, Type>{&node->prop};
360  }
361 
370  template<typename Base>
371  auto base() ENTT_NOEXCEPT {
372  static_assert(std::is_base_of_v<Base, Type>, "Invalid base type");
373  auto * const type = internal::meta_info<Type>::resolve();
374 
375  static internal::meta_base_node node{
376  type,
377  nullptr,
378  &internal::meta_info<Base>::resolve,
379  [](const void *instance) ENTT_NOEXCEPT -> const void * {
380  return static_cast<const Base *>(static_cast<const Type *>(instance));
381  }
382  };
383 
384  ENTT_ASSERT(!exists(&node, type->base));
385  node.next = type->base;
386  type->base = &node;
387 
388  return meta_factory<Type>{};
389  }
390 
400  template<typename To>
401  auto conv() ENTT_NOEXCEPT {
402  static_assert(std::is_convertible_v<Type, To>, "Could not convert to the required type");
403  auto * const type = internal::meta_info<Type>::resolve();
404 
405  static internal::meta_conv_node node{
406  type,
407  nullptr,
408  &internal::meta_info<To>::resolve,
409  [](const void *instance) -> meta_any {
410  return static_cast<To>(*static_cast<const Type *>(instance));
411  }
412  };
413 
414  ENTT_ASSERT(!exists(&node, type->conv));
415  node.next = type->conv;
416  type->conv = &node;
417 
418  return meta_factory<Type>{};
419  }
420 
433  template<auto Candidate>
434  auto conv() ENTT_NOEXCEPT {
435  using conv_type = std::invoke_result_t<decltype(Candidate), Type &>;
436  auto * const type = internal::meta_info<Type>::resolve();
437 
438  static internal::meta_conv_node node{
439  type,
440  nullptr,
441  &internal::meta_info<conv_type>::resolve,
442  [](const void *instance) -> meta_any {
443  return std::invoke(Candidate, *static_cast<const Type *>(instance));
444  }
445  };
446 
447  ENTT_ASSERT(!exists(&node, type->conv));
448  node.next = type->conv;
449  type->conv = &node;
450 
451  return meta_factory<Type>{};
452  }
453 
467  template<auto Func, typename Policy = as_is_t>
468  auto ctor() ENTT_NOEXCEPT {
469  using helper_type = internal::meta_function_helper_t<decltype(Func)>;
470  static_assert(std::is_same_v<typename helper_type::return_type, Type>, "The function doesn't return an object of the required type");
471  auto * const type = internal::meta_info<Type>::resolve();
472 
473  static internal::meta_ctor_node node{
474  type,
475  nullptr,
476  nullptr,
477  std::tuple_size_v<typename helper_type::args_type>,
478  &helper_type::arg,
479  [](meta_any * const any) {
480  return internal::invoke<Type, Func, Policy>({}, any, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
481  }
482  };
483 
484  ENTT_ASSERT(!exists(&node, type->ctor));
485  node.next = type->ctor;
486  type->ctor = &node;
487 
488  return meta_factory<Type, std::integral_constant<decltype(Func), Func>>{&node.prop};
489  }
490 
501  template<typename... Args>
502  auto ctor() ENTT_NOEXCEPT {
503  using helper_type = internal::meta_function_helper_t<Type(*)(Args...)>;
504  auto * const type = internal::meta_info<Type>::resolve();
505 
506  static internal::meta_ctor_node node{
507  type,
508  nullptr,
509  nullptr,
510  std::tuple_size_v<typename helper_type::args_type>,
511  &helper_type::arg,
512  [](meta_any * const any) {
513  return internal::construct<Type, std::remove_cv_t<std::remove_reference_t<Args>>...>(any, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
514  }
515  };
516 
517  ENTT_ASSERT(!exists(&node, type->ctor));
518  node.next = type->ctor;
519  type->ctor = &node;
520 
521  return meta_factory<Type, Type(Args...)>{&node.prop};
522  }
523 
540  template<auto Func>
541  auto dtor() ENTT_NOEXCEPT {
542  static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
543  auto * const type = internal::meta_info<Type>::resolve();
544 
545  ENTT_ASSERT(!type->dtor);
546 
547  type->dtor = [](void *instance) {
548  if(instance) {
549  std::invoke(Func, *static_cast<Type *>(instance));
550  }
551  };
552 
553  return meta_factory<Type>{};
554  }
555 
569  template<auto Data, typename Policy = as_is_t>
570  auto data(const id_type id) ENTT_NOEXCEPT {
571  if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
572  return data<Data, Data, Policy>(id);
573  } else {
574  using data_type = std::remove_pointer_t<std::decay_t<decltype(Data)>>;
575  auto * const type = internal::meta_info<Type>::resolve();
576 
577  static internal::meta_data_node node{
578  {},
579  type,
580  nullptr,
581  nullptr,
582  true,
583  &internal::meta_info<data_type>::resolve,
584  []() -> std::remove_const_t<decltype(internal::meta_data_node::set)> {
585  if constexpr(std::is_same_v<Type, data_type> || std::is_const_v<data_type>) {
586  return nullptr;
587  } else {
588  return &internal::setter<Type, Data>;
589  }
590  }(),
591  &internal::getter<Type, Data, Policy>
592  };
593 
594  ENTT_ASSERT(!exists(id, type->data));
595  ENTT_ASSERT(!exists(&node, type->data));
596  node.id = id;
597  node.next = type->data;
598  type->data = &node;
599 
600  return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop};
601  }
602  }
603 
624  template<auto Setter, auto Getter, typename Policy = as_is_t>
625  auto data(const id_type id) ENTT_NOEXCEPT {
626  using underlying_type = std::remove_reference_t<std::invoke_result_t<decltype(Getter), Type &>>;
627  auto * const type = internal::meta_info<Type>::resolve();
628 
629  static internal::meta_data_node node{
630  {},
631  type,
632  nullptr,
633  nullptr,
634  false,
635  &internal::meta_info<underlying_type>::resolve,
636  []() -> std::remove_const_t<decltype(internal::meta_data_node::set)> {
637  if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t> || (std::is_member_object_pointer_v<decltype(Setter)> && std::is_const_v<underlying_type>)) {
638  return nullptr;
639  } else {
640  return &internal::setter<Type, Setter>;
641  }
642  }(),
643  &internal::getter<Type, Getter, Policy>
644  };
645 
646  ENTT_ASSERT(!exists(id, type->data));
647  ENTT_ASSERT(!exists(&node, type->data));
648  node.id = id;
649  node.next = type->data;
650  type->data = &node;
651 
652  return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
653  }
654 
668  template<auto Candidate, typename Policy = as_is_t>
669  auto func(const id_type id) ENTT_NOEXCEPT {
670  using helper_type = internal::meta_function_helper_t<decltype(Candidate)>;
671  auto * const type = internal::meta_info<Type>::resolve();
672 
673  static internal::meta_func_node node{
674  {},
675  type,
676  nullptr,
677  nullptr,
678  std::tuple_size_v<typename helper_type::args_type>,
679  helper_type::is_const,
680  !std::is_member_function_pointer_v<decltype(Candidate)>,
681  &internal::meta_info<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, typename helper_type::return_type>>::resolve,
682  &helper_type::arg,
683  [](meta_handle instance, meta_any *args) {
684  return internal::invoke<Type, Candidate, Policy>(*instance, args, std::make_index_sequence<std::tuple_size_v<typename helper_type::args_type>>{});
685  }
686  };
687 
688  ENTT_ASSERT(!exists(id, type->func));
689  ENTT_ASSERT(!exists(&node, type->func));
690  node.id = id;
691  node.next = type->func;
692  type->func = &node;
693 
694  return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
695  }
696 };
697 
698 
710 template<typename Type>
711 [[nodiscard]] auto meta() ENTT_NOEXCEPT {
712  auto * const node = internal::meta_info<Type>::resolve();
713  // extended meta factory to allow assigning properties to opaque meta types
714  return meta_factory<Type, Type>{&node->prop};
715 }
716 
717 
718 }
719 
720 
721 #endif
entt::meta_any
Opaque wrapper for values of any type.
Definition: meta.hpp:164
entt::meta
auto meta() noexcept
Utility function to use for reflection.
Definition: factory.hpp:711
entt::meta_factory< Type >::base
auto base() noexcept
Assigns a meta base to a meta type.
Definition: factory.hpp:371
entt::meta_factory< Type >::func
auto func(const id_type id) noexcept
Assigns a meta funcion to a meta type.
Definition: factory.hpp:669
entt::id_type
std::uint32_t id_type
Alias declaration for type identifiers.
Definition: fwd.hpp:12
entt::meta_factory< Type, Spec... >::meta_factory
meta_factory(internal::meta_prop_node **target) noexcept
Constructs an extended factory from a given node.
Definition: factory.hpp:281
entt::meta_factory
Meta factory to be used for reflection purposes.
Definition: factory.hpp:208
entt::meta_factory< Type >::data
auto data(const id_type id) noexcept
Assigns a meta data to a meta type by means of its setter and getter.
Definition: factory.hpp:625
entt::meta_factory< Type >::ctor
auto ctor() noexcept
Assigns a meta constructor to a meta type.
Definition: factory.hpp:468
entt::type_info
Type info.
Definition: type_info.hpp:98
entt::meta_factory< Type, Spec... >::prop
auto prop(PropertyOrKey &&property_or_key, Value &&... value) &&
Assigns a property to the last meta object created.
Definition: factory.hpp:297
entt::meta_factory< Type >::type
auto type(const id_type id=type_info< Type >::id())
Makes a meta type searchable.
Definition: factory.hpp:350
entt
EnTT default namespace.
Definition: algorithm.hpp:13
entt::meta_factory< Type >::conv
auto conv() noexcept
Assigns a meta conversion function to a meta type.
Definition: factory.hpp:401
entt::meta_factory< Type, Spec... >::props
auto props(Property... property) &&
Assigns properties to the last meta object created.
Definition: factory.hpp:318
entt::resolve
meta_type resolve() noexcept
Returns the meta type associated with a given type.
Definition: resolve.hpp:20
entt::invoke
void invoke(basic_registry< Entity > &reg, const Entity entt)
Helper to create a listener that directly invokes a member function.
Definition: helper.hpp:124
entt::choice_t< 0 >
Utility class to disambiguate overloaded functions.
Definition: type_traits.hpp:64
entt::meta_factory< Type >::data
auto data(const id_type id) noexcept
Assigns a meta data to a meta type.
Definition: factory.hpp:570
entt::meta_factory< Type >::ctor
auto ctor() noexcept
Assigns a meta constructor to a meta type.
Definition: factory.hpp:502
entt::meta_handle
Opaque pointers to instances of any type.
Definition: meta.hpp:496
entt::meta_factory< Type >::dtor
auto dtor() noexcept
Assigns a meta destructor to a meta type.
Definition: factory.hpp:541
entt::meta_factory< Type >
Basic meta factory to be used for reflection purposes.
Definition: factory.hpp:333
entt::choice_t
Utility class to disambiguate overloaded functions.
Definition: type_traits.hpp:59