EnTT  3.10.0
factory.hpp
1 #ifndef ENTT_META_FACTORY_HPP
2 #define ENTT_META_FACTORY_HPP
3 
4 #include <algorithm>
5 #include <cstddef>
6 #include <functional>
7 #include <tuple>
8 #include <type_traits>
9 #include <utility>
10 #include "../config/config.h"
11 #include "../core/fwd.hpp"
12 #include "../core/type_info.hpp"
13 #include "../core/type_traits.hpp"
14 #include "meta.hpp"
15 #include "node.hpp"
16 #include "policy.hpp"
17 #include "range.hpp"
18 #include "utility.hpp"
19 
20 namespace entt {
21 
30 template<typename...>
32 
38 template<typename Type, typename... Spec>
39 class meta_factory<Type, Spec...>: public meta_factory<Type> {
40  void link_prop_if_required(internal::meta_prop_node &node) ENTT_NOEXCEPT {
41  if(meta_range<internal::meta_prop_node *, internal::meta_prop_node> range{*ref}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
42  ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [&node](const auto *curr) { return curr->id == node.id; }) == range.cend(), "Duplicate identifier");
43  node.next = *ref;
44  *ref = &node;
45  }
46  }
47 
48  template<std::size_t Step = 0, typename... Property, typename... Other>
49  void unroll(choice_t<2>, std::tuple<Property...> property, Other &&...other) ENTT_NOEXCEPT {
50  std::apply([this](auto &&...curr) { (this->unroll<Step>(choice<2>, std::forward<Property>(curr)...)); }, property);
51  unroll<Step + sizeof...(Property)>(choice<2>, std::forward<Other>(other)...);
52  }
53 
54  template<std::size_t Step = 0, typename... Property, typename... Other>
55  void unroll(choice_t<1>, std::pair<Property...> property, Other &&...other) ENTT_NOEXCEPT {
56  assign<Step>(std::move(property.first), std::move(property.second));
57  unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
58  }
59 
60  template<std::size_t Step = 0, typename Property, typename... Other>
61  void unroll(choice_t<0>, Property &&property, Other &&...other) ENTT_NOEXCEPT {
62  assign<Step>(std::forward<Property>(property));
63  unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
64  }
65 
66  template<std::size_t>
67  void unroll(choice_t<0>) ENTT_NOEXCEPT {}
68 
69  template<std::size_t = 0>
70  void assign(meta_any key, meta_any value = {}) {
71  static meta_any property[2u]{};
72 
73  static internal::meta_prop_node node{
74  nullptr,
75  property[0u],
76  property[1u]
77  // tricks clang-format
78  };
79 
80  property[0u] = std::move(key);
81  property[1u] = std::move(value);
82 
83  link_prop_if_required(node);
84  }
85 
86 public:
91  meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT
92  : ref{target} {}
93 
105  template<typename PropertyOrKey, typename... Value>
106  meta_factory<Type> prop(PropertyOrKey &&property_or_key, Value &&...value) {
107  if constexpr(sizeof...(Value) == 0) {
108  unroll(choice<2>, std::forward<PropertyOrKey>(property_or_key));
109  } else {
110  assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
111  }
112 
113  return {};
114  }
115 
125  template<typename... Property>
126  meta_factory<Type> props(Property... property) {
127  unroll(choice<2>, std::forward<Property>(property)...);
128  return {};
129  }
130 
131 private:
132  internal::meta_prop_node **ref;
133 };
134 
139 template<typename Type>
140 class meta_factory<Type> {
141  void link_base_if_required(internal::meta_base_node &node) ENTT_NOEXCEPT {
142  if(meta_range<internal::meta_base_node *, internal::meta_base_node> range{owner->base}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
143  node.next = owner->base;
144  owner->base = &node;
145  }
146  }
147 
148  void link_conv_if_required(internal::meta_conv_node &node) ENTT_NOEXCEPT {
149  if(meta_range<internal::meta_conv_node *, internal::meta_conv_node> range{owner->conv}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
150  node.next = owner->conv;
151  owner->conv = &node;
152  }
153  }
154 
155  void link_ctor_if_required(internal::meta_ctor_node &node) ENTT_NOEXCEPT {
156  if(meta_range<internal::meta_ctor_node *, internal::meta_ctor_node> range{owner->ctor}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
157  node.next = owner->ctor;
158  owner->ctor = &node;
159  }
160  }
161 
162  void link_data_if_required(const id_type id, internal::meta_data_node &node) ENTT_NOEXCEPT {
164  ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, &node](const auto *curr) { return curr != &node && curr->id == id; }) == range.cend(), "Duplicate identifier");
165  node.id = id;
166 
167  if(std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
168  node.next = owner->data;
169  owner->data = &node;
170  }
171  }
172 
173  void link_func_if_required(const id_type id, internal::meta_func_node &node) ENTT_NOEXCEPT {
174  node.id = id;
175 
176  if(meta_range<internal::meta_func_node *, internal::meta_func_node> range{owner->func}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) {
177  node.next = owner->func;
178  owner->func = &node;
179  }
180  }
181 
182  template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
183  auto data(const id_type id, std::index_sequence<Index...>) ENTT_NOEXCEPT {
184  using data_type = std::invoke_result_t<decltype(Getter), Type &>;
185  using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
186  static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
187 
188  static internal::meta_data_node node{
189  {},
190  /* this is never static */
191  (std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
192  nullptr,
193  nullptr,
194  Setter::size,
195  internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
196  &meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
197  [](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
198  &meta_getter<Type, Getter, Policy>
199  // tricks clang-format
200  };
201 
202  link_data_if_required(id, node);
203  return meta_factory<Type, Setter, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
204  }
205 
206 public:
208  meta_factory() ENTT_NOEXCEPT
209  : owner{internal::meta_node<Type>::resolve()} {}
210 
216  auto type(const id_type id = type_hash<Type>::value()) ENTT_NOEXCEPT {
217  meta_range<internal::meta_type_node *, internal::meta_type_node> range{*internal::meta_context::global()};
218  ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, this](const auto *curr) { return curr != owner && curr->id == id; }) == range.cend(), "Duplicate identifier");
219  owner->id = id;
220 
221  if(std::find(range.cbegin(), range.cend(), owner) == range.cend()) {
222  owner->next = *internal::meta_context::global();
223  *internal::meta_context::global() = owner;
224  }
225 
226  return meta_factory<Type, Type>{&owner->prop};
227  }
228 
237  template<typename Base>
238  auto base() ENTT_NOEXCEPT {
239  static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
240 
241  static internal::meta_base_node node{
242  nullptr,
243  internal::meta_node<Base>::resolve(),
244  [](meta_any other) ENTT_NOEXCEPT -> meta_any {
245  if(auto *ptr = other.data(); ptr) {
246  return forward_as_meta(*static_cast<Base *>(static_cast<Type *>(ptr)));
247  }
248 
249  return forward_as_meta(*static_cast<const Base *>(static_cast<const Type *>(std::as_const(other).data())));
250  }
251  // tricks clang-format
252  };
253 
254  link_base_if_required(node);
255  return meta_factory<Type>{};
256  }
257 
270  template<auto Candidate>
271  auto conv() ENTT_NOEXCEPT {
272  static internal::meta_conv_node node{
273  nullptr,
274  internal::meta_node<std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>>::resolve(),
275  [](const meta_any &instance) -> meta_any {
276  return forward_as_meta(std::invoke(Candidate, *static_cast<const Type *>(instance.data())));
277  }
278  // tricks clang-format
279  };
280 
281  link_conv_if_required(node);
282  return meta_factory<Type>{};
283  }
284 
294  template<typename To>
295  auto conv() ENTT_NOEXCEPT {
296  static internal::meta_conv_node node{
297  nullptr,
298  internal::meta_node<std::remove_cv_t<std::remove_reference_t<To>>>::resolve(),
299  [](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast<To>(*static_cast<const Type *>(instance.data()))); }
300  // tricks clang-format
301  };
302 
303  link_conv_if_required(node);
304  return meta_factory<Type>{};
305  }
306 
320  template<auto Candidate, typename Policy = as_is_t>
321  auto ctor() ENTT_NOEXCEPT {
322  using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
323  static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
324  static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
325 
326  static internal::meta_ctor_node node{
327  nullptr,
328  descriptor::args_type::size,
329  &meta_arg<typename descriptor::args_type>,
330  &meta_construct<Type, Candidate, Policy>
331  // tricks clang-format
332  };
333 
334  link_ctor_if_required(node);
335  return meta_factory<Type>{};
336  }
337 
348  template<typename... Args>
349  auto ctor() ENTT_NOEXCEPT {
350  using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
351 
352  static internal::meta_ctor_node node{
353  nullptr,
354  descriptor::args_type::size,
355  &meta_arg<typename descriptor::args_type>,
356  &meta_construct<Type, Args...>
357  // tricks clang-format
358  };
359 
360  link_ctor_if_required(node);
361  return meta_factory<Type>{};
362  }
363 
382  template<auto Func>
383  auto dtor() ENTT_NOEXCEPT {
384  static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
385  owner->dtor = [](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
386  return meta_factory<Type>{};
387  }
388 
402  template<auto Data, typename Policy = as_is_t>
403  auto data(const id_type id) ENTT_NOEXCEPT {
404  if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
405  using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>;
406 
407  static internal::meta_data_node node{
408  {},
409  /* this is never static */
410  std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
411  nullptr,
412  nullptr,
413  1u,
414  internal::meta_node<std::remove_const_t<data_type>>::resolve(),
415  &meta_arg<type_list<std::remove_const_t<data_type>>>,
416  &meta_setter<Type, Data>,
417  &meta_getter<Type, Data, Policy>
418  // tricks clang-format
419  };
420 
421  link_data_if_required(id, node);
422  return meta_factory<Type, std::integral_constant<decltype(Data), Data>, std::integral_constant<decltype(Data), Data>>{&node.prop};
423  } else {
424  using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
425 
426  static internal::meta_data_node node{
427  {},
428  ((std::is_same_v<Type, std::remove_const_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
429  nullptr,
430  nullptr,
431  1u,
432  internal::meta_node<std::remove_const_t<data_type>>::resolve(),
433  &meta_arg<type_list<std::remove_const_t<data_type>>>,
434  &meta_setter<Type, Data>,
435  &meta_getter<Type, Data, Policy>
436  // tricks clang-format
437  };
438 
439  link_data_if_required(id, node);
440  return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&node.prop};
441  }
442  }
443 
464  template<auto Setter, auto Getter, typename Policy = as_is_t>
465  auto data(const id_type id) ENTT_NOEXCEPT {
466  using data_type = std::invoke_result_t<decltype(Getter), Type &>;
467  static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
468 
469  if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
470  static internal::meta_data_node node{
471  {},
472  /* this is never static */
473  internal::meta_traits::is_const,
474  nullptr,
475  nullptr,
476  0u,
477  internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
478  &meta_arg<type_list<>>,
479  &meta_setter<Type, Setter>,
480  &meta_getter<Type, Getter, Policy>
481  // tricks clang-format
482  };
483 
484  link_data_if_required(id, node);
485  return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
486  } else {
487  using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
488 
489  static internal::meta_data_node node{
490  {},
491  /* this is never static nor const */
492  internal::meta_traits::is_none,
493  nullptr,
494  nullptr,
495  1u,
496  internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
497  &meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
498  &meta_setter<Type, Setter>,
499  &meta_getter<Type, Getter, Policy>
500  // tricks clang-format
501  };
502 
503  link_data_if_required(id, node);
504  return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&node.prop};
505  }
506  }
507 
525  template<typename Setter, auto Getter, typename Policy = as_is_t>
526  auto data(const id_type id) ENTT_NOEXCEPT {
527  return data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
528  }
529 
543  template<auto Candidate, typename Policy = as_is_t>
544  auto func(const id_type id) ENTT_NOEXCEPT {
545  using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
546  static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
547 
548  static internal::meta_func_node node{
549  {},
550  (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
551  nullptr,
552  nullptr,
553  descriptor::args_type::size,
554  internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>::resolve(),
555  &meta_arg<typename descriptor::args_type>,
556  &meta_invoke<Type, Candidate, Policy>
557  // tricks clang-format
558  };
559 
560  link_func_if_required(id, node);
561  return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
562  }
563 
564 private:
565  internal::meta_type_node *owner;
566 };
567 
579 template<typename Type>
580 [[nodiscard]] auto meta() ENTT_NOEXCEPT {
581  auto *const node = internal::meta_node<Type>::resolve();
582  // extended meta factory to allow assigning properties to opaque meta types
583  return meta_factory<Type, Type>{&node->prop};
584 }
585 
597 inline void meta_reset(const id_type id) ENTT_NOEXCEPT {
598  auto clear_chain = [](auto **curr, auto... member) {
599  for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) {
600  if constexpr(sizeof...(member) != 0u) {
601  static_assert(sizeof...(member) == 1u, "Assert in defense of the future me");
602  for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {}
603  }
604  }
605  };
606 
607  for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) {
608  if(auto *node = *it; node->id == id) {
609  clear_chain(&node->prop);
610  clear_chain(&node->base);
611  clear_chain(&node->conv);
612  clear_chain(&node->ctor);
613  clear_chain(&node->data, &internal::meta_data_node::prop);
614  clear_chain(&node->func, &internal::meta_func_node::prop);
615 
616  node->id = {};
617  node->dtor = nullptr;
618  *it = std::exchange(node->next, nullptr);
619 
620  break;
621  }
622  }
623 }
624 
632 template<typename Type>
633 void meta_reset() ENTT_NOEXCEPT {
634  meta_reset(internal::meta_node<Type>::resolve()->id);
635 }
636 
642 inline void meta_reset() ENTT_NOEXCEPT {
643  while(*internal::meta_context::global()) {
644  meta_reset((*internal::meta_context::global())->id);
645  }
646 }
647 
648 } // namespace entt
649 
650 #endif
Opaque wrapper for values of any type.
Definition: meta.hpp:141
meta_any as_ref()
Aliasing constructor.
Definition: meta.hpp:551
meta_factory< Type > prop(PropertyOrKey &&property_or_key, Value &&...value)
Assigns a property to the last meta object created.
Definition: factory.hpp:106
meta_factory< Type > props(Property... property)
Assigns properties to the last meta object created.
Definition: factory.hpp:126
meta_factory(internal::meta_prop_node **target)
Constructs an extended factory from a given node.
Definition: factory.hpp:91
Basic meta factory to be used for reflection purposes.
Definition: factory.hpp:140
auto type(const id_type id=type_hash< Type >::value())
Makes a meta type searchable.
Definition: factory.hpp:216
auto data(const id_type id)
Assigns a meta data to a meta type by means of its setter and getter.
Definition: factory.hpp:465
auto base()
Assigns a meta base to a meta type.
Definition: factory.hpp:238
auto ctor()
Assigns a meta constructor to a meta type.
Definition: factory.hpp:349
auto conv()
Assigns a meta conversion function to a meta type.
Definition: factory.hpp:271
auto ctor()
Assigns a meta constructor to a meta type.
Definition: factory.hpp:321
auto func(const id_type id)
Assigns a meta function to a meta type.
Definition: factory.hpp:544
meta_factory()
Default constructor.
Definition: factory.hpp:208
auto data(const id_type id)
Assigns a meta data to a meta type.
Definition: factory.hpp:403
auto dtor()
Assigns a meta destructor to a meta type.
Definition: factory.hpp:383
Meta factory to be used for reflection purposes.
Definition: factory.hpp:31
EnTT default namespace.
Definition: dense_map.hpp:22
meta_type resolve()
Returns the meta type associated with a given type.
Definition: resolve.hpp:19
std::uint32_t id_type
Alias declaration for type identifiers.
Definition: fwd.hpp:14
auto meta()
Utility function to use for reflection.
Definition: factory.hpp:580
meta_any meta_construct(meta_any *const args)
Tries to construct an instance given a list of erased parameters.
Definition: utility.hpp:359
void meta_reset(const id_type id)
Resets a type and all its parts.
Definition: factory.hpp:597
typename meta_function_helper< Type, Candidate >::type meta_function_helper_t
Helper type.
Definition: utility.hpp:154
meta_any forward_as_meta(Type &&value)
Forwards its argument and avoids copies for lvalue references.
Definition: meta.hpp:600
typename type_list_element< Index, List >::type type_list_element_t
Helper type.
bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value)
Sets the value of a given variable.
Definition: utility.hpp:197
Utility class to disambiguate overloaded functions.
Definition: type_traits.hpp:25
Utility class to disambiguate overloaded functions.
Definition: type_traits.hpp:21
Opaque pointers to instances of any type.
Definition: meta.hpp:611
Iterable range to use to iterate all types of meta objects.
Definition: range.hpp:74
const_iterator cend() const
Returns an iterator to the end.
Definition: range.hpp:110
Type hash.
Definition: type_info.hpp:100
A class to use to push around lists of types, nothing more.