diff --git a/TODO b/TODO index c4c1393cd..dd2a592a6 100644 --- a/TODO +++ b/TODO @@ -21,7 +21,7 @@ * multi component registry::remove and some others? * can I use opaque connection also for the emitter? * built-in support for dual (or N-) buffering -* meta: opaque references and pointers TODO * custom (decoupled) pools ==> double buffering, shared components, multi-model +* use direct access (pool-like) also for context variables diff --git a/docs/md/meta.md b/docs/md/meta.md index 26e08af56..b3da10354 100644 --- a/docs/md/meta.md +++ b/docs/md/meta.md @@ -195,13 +195,21 @@ entt::meta_any any{0}; entt::meta_any empty{}; ``` -It can be constructed or assigned by copy and move and it takes the burden of -destroying the contained object when required.
+It takes the burden of destroying the contained instance when required.
+Moreover, it can be used as an opaque container for unmanaged objects if needed: + +```cpp +int value; +entt::meta_any any{std::in_place, value}; +``` + +In this case, the contained instance is never destroyed and users must ensure +that the lifetime of the object exceeds that of the container. + A meta any object has a `type` member function that returns the meta type of the -contained value, if any. The member functions `can_cast` and `can_convert` are -used to know if the underlying object has a given type as a base or if it can be -converted implicitly to it. Similarly, `cast` and `convert` do what they promise -and return the expected value. +contained value, if any. The member functions `try_cast`, `cast` and `convert` +are used to know if the underlying object has a given type as a base or if it +can be converted implicitly to it. ## Enjoy the runtime diff --git a/src/entt/meta/factory.hpp b/src/entt/meta/factory.hpp index bf863d9b2..6726b1023 100644 --- a/src/entt/meta/factory.hpp +++ b/src/entt/meta/factory.hpp @@ -2,8 +2,11 @@ #define ENTT_META_FACTORY_HPP +#include +#include +#include #include -#include +#include #include #include "../config/config.h" #include "meta.hpp" @@ -12,16 +15,220 @@ namespace entt { -template -class meta_factory; +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ -template -meta_factory reflect(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT; +namespace internal { -template -bool unregister() ENTT_NOEXCEPT; +template +struct meta_function_helper; + + +template +struct meta_function_helper { + using return_type = Ret; + using args_type = std::tuple; + + template + using arg_type = std::decay_t>; + + static constexpr auto size = sizeof...(Args); + + static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { + return std::array{{meta_info::resolve()...}}[index]; + } +}; + + +template +struct meta_function_helper, std::bool_constant>: meta_function_helper { + using class_type = Class; + static constexpr auto is_const = Const; + static constexpr auto is_static = Static; +}; + + +template +constexpr meta_function_helper, std::bool_constant> +to_meta_function_helper(Ret(Class:: *)(Args...)); + + +template +constexpr meta_function_helper, std::bool_constant> +to_meta_function_helper(Ret(Class:: *)(Args...) const); + + +template +constexpr meta_function_helper, std::bool_constant> +to_meta_function_helper(Ret(*)(Args...)); + + +template +struct meta_function_helper>: decltype(to_meta_function_helper(Func)) {}; + + +template +meta_any construct(meta_any * const args, std::index_sequence) { + [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast>>()...); + meta_any any{}; + + if(((std::get(direct) || (args+Indexes)->convert>>()) && ...)) { + any = Type{(std::get(direct) ? *std::get(direct) : (args+Indexes)->cast>>())...}; + } + + return any; +} + + +template +bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) { + bool accepted = false; + + if constexpr(!Const) { + if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { + using helper_type = meta_function_helper>; + using data_type = std::decay_t, typename helper_type::args_type>>; + static_assert(std::is_invocable_v); + auto *direct = value.try_cast(); + auto *clazz = handle.data(); + + if(clazz && (direct || value.convert())) { + std::invoke(Data, *clazz, direct ? *direct : value.cast()); + accepted = true; + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_cv_t().*Data)>>; + static_assert(std::is_invocable_v); + auto *clazz = handle.data(); + + if constexpr(std::is_array_v) { + using underlying_type = std::remove_extent_t; + auto *direct = value.try_cast(); + auto *idx = index.try_cast(); + + if(clazz && idx && (direct || value.convert())) { + std::invoke(Data, clazz)[*idx] = direct ? *direct : value.cast(); + accepted = true; + } + } else { + auto *direct = value.try_cast(); + + if(clazz && (direct || value.convert())) { + std::invoke(Data, clazz) = (direct ? *direct : value.cast()); + accepted = true; + } + } + } else { + static_assert(std::is_pointer_v); + using data_type = std::remove_cv_t>; + + if constexpr(std::is_array_v) { + using underlying_type = std::remove_extent_t; + auto *direct = value.try_cast(); + auto *idx = index.try_cast(); + + if(idx && (direct || value.convert())) { + (*Data)[*idx] = (direct ? *direct : value.cast()); + accepted = true; + } + } else { + auto *direct = value.try_cast(); + + if(direct || value.convert()) { + *Data = (direct ? *direct : value.cast()); + accepted = true; + } + } + } + } + + return accepted; +} + + +template +meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index) { + if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { + static_assert(std::is_invocable_v); + auto *clazz = handle.data(); + return clazz ? std::invoke(Data, *clazz) : meta_any{}; + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_cv_t().*Data)>>; + static_assert(std::is_invocable_v); + auto *clazz = handle.data(); + + if constexpr(std::is_array_v) { + auto *idx = index.try_cast(); + return (clazz && idx) ? std::invoke(Data, clazz)[*idx] : meta_any{}; + } else { + return clazz ? std::invoke(Data, clazz) : meta_any{}; + } + } else { + static_assert(std::is_pointer_v); + + if constexpr(std::is_array_v>) { + auto *idx = index.try_cast(); + return idx ? (*Data)[*idx] : meta_any{}; + } else { + return *Data; + } + } +} + + +template +std::enable_if_t>, meta_any> +invoke(meta_handle, meta_any *args, std::index_sequence) { + using helper_type = meta_function_helper>; + [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast>()...); + meta_any any{}; + + if(((std::get(direct) || (args+Indexes)->convert>()) && ...)) { + if constexpr(std::is_void_v) { + std::invoke(Func, (std::get(direct) ? *std::get(direct) : (args+Indexes)->cast>())...); + any.emplace(); + } else { + any = std::invoke(Func, (std::get(direct) ? *std::get(direct) : (args+Indexes)->cast>())...); + } + } + + return any; +} + + +template +std::enable_if_t, meta_any> +invoke(meta_handle handle, meta_any *args, std::index_sequence) { + using helper_type = meta_function_helper>; + static_assert(std::is_base_of_v); + [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast>()...); + auto *clazz = handle.data(); + meta_any any{}; + + if(clazz && ((std::get(direct) || (args+Indexes)->convert>()) && ...)) { + if constexpr(std::is_void_v) { + std::invoke(Member, clazz, (std::get(direct) ? *std::get(direct) : (args+Indexes)->cast>())...); + any.emplace(); + } else { + any = std::invoke(Member, clazz, (std::get(direct) ? *std::get(direct) : (args+Indexes)->cast>())...); + } + } + + return any; +} + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ /** @@ -75,44 +282,6 @@ class meta_factory { return &node; } - template - meta_factory type(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - static internal::meta_type_node node{ - {}, - nullptr, - nullptr, - std::is_void_v, - std::is_integral_v, - std::is_floating_point_v, - std::is_array_v, - std::is_enum_v, - std::is_union_v, - std::is_class_v, - std::is_pointer_v, - std::is_function_v, - std::is_member_object_pointer_v, - std::is_member_function_pointer_v, - std::extent_v, - []() ENTT_NOEXCEPT -> meta_type { - return internal::meta_info>::resolve(); - }, - &internal::destroy, - []() ENTT_NOEXCEPT -> meta_type { - return &node; - } - }; - - node.identifier = identifier; - node.next = internal::meta_info<>::type; - node.prop = properties(std::forward(property)...); - ENTT_ASSERT(!duplicate(identifier, node.next)); - ENTT_ASSERT(!internal::meta_info::type); - internal::meta_info::type = &node; - internal::meta_info<>::type = &node; - - return *this; - } - void unregister_prop(internal::meta_prop_node **prop) { while(*prop) { auto *node = *prop; @@ -150,46 +319,29 @@ class meta_factory { } } - bool unregister() ENTT_NOEXCEPT { - const auto registered = internal::meta_info::type; - - if(registered) { - if(auto *curr = internal::meta_info<>::type; curr == internal::meta_info::type) { - internal::meta_info<>::type = internal::meta_info::type->next; - } else { - while(curr && curr->next != internal::meta_info::type) { - curr = curr->next; - } - - if(curr) { - curr->next = internal::meta_info::type->next; - } - } - - unregister_prop(&internal::meta_info::type->prop); - unregister_all<&internal::meta_type_node::base>(0); - unregister_all<&internal::meta_type_node::conv>(0); - unregister_all<&internal::meta_type_node::ctor>(0); - unregister_all<&internal::meta_type_node::data>(0); - unregister_all<&internal::meta_type_node::func>(0); - unregister_dtor(); - - internal::meta_info::type->identifier = {}; - internal::meta_info::type->next = nullptr; - internal::meta_info::type = nullptr; - } - - return registered; - } - meta_factory() ENTT_NOEXCEPT = default; public: - template - friend meta_factory reflect(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT; + /** + * @brief Extends a meta type by assigning it an identifier and properties. + * @tparam Property Types of properties to assign to the meta type. + * @param identifier Unique identifier. + * @param property Properties to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + meta_factory type(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { + ENTT_ASSERT(!internal::meta_info::type); + auto *node = internal::meta_info::resolve(); + node->identifier = identifier; + node->next = internal::meta_info<>::type; + node->prop = properties(std::forward(property)...); + ENTT_ASSERT(!duplicate(identifier, node->next)); + internal::meta_info::type = node; + internal::meta_info<>::type = node; - template - friend bool unregister() ENTT_NOEXCEPT; + return *this; + } /** * @brief Assigns a meta base to a meta type. @@ -326,7 +478,7 @@ public: helper_type::size, &helper_type::arg, [](meta_any * const any) { - return internal::invoke(nullptr, any, std::make_index_sequence{}); + return internal::invoke({}, any, std::make_index_sequence{}); }, []() ENTT_NOEXCEPT -> meta_ctor { return &node; @@ -409,7 +561,7 @@ public: type, [](meta_handle handle) { return handle.type() == internal::meta_info::resolve()->meta() - ? ((*Func)(static_cast(handle.data())), true) + ? ((*Func)(handle.data()), true) : false; }, []() ENTT_NOEXCEPT -> meta_dtor { @@ -622,11 +774,54 @@ public: return *this; } + + /** + * @brief Unregisters a meta type and all its parts. + * + * This function unregisters a meta type and all its data members, member + * functions and properties, as well as its constructors, destructors and + * conversion functions if any.
+ * Base classes aren't unregistered but the link between the two types is + * removed. + * + * @return True if the meta type exists, false otherwise. + */ + bool unregister() ENTT_NOEXCEPT { + const auto registered = internal::meta_info::type; + + if(registered) { + if(auto *curr = internal::meta_info<>::type; curr == internal::meta_info::type) { + internal::meta_info<>::type = internal::meta_info::type->next; + } else { + while(curr && curr->next != internal::meta_info::type) { + curr = curr->next; + } + + if(curr) { + curr->next = internal::meta_info::type->next; + } + } + + unregister_prop(&internal::meta_info::type->prop); + unregister_all<&internal::meta_type_node::base>(0); + unregister_all<&internal::meta_type_node::conv>(0); + unregister_all<&internal::meta_type_node::ctor>(0); + unregister_all<&internal::meta_type_node::data>(0); + unregister_all<&internal::meta_type_node::func>(0); + unregister_dtor(); + + internal::meta_info::type->identifier = {}; + internal::meta_info::type->next = nullptr; + internal::meta_info::type = nullptr; + } + + return registered; + } }; /** - * @brief Basic function to use for reflection. + * @brief Utility function to use for reflection. * * This is the point from which everything starts.
* By invoking this function with a type that is not yet reflected, a meta type @@ -646,7 +841,7 @@ inline meta_factory reflect(const ENTT_ID_TYPE identifier, Property &&... /** - * @brief Basic function to use for reflection. + * @brief Utility function to use for reflection. * * This is the point from which everything starts.
* By invoking this function with a type that is not yet reflected, a meta type @@ -663,7 +858,7 @@ inline meta_factory reflect() ENTT_NOEXCEPT { /** - * @brief Basic function to unregister a type. + * @brief Utility function to unregister a type. * * This function unregisters a type and all its data members, member functions * and properties, as well as its constructors, destructors and conversion @@ -676,7 +871,7 @@ inline meta_factory reflect() ENTT_NOEXCEPT { */ template inline bool unregister() ENTT_NOEXCEPT { - return meta_factory().unregister(); + return meta_factory{}.unregister(); } diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index eb4fc6829..54524ced7 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -2,13 +2,11 @@ #define ENTT_META_META_HPP -#include #include #include #include #include #include -#include #include #include "../config/config.h" @@ -17,7 +15,7 @@ namespace entt { class meta_any; -class meta_handle; +struct meta_handle; class meta_prop; class meta_base; class meta_conv; @@ -300,74 +298,41 @@ inline auto ctor(std::index_sequence, const meta_type_node *node) EN */ class meta_any { /*! @brief A meta handle is allowed to _inherit_ from a meta any. */ - friend class meta_handle; + friend struct meta_handle; using storage_type = std::aligned_storage_t; using compare_fn_type = bool(const void *, const void *); using copy_fn_type = void *(storage_type &, const void *); using destroy_fn_type = void(storage_type &); - using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT; + using steal_fn_type = void *(storage_type &, storage_type &, destroy_fn_type *) ENTT_NOEXCEPT; template> struct type_traits { - using chunk_type = std::aligned_storage_t; - template static void * instance(storage_type &storage, Args &&... args) { - auto chunk = std::make_unique(); - auto *instance = new (chunk.get()) Type{std::forward(args)...}; - new (&storage) chunk_type *{chunk.get()}; - chunk.release(); - return instance; + auto instance = std::make_unique(std::forward(args)...); + new (&storage) Type *{instance.get()}; + return instance.release(); } static void destroy(storage_type &storage) { auto *node = internal::meta_info::resolve(); - auto *chunk = *reinterpret_cast(&storage); - auto *instance = reinterpret_cast(chunk); + auto *instance = *reinterpret_cast(&storage); node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance); - delete chunk; + delete instance; } - static void * copy(storage_type &storage, const void *instance) { - auto chunk = std::make_unique(); - new (&storage) chunk_type *{chunk.get()}; - auto *other = new (chunk.get()) Type{*static_cast(instance)}; - chunk.release(); - return other; + static void * copy(storage_type &storage, const void *other) { + auto instance = std::make_unique(*static_cast(other)); + new (&storage) Type *{instance.get()}; + return instance.release(); } - static void * steal(storage_type &to, storage_type &from, destroy_fn_type *, void *instance) ENTT_NOEXCEPT { - auto *chunk = *reinterpret_cast(&from); - new (&to) chunk_type *{chunk}; - chunk->~chunk_type(); + static void * steal(storage_type &to, storage_type &from, destroy_fn_type *) ENTT_NOEXCEPT { + auto *instance = *reinterpret_cast(&from); + new (&to) Type *{instance}; return instance; } - - static bool compare(const void *lhs, const void *rhs) { - return meta_any::compare(0, *static_cast(lhs), *static_cast(rhs)); - } - }; - - template - struct type_traits>> { - static void * instance(storage_type &) { - return nullptr; - } - - static void destroy(storage_type &) {} - - static void * copy(storage_type &, const void *) { - return nullptr; - } - - static void * steal(storage_type &, storage_type &, destroy_fn_type *, void *) ENTT_NOEXCEPT { - return nullptr; - } - - static bool compare(const void *, const void *) { - return true; - } }; template @@ -381,25 +346,23 @@ class meta_any { auto *node = internal::meta_info::resolve(); auto *instance = reinterpret_cast(&storage); node->dtor ? node->dtor->invoke(*instance) : node->destroy(*instance); + instance->~Type(); } static void * copy(storage_type &storage, const void *instance) { return new (&storage) Type{*static_cast(instance)}; } - static void * steal(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn, void *) ENTT_NOEXCEPT { + static void * steal(storage_type &to, storage_type &from, destroy_fn_type *destroy_fn) ENTT_NOEXCEPT { void *instance = new (&to) Type{std::move(*reinterpret_cast(&from))}; destroy_fn(from); return instance; } - - static bool compare(const void *lhs, const void *rhs) { - return meta_any::compare(0, *static_cast(lhs), *static_cast(rhs)); - } }; template - static auto compare(int, const Type &lhs, const Type &rhs) -> decltype(lhs == rhs, bool{}) { + static auto compare(int, const Type &lhs, const Type &rhs) + -> decltype(lhs == rhs, bool{}) { return lhs == rhs; } @@ -422,41 +385,48 @@ public: /** * @brief Constructs a meta any by directly initializing the new object. - * - * This class uses a technique called small buffer optimization (SBO) to - * completely eliminate the need to allocate memory, where possible.
- * From the user's point of view, nothing will change, but the elimination - * of allocations will reduce the jumps in memory and therefore will avoid - * chasing of pointers. This will greatly improve the use of the cache, thus - * increasing the overall performance. - * * @tparam Type Type of object to use to initialize the container. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template - meta_any(std::in_place_type_t, Args &&... args) { - using actual_type = std::remove_cv_t>; - using traits_type = type_traits; - + explicit meta_any(std::in_place_type_t, [[maybe_unused]] Args &&... args) + : meta_any{} + { node = internal::meta_info::resolve(); - instance = traits_type::instance(storage, std::forward(args)...); - destroy_fn = &traits_type::destroy; - copy_fn = &traits_type::copy; - steal_fn = &traits_type::steal; - compare_fn = &traits_type::compare; + + if constexpr(!std::is_void_v) { + using traits_type = type_traits>>; + instance = traits_type::instance(storage, std::forward(args)...); + destroy_fn = &traits_type::destroy; + copy_fn = &traits_type::copy; + steal_fn = &traits_type::steal; + + compare_fn = [](const void *lhs, const void *rhs) { + return compare(0, *static_cast(lhs), *static_cast(rhs)); + }; + } + } + + /** + * @brief Constructs a meta any that holds an unmanaged object. + * @tparam Type Type of object to use to initialize the container. + * @param type An instance of an object to use to initialize the container. + */ + template + explicit meta_any(std::in_place_t, Type &type) + : meta_any{} + { + node = internal::meta_info::resolve(); + instance = &type; + + compare_fn = [](const void *lhs, const void *rhs) { + return compare(0, *static_cast(lhs), *static_cast(rhs)); + }; } /** * @brief Constructs a meta any from a given value. - * - * This class uses a technique called small buffer optimization (SBO) to - * completely eliminate the need to allocate memory, where possible.
- * From the user's point of view, nothing will change, but the elimination - * of allocations will reduce the jumps in memory and therefore will avoid - * chasing of pointers. This will greatly improve the use of the cache, thus - * increasing the overall performance. - * * @tparam Type Type of object to use to initialize the container. * @param type An instance of an object to use to initialize the container. */ @@ -472,14 +442,12 @@ public: meta_any(const meta_any &other) : meta_any{} { - if(other) { - instance = other.copy_fn(storage, other.instance); - node = other.node; - destroy_fn = other.destroy_fn; - compare_fn = other.compare_fn; - copy_fn = other.copy_fn; - steal_fn = other.steal_fn; - } + node = other.node; + instance = other.copy_fn ? other.copy_fn(storage, other.instance) : other.instance; + destroy_fn = other.destroy_fn; + compare_fn = other.compare_fn; + copy_fn = other.copy_fn; + steal_fn = other.steal_fn; } /** @@ -506,11 +474,32 @@ public: /** * @brief Assignment operator. + * @tparam Type Type of object to use to initialize the container. + * @param type An instance of an object to use to initialize the container. + * @return This meta any object. + */ + template>, meta_any>>> + meta_any & operator=(Type &&type) { + return (*this = meta_any{std::forward(type)}); + } + + /** + * @brief Copy assignment operator. * @param other The instance to assign. * @return This meta any object. */ - meta_any & operator=(meta_any other) { - swap(other, *this); + meta_any & operator=(const meta_any &other) { + return (*this = meta_any{other}); + } + + /** + * @brief Move assignment operator. + * @param other The instance to assign. + * @return This meta any object. + */ + meta_any & operator=(meta_any &&other) ENTT_NOEXCEPT { + meta_any any{std::move(other)}; + swap(any, *this); return *this; } @@ -534,14 +523,19 @@ public: } /** - * @brief Checks if it's possible to cast an instance to a given type. + * @brief Tries to cast an instance to a given type. * @tparam Type Type to which to cast the instance. - * @return True if the cast is viable, false otherwise. + * @return A (possibly null) pointer to the contained instance. */ template - bool can_cast() const ENTT_NOEXCEPT { - const auto *type = internal::meta_info::resolve(); - return internal::can_cast_or_convert<&internal::meta_type_node::base>(node, type); + const Type * try_cast() const ENTT_NOEXCEPT { + return internal::try_cast(node, instance); + } + + /*! @copydoc try_cast */ + template + Type * try_cast() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).try_cast()); } /** @@ -560,8 +554,8 @@ public: */ template const Type & cast() const ENTT_NOEXCEPT { - ENTT_ASSERT(can_cast()); - return *internal::try_cast(node, instance); + ENTT_ASSERT(try_cast()); + return *try_cast(); } /*! @copydoc cast */ @@ -570,17 +564,6 @@ public: return const_cast(std::as_const(*this).cast()); } - /** - * @brief Checks if it's possible to convert an instance to a given type. - * @tparam Type Type to which to convert the instance. - * @return True if the conversion is viable, false otherwise. - */ - template - bool can_convert() const ENTT_NOEXCEPT { - const auto *type = internal::meta_info::resolve(); - return internal::can_cast_or_convert<&internal::meta_type_node::conv>(node, type); - } - /** * @brief Tries to convert an instance to a given type and returns it. * @tparam Type Type to which to convert the instance. @@ -642,7 +625,7 @@ public: * @return False if the container is empty, true otherwise. */ explicit operator bool() const ENTT_NOEXCEPT { - return destroy_fn; + return node; } /** @@ -652,7 +635,7 @@ public: * otherwise. */ bool operator==(const meta_any &other) const ENTT_NOEXCEPT { - return (!compare_fn && !other.compare_fn) || (compare_fn && other.compare_fn && node == other.node && compare_fn(instance, other.instance)); + return node == other.node && ((!compare_fn && !other.compare_fn) || compare_fn(instance, other.instance)); } /** @@ -661,17 +644,19 @@ public: * @param rhs A valid meta any object. */ friend void swap(meta_any &lhs, meta_any &rhs) ENTT_NOEXCEPT { - if(lhs && rhs) { + if(lhs.steal_fn && rhs.steal_fn) { storage_type buffer; - void *instance = lhs.steal_fn(buffer, lhs.storage, lhs.destroy_fn, lhs.instance); - lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn, rhs.instance); - rhs.instance = lhs.steal_fn(rhs.storage, buffer, lhs.destroy_fn, instance); - } else if(lhs) { - rhs.instance = lhs.steal_fn(rhs.storage, lhs.storage, lhs.destroy_fn, lhs.instance); - lhs.instance = nullptr; - } else if(rhs) { - lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn, rhs.instance); - rhs.instance = nullptr; + lhs.steal_fn(buffer, lhs.storage, lhs.destroy_fn); + lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn); + rhs.instance = lhs.steal_fn(rhs.storage, buffer, lhs.destroy_fn); + } else if(lhs.steal_fn) { + lhs.instance = rhs.instance; + rhs.instance = lhs.steal_fn(rhs.storage, lhs.storage, lhs.destroy_fn); + } else if(rhs.steal_fn) { + rhs.instance = lhs.instance; + lhs.instance = rhs.steal_fn(lhs.storage, rhs.storage, rhs.destroy_fn); + } else { + std::swap(lhs.instance, rhs.instance); } std::swap(lhs.node, rhs.node); @@ -702,33 +687,31 @@ private: * responsible for ensuring that the target object remains alive for the entire * interval of use of the handle. */ -class meta_handle { - meta_handle(int, meta_any &any) ENTT_NOEXCEPT - : node{any.node}, - instance{any.instance} - {} - - template - meta_handle(char, Type &&obj) ENTT_NOEXCEPT - : node{internal::meta_info::resolve()}, - instance{&obj} - {} - -public: +struct meta_handle { /*! @brief Default constructor. */ meta_handle() ENTT_NOEXCEPT : node{nullptr}, instance{nullptr} {} + /** + * @brief Constructs a meta handle from a meta any object. + * @param any A reference to an object to use to initialize the handle. + */ + meta_handle(meta_any &any) ENTT_NOEXCEPT + : node{any.node}, + instance{any.instance} + {} + /** * @brief Constructs a meta handle from a given instance. * @tparam Type Type of object to use to initialize the handle. * @param obj A reference to an object to use to initialize the handle. */ template>, meta_handle>>> - meta_handle(Type &&obj) ENTT_NOEXCEPT - : meta_handle{0, std::forward(obj)} + meta_handle(Type &obj) ENTT_NOEXCEPT + : node{internal::meta_info::resolve()}, + instance{&obj} {} /** @@ -737,31 +720,6 @@ public: */ inline meta_type type() const ENTT_NOEXCEPT; - /** - * @brief Tries to cast an instance to a given type. - * - * The type of the instance must be such that the conversion is possible. - * - * @warning - * Attempting to perform a conversion that isn't viable results in undefined - * behavior.
- * An assertion will abort the execution at runtime in debug mode in case - * the conversion is not feasible. - * - * @tparam Type Type to which to cast the instance. - * @return A pointer to the contained instance. - */ - template - const Type * try_cast() const ENTT_NOEXCEPT { - return internal::try_cast(node, instance); - } - - /*! @copydoc try_cast */ - template - Type * try_cast() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).try_cast()); - } - /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. @@ -775,6 +733,22 @@ public: return const_cast(std::as_const(*this).data()); } + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the underlying object. + */ + template + const Type * data() const ENTT_NOEXCEPT { + return internal::try_cast(node, instance); + } + + /*! @copydoc data */ + template + Type * data() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).data()); + } + /** * @brief Returns false if a handle is empty, true otherwise. * @return False if the handle is empty, true otherwise. @@ -1590,9 +1564,6 @@ inline bool operator!=(const meta_func &lhs, const meta_func &rhs) ENTT_NOEXCEPT * able to work through it on real objects. */ class meta_type { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - /*! @brief A meta node is allowed to create meta objects. */ template friend struct internal::meta_node; @@ -2093,215 +2064,6 @@ inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { namespace internal { -template -struct meta_function_helper; - - -template -struct meta_function_helper { - using return_type = Ret; - using args_type = std::tuple; - - template - using arg_type = std::decay_t>; - - static constexpr auto size = sizeof...(Args); - - static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { - return std::array{{meta_info::resolve()...}}[index]; - } -}; - - -template -struct meta_function_helper, std::bool_constant>: meta_function_helper { - using class_type = Class; - static constexpr auto is_const = Const; - static constexpr auto is_static = Static; -}; - - -template -constexpr meta_function_helper, std::bool_constant> -to_meta_function_helper(Ret(Class:: *)(Args...)); - - -template -constexpr meta_function_helper, std::bool_constant> -to_meta_function_helper(Ret(Class:: *)(Args...) const); - - -template -constexpr meta_function_helper, std::bool_constant> -to_meta_function_helper(Ret(*)(Args...)); - - -template -struct meta_function_helper>: decltype(to_meta_function_helper(Func)) {}; - - -template -bool destroy([[maybe_unused]] meta_handle handle) { - bool accepted = false; - - if constexpr(std::is_object_v && !std::is_array_v) { - accepted = (handle.type() == meta_info::resolve()->meta()); - - if(accepted) { - static_cast(handle.data())->~Type(); - } - } - - return accepted; -} - - -template -meta_any construct(meta_any * const args, std::index_sequence) { - [[maybe_unused]] std::array can_cast{{(args+Indexes)->can_cast>>()...}}; - [[maybe_unused]] std::array can_convert{{(std::get(can_cast) ? false : (args+Indexes)->can_convert>>())...}}; - meta_any any{}; - - if(((std::get(can_cast) || std::get(can_convert)) && ...)) { - ((std::get(can_convert) ? void((args+Indexes)->convert>>()) : void()), ...); - any = Type{(args+Indexes)->cast>>()...}; - } - - return any; -} - - -template -bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) { - bool accepted = false; - - if constexpr(!Const) { - if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { - using helper_type = meta_function_helper>; - using data_type = std::decay_t, typename helper_type::args_type>>; - static_assert(std::is_invocable_v); - accepted = value.can_cast() || value.convert(); - auto *clazz = handle.try_cast(); - - if(accepted && clazz) { - std::invoke(Data, *clazz, value.cast()); - } - } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_cv_t().*Data)>>; - static_assert(std::is_invocable_v); - auto *clazz = handle.try_cast(); - - if constexpr(std::is_array_v) { - using underlying_type = std::remove_extent_t; - accepted = index.can_cast() && (value.can_cast() || value.convert()); - - if(accepted && clazz) { - std::invoke(Data, clazz)[index.cast()] = value.cast(); - } - } else { - accepted = value.can_cast() || value.convert(); - - if(accepted && clazz) { - std::invoke(Data, clazz) = value.cast(); - } - } - } else { - static_assert(std::is_pointer_v); - using data_type = std::remove_cv_t>; - - if constexpr(std::is_array_v) { - using underlying_type = std::remove_extent_t; - accepted = index.can_cast() && (value.can_cast() || value.convert()); - - if(accepted) { - (*Data)[index.cast()] = value.cast(); - } - } else { - accepted = value.can_cast() || value.convert(); - - if(accepted) { - *Data = value.cast(); - } - } - } - } - - return accepted; -} - - -template -meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index) { - if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { - static_assert(std::is_invocable_v); - auto *clazz = handle.try_cast(); - return clazz ? std::invoke(Data, *clazz) : meta_any{}; - } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_cv_t().*Data)>>; - static_assert(std::is_invocable_v); - auto *clazz = handle.try_cast(); - - if constexpr(std::is_array_v) { - return (clazz && index.can_cast()) ? std::invoke(Data, clazz)[index.cast()] : meta_any{}; - } else { - return clazz ? std::invoke(Data, clazz) : meta_any{}; - } - } else { - static_assert(std::is_pointer_v); - - if constexpr(std::is_array_v>) { - return index.can_cast() ? (*Data)[index.cast()] : meta_any{}; - } else { - return *Data; - } - } -} - - -template -std::enable_if_t>, meta_any> -invoke(const meta_handle &, meta_any *args, std::index_sequence) { - using helper_type = meta_function_helper>; - meta_any any{}; - - if((((args+Indexes)->can_cast>() - || (args+Indexes)->convert>()) && ...)) - { - if constexpr(std::is_void_v) { - std::invoke(Func, (args+Indexes)->cast>()...); - any.emplace(); - } else { - any = meta_any{std::invoke(Func, (args+Indexes)->cast>()...)}; - } - } - - return any; -} - - -template -std::enable_if_t, meta_any> -invoke(meta_handle &handle, meta_any *args, std::index_sequence) { - using helper_type = meta_function_helper>; - static_assert(std::is_base_of_v); - auto *clazz = handle.try_cast(); - meta_any any{}; - - if(clazz && (((args+Indexes)->can_cast>() - || (args+Indexes)->convert>()) && ...)) - { - if constexpr(std::is_void_v) { - std::invoke(Member, clazz, (args+Indexes)->cast>()...); - any.emplace(); - } else { - any = meta_any{std::invoke(Member, clazz, (args+Indexes)->cast>()...)}; - } - } - - return any; -} - - template inline meta_type_node * meta_node::resolve() ENTT_NOEXCEPT { if(!type) { @@ -2324,7 +2086,18 @@ inline meta_type_node * meta_node::resolve() ENTT_NOEXCEPT { []() ENTT_NOEXCEPT -> meta_type { return internal::meta_info>::resolve(); }, - &destroy, + []([[maybe_unused]] meta_handle handle) { + bool accepted = false; + + if constexpr(std::is_object_v && !std::is_array_v) { + if((handle.type() == meta_info::resolve()->meta())) { + handle.data()->~Type(); + accepted = true; + } + } + + return accepted; + }, []() ENTT_NOEXCEPT -> meta_type { return &node; } diff --git a/test/entt/meta/meta.cpp b/test/entt/meta/meta.cpp index 0e285aa70..de919a079 100644 --- a/test/entt/meta/meta.cpp +++ b/test/entt/meta/meta.cpp @@ -231,14 +231,14 @@ TEST_F(Meta, MetaAnySBO) { entt::meta_any any{'c'}; ASSERT_TRUE(any); - ASSERT_FALSE(any.can_cast()); - ASSERT_TRUE(any.can_cast()); + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast(), 'c'); ASSERT_EQ(std::as_const(any).cast(), 'c'); ASSERT_NE(any.data(), nullptr); ASSERT_NE(std::as_const(any).data(), nullptr); ASSERT_EQ(any, entt::meta_any{'c'}); - ASSERT_NE(any, entt::meta_any{'h'}); + ASSERT_NE(entt::meta_any{'h'}, any); } TEST_F(Meta, MetaAnyNoSBO) { @@ -247,14 +247,14 @@ TEST_F(Meta, MetaAnyNoSBO) { entt::meta_any any{instance}; ASSERT_TRUE(any); - ASSERT_FALSE(any.can_cast()); - ASSERT_TRUE(any.can_cast()); + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast(), instance); ASSERT_EQ(std::as_const(any).cast(), instance); ASSERT_NE(any.data(), nullptr); ASSERT_NE(std::as_const(any).data(), nullptr); ASSERT_EQ(any, entt::meta_any{instance}); - ASSERT_NE(any, fat_type{}); + ASSERT_NE(fat_type{}, any); } TEST_F(Meta, MetaAnyEmpty) { @@ -262,27 +262,45 @@ TEST_F(Meta, MetaAnyEmpty) { ASSERT_FALSE(any); ASSERT_FALSE(any.type()); - ASSERT_FALSE(any.can_cast()); - ASSERT_FALSE(any.can_cast()); + ASSERT_FALSE(any.try_cast()); + ASSERT_FALSE(any.try_cast()); ASSERT_EQ(any.data(), nullptr); ASSERT_EQ(std::as_const(any).data(), nullptr); ASSERT_EQ(any, entt::meta_any{}); - ASSERT_NE(any, entt::meta_any{'c'}); + ASSERT_NE(entt::meta_any{'c'}, any); } -TEST_F(Meta, MetaAnySBOInPlaceConstruction) { +TEST_F(Meta, MetaAnySBOInPlaceTypeConstruction) { entt::meta_any any{std::in_place_type, 42}; ASSERT_TRUE(any); - ASSERT_FALSE(any.can_cast()); - ASSERT_TRUE(any.can_cast()); + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast(), 42); ASSERT_EQ(std::as_const(any).cast(), 42); ASSERT_NE(any.data(), nullptr); ASSERT_NE(std::as_const(any).data(), nullptr); ASSERT_EQ(any, (entt::meta_any{std::in_place_type, 42})); ASSERT_EQ(any, entt::meta_any{42}); - ASSERT_NE(any, entt::meta_any{3}); + ASSERT_NE(entt::meta_any{3}, any); +} + +TEST_F(Meta, MetaAnySBOInPlaceConstruction) { + int value = 3; + int other = 42; + entt::meta_any any{std::in_place, value}; + + ASSERT_TRUE(any); + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); + ASSERT_EQ(any.cast(), 3); + ASSERT_EQ(std::as_const(any).cast(), 3); + ASSERT_NE(any.data(), nullptr); + ASSERT_NE(std::as_const(any).data(), nullptr); + ASSERT_EQ(any, (entt::meta_any{std::in_place, value})); + ASSERT_NE(any, (entt::meta_any{std::in_place, other})); + ASSERT_NE(any, entt::meta_any{42}); + ASSERT_EQ(entt::meta_any{3}, any); } TEST_F(Meta, MetaAnySBOCopyConstruction) { @@ -291,8 +309,8 @@ TEST_F(Meta, MetaAnySBOCopyConstruction) { ASSERT_TRUE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), 42); ASSERT_EQ(std::as_const(other).cast(), 42); ASSERT_EQ(other, entt::meta_any{42}); @@ -307,8 +325,8 @@ TEST_F(Meta, MetaAnySBOCopyAssignment) { ASSERT_TRUE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), 42); ASSERT_EQ(std::as_const(other).cast(), 42); ASSERT_EQ(other, entt::meta_any{42}); @@ -321,8 +339,8 @@ TEST_F(Meta, MetaAnySBOMoveConstruction) { ASSERT_FALSE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), 42); ASSERT_EQ(std::as_const(other).cast(), 42); ASSERT_EQ(other, entt::meta_any{42}); @@ -337,29 +355,58 @@ TEST_F(Meta, MetaAnySBOMoveAssignment) { ASSERT_FALSE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), 42); ASSERT_EQ(std::as_const(other).cast(), 42); ASSERT_EQ(other, entt::meta_any{42}); ASSERT_NE(other, entt::meta_any{0}); } -TEST_F(Meta, MetaAnyNoSBOInPlaceConstruction) { +TEST_F(Meta, MetaAnySBODirectAssignment) { + entt::meta_any any{}; + any = 42; + + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); + ASSERT_EQ(any.cast(), 42); + ASSERT_EQ(std::as_const(any).cast(), 42); + ASSERT_EQ(any, entt::meta_any{42}); + ASSERT_NE(entt::meta_any{0}, any); +} + +TEST_F(Meta, MetaAnyNoSBOInPlaceTypeConstruction) { int value = 42; fat_type instance{&value}; entt::meta_any any{std::in_place_type, instance}; ASSERT_TRUE(any); - ASSERT_FALSE(any.can_cast()); - ASSERT_TRUE(any.can_cast()); + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast(), instance); ASSERT_EQ(std::as_const(any).cast(), instance); ASSERT_NE(any.data(), nullptr); ASSERT_NE(std::as_const(any).data(), nullptr); ASSERT_EQ(any, (entt::meta_any{std::in_place_type, instance})); ASSERT_EQ(any, entt::meta_any{instance}); - ASSERT_NE(any, entt::meta_any{fat_type{}}); + ASSERT_NE(entt::meta_any{fat_type{}}, any); +} + +TEST_F(Meta, MetaAnyNoSBOInPlaceConstruction) { + int value = 3; + fat_type instance{&value}; + entt::meta_any any{std::in_place, instance}; + + ASSERT_TRUE(any); + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); + ASSERT_EQ(any.cast(), instance); + ASSERT_EQ(std::as_const(any).cast(), instance); + ASSERT_NE(any.data(), nullptr); + ASSERT_NE(std::as_const(any).data(), nullptr); + ASSERT_EQ(any, (entt::meta_any{std::in_place, instance})); + ASSERT_EQ(any, entt::meta_any{instance}); + ASSERT_NE(entt::meta_any{fat_type{}}, any); } TEST_F(Meta, MetaAnyNoSBOCopyConstruction) { @@ -370,8 +417,8 @@ TEST_F(Meta, MetaAnyNoSBOCopyConstruction) { ASSERT_TRUE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), instance); ASSERT_EQ(std::as_const(other).cast(), instance); ASSERT_EQ(other, entt::meta_any{instance}); @@ -388,8 +435,8 @@ TEST_F(Meta, MetaAnyNoSBOCopyAssignment) { ASSERT_TRUE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), instance); ASSERT_EQ(std::as_const(other).cast(), instance); ASSERT_EQ(other, entt::meta_any{instance}); @@ -404,8 +451,8 @@ TEST_F(Meta, MetaAnyNoSBOMoveConstruction) { ASSERT_FALSE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), instance); ASSERT_EQ(std::as_const(other).cast(), instance); ASSERT_EQ(other, entt::meta_any{instance}); @@ -422,23 +469,37 @@ TEST_F(Meta, MetaAnyNoSBOMoveAssignment) { ASSERT_FALSE(any); ASSERT_TRUE(other); - ASSERT_FALSE(other.can_cast()); - ASSERT_TRUE(other.can_cast()); + ASSERT_FALSE(other.try_cast()); + ASSERT_TRUE(other.try_cast()); ASSERT_EQ(other.cast(), instance); ASSERT_EQ(std::as_const(other).cast(), instance); ASSERT_EQ(other, entt::meta_any{instance}); ASSERT_NE(other, fat_type{}); } -TEST_F(Meta, MetaAnyVoidInPlaceConstruction) { +TEST_F(Meta, MetaAnyNoSBODirectAssignment) { + int value = 42; + entt::meta_any any{}; + any = fat_type{&value}; + + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); + ASSERT_EQ(any.cast(), fat_type{&value}); + ASSERT_EQ(std::as_const(any).cast(), fat_type{&value}); + ASSERT_EQ(any, entt::meta_any{fat_type{&value}}); + ASSERT_NE(fat_type{}, any); +} + +TEST_F(Meta, MetaAnyVoidInPlaceTypeConstruction) { entt::meta_any any{std::in_place_type}; ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); - ASSERT_FALSE(any.can_cast()); + ASSERT_FALSE(any.try_cast()); ASSERT_EQ(any.data(), nullptr); ASSERT_EQ(std::as_const(any).data(), nullptr); + ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(any, entt::meta_any{std::in_place_type}); + ASSERT_NE(entt::meta_any{3}, any); } TEST_F(Meta, MetaAnyVoidCopyConstruction) { @@ -447,7 +508,7 @@ TEST_F(Meta, MetaAnyVoidCopyConstruction) { ASSERT_TRUE(any); ASSERT_TRUE(other); - ASSERT_TRUE(other.can_cast()); + ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(other, entt::meta_any{std::in_place_type}); } @@ -459,7 +520,7 @@ TEST_F(Meta, MetaAnyVoidCopyAssignment) { ASSERT_TRUE(any); ASSERT_TRUE(other); - ASSERT_TRUE(other.can_cast()); + ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(other, entt::meta_any{std::in_place_type}); } @@ -469,7 +530,7 @@ TEST_F(Meta, MetaAnyVoidMoveConstruction) { ASSERT_FALSE(any); ASSERT_TRUE(other); - ASSERT_TRUE(other.can_cast()); + ASSERT_EQ(other.type(), entt::resolve()); ASSERT_EQ(other, entt::meta_any{std::in_place_type}); } @@ -481,7 +542,7 @@ TEST_F(Meta, MetaAnyVoidMoveAssignment) { ASSERT_FALSE(any); ASSERT_TRUE(other); - ASSERT_TRUE(other.can_cast()); + ASSERT_EQ(other.type(), entt::resolve()); ASSERT_EQ(other, entt::meta_any{std::in_place_type}); } @@ -539,15 +600,15 @@ TEST_F(Meta, MetaAnyEmplace) { any.emplace(42); ASSERT_TRUE(any); - ASSERT_FALSE(any.can_cast()); - ASSERT_TRUE(any.can_cast()); + ASSERT_FALSE(any.try_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast(), 42); ASSERT_EQ(std::as_const(any).cast(), 42); ASSERT_NE(any.data(), nullptr); ASSERT_NE(std::as_const(any).data(), nullptr); ASSERT_EQ(any, (entt::meta_any{std::in_place_type, 42})); ASSERT_EQ(any, entt::meta_any{42}); - ASSERT_NE(any, entt::meta_any{3}); + ASSERT_NE(entt::meta_any{3}, any); } TEST_F(Meta, MetaAnyEmplaceVoid) { @@ -555,9 +616,9 @@ TEST_F(Meta, MetaAnyEmplaceVoid) { any.emplace(); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); ASSERT_EQ(any.data(), nullptr); ASSERT_EQ(std::as_const(any).data(), nullptr); + ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(any, (entt::meta_any{std::in_place_type})); } @@ -567,9 +628,9 @@ TEST_F(Meta, MetaAnySBOSwap) { std::swap(lhs, rhs); - ASSERT_TRUE(lhs.can_cast()); + ASSERT_TRUE(lhs.try_cast()); ASSERT_EQ(lhs.cast(), 42); - ASSERT_TRUE(rhs.can_cast()); + ASSERT_TRUE(rhs.try_cast()); ASSERT_EQ(rhs.cast(), 'c'); } @@ -601,9 +662,9 @@ TEST_F(Meta, MetaAnySBOWithNoSBOSwap) { std::swap(lhs, rhs); - ASSERT_TRUE(lhs.can_cast()); + ASSERT_TRUE(lhs.try_cast()); ASSERT_EQ(lhs.cast(), 'c'); - ASSERT_TRUE(rhs.can_cast()); + ASSERT_TRUE(rhs.try_cast()); ASSERT_EQ(rhs.cast().foo, &value); ASSERT_EQ(rhs.cast().bar, &value); } @@ -615,13 +676,13 @@ TEST_F(Meta, MetaAnySBOWithEmptySwap) { std::swap(lhs, rhs); ASSERT_FALSE(lhs); - ASSERT_TRUE(rhs.can_cast()); + ASSERT_TRUE(rhs.try_cast()); ASSERT_EQ(rhs.cast(), 'c'); std::swap(lhs, rhs); ASSERT_FALSE(rhs); - ASSERT_TRUE(lhs.can_cast()); + ASSERT_TRUE(lhs.try_cast()); ASSERT_EQ(lhs.cast(), 'c'); } @@ -631,8 +692,8 @@ TEST_F(Meta, MetaAnySBOWithVoidSwap) { std::swap(lhs, rhs); - ASSERT_TRUE(lhs.can_cast()); - ASSERT_TRUE(rhs.can_cast()); + ASSERT_EQ(lhs.type(), entt::resolve()); + ASSERT_TRUE(rhs.try_cast()); ASSERT_EQ(rhs.cast(), 'c'); } @@ -669,7 +730,7 @@ TEST_F(Meta, MetaAnyComparable) { ASSERT_EQ(any, any); ASSERT_EQ(any, entt::meta_any{'c'}); - ASSERT_NE(any, entt::meta_any{'a'}); + ASSERT_NE(entt::meta_any{'a'}, any); ASSERT_NE(any, entt::meta_any{}); ASSERT_TRUE(any == any); @@ -684,7 +745,7 @@ TEST_F(Meta, MetaAnyNotComparable) { ASSERT_EQ(any, any); ASSERT_NE(any, entt::meta_any{not_comparable_type{}}); - ASSERT_NE(any, entt::meta_any{}); + ASSERT_NE(entt::meta_any{}, any); ASSERT_TRUE(any == any); ASSERT_FALSE(any == entt::meta_any{not_comparable_type{}}); @@ -696,7 +757,7 @@ TEST_F(Meta, MetaAnyCompareVoid) { ASSERT_EQ(any, any); ASSERT_EQ(any, entt::meta_any{std::in_place_type}); - ASSERT_NE(any, entt::meta_any{'a'}); + ASSERT_NE(entt::meta_any{'a'}, any); ASSERT_NE(any, entt::meta_any{}); ASSERT_TRUE(any == any); @@ -706,19 +767,28 @@ TEST_F(Meta, MetaAnyCompareVoid) { ASSERT_TRUE(any != entt::meta_any{}); } -TEST_F(Meta, MetaAnyCast) { +TEST_F(Meta, MetaAnyTryCast) { entt::meta_any any{derived_type{}}; - entt::meta_handle handle{any}; ASSERT_TRUE(any); ASSERT_EQ(any.type(), entt::resolve()); - ASSERT_FALSE(any.can_cast()); - ASSERT_TRUE(any.can_cast()); - ASSERT_TRUE(any.can_cast()); - ASSERT_EQ(&any.cast(), handle.try_cast()); - ASSERT_EQ(&any.cast(), handle.try_cast()); - ASSERT_EQ(&std::as_const(any).cast(), handle.try_cast()); - ASSERT_EQ(&std::as_const(any).cast(), handle.try_cast()); + ASSERT_EQ(any.try_cast(), nullptr); + ASSERT_NE(any.try_cast(), nullptr); + ASSERT_EQ(any.try_cast(), any.data()); + ASSERT_EQ(std::as_const(any).try_cast(), any.try_cast()); + ASSERT_EQ(std::as_const(any).try_cast(), any.data()); +} + +TEST_F(Meta, MetaAnyCast) { + entt::meta_any any{derived_type{}}; + + ASSERT_TRUE(any); + ASSERT_EQ(any.type(), entt::resolve()); + ASSERT_EQ(any.try_cast(), nullptr); + ASSERT_NE(any.try_cast(), nullptr); + ASSERT_EQ(any.try_cast(), any.data()); + ASSERT_EQ(std::as_const(any).try_cast(), any.try_cast()); + ASSERT_EQ(std::as_const(any).try_cast(), any.data()); } TEST_F(Meta, MetaAnyConvert) { @@ -726,18 +796,11 @@ TEST_F(Meta, MetaAnyConvert) { ASSERT_TRUE(any); ASSERT_EQ(any.type(), entt::resolve()); - ASSERT_FALSE(any.can_convert()); - ASSERT_TRUE(any.can_convert()); - ASSERT_TRUE(any.can_convert()); - ASSERT_TRUE(any.convert()); ASSERT_FALSE(any.convert()); - ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(any.cast(), 42.); - ASSERT_TRUE(any.convert()); - ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(any.cast(), 42); } @@ -747,13 +810,8 @@ TEST_F(Meta, MetaAnyConstConvert) { ASSERT_TRUE(any); ASSERT_EQ(any.type(), entt::resolve()); - ASSERT_FALSE(any.can_convert()); - ASSERT_TRUE(any.can_convert()); - ASSERT_TRUE(any.can_convert()); - ASSERT_TRUE(any.convert()); ASSERT_FALSE(any.convert()); - ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(any.cast(), 42.); @@ -771,9 +829,9 @@ TEST_F(Meta, MetaHandleFromObject) { ASSERT_TRUE(handle); ASSERT_EQ(handle.type(), entt::resolve()); - ASSERT_EQ(handle.try_cast(), nullptr); - ASSERT_EQ(handle.try_cast(), &empty); - ASSERT_EQ(std::as_const(handle).try_cast(), &empty); + ASSERT_EQ(handle.data(), nullptr); + ASSERT_EQ(handle.data(), &empty); + ASSERT_EQ(std::as_const(handle).data(), &empty); ASSERT_EQ(handle.data(), &empty); ASSERT_EQ(std::as_const(handle).data(), &empty); } @@ -784,9 +842,9 @@ TEST_F(Meta, MetaHandleFromMetaAny) { ASSERT_TRUE(handle); ASSERT_EQ(handle.type(), entt::resolve()); - ASSERT_EQ(handle.try_cast(), nullptr); - ASSERT_EQ(handle.try_cast(), any.data()); - ASSERT_EQ(std::as_const(handle).try_cast(), any.data()); + ASSERT_EQ(handle.data(), nullptr); + ASSERT_EQ(handle.data(), any.data()); + ASSERT_EQ(std::as_const(handle).data(), any.data()); ASSERT_EQ(handle.data(), any.data()); ASSERT_EQ(std::as_const(handle).data(), any.data()); } @@ -796,24 +854,24 @@ TEST_F(Meta, MetaHandleEmpty) { ASSERT_FALSE(handle); ASSERT_FALSE(handle.type()); - ASSERT_EQ(handle.try_cast(), nullptr); - ASSERT_EQ(handle.try_cast(), nullptr); + ASSERT_EQ(handle.data(), nullptr); + ASSERT_EQ(handle.data(), nullptr); ASSERT_EQ(handle.data(), nullptr); ASSERT_EQ(std::as_const(handle).data(), nullptr); } -TEST_F(Meta, MetaHandleTryCast) { +TEST_F(Meta, MetaHandleData) { derived_type derived{}; base_type *base = &derived; entt::meta_handle handle{derived}; ASSERT_TRUE(handle); ASSERT_EQ(handle.type(), entt::resolve()); - ASSERT_EQ(handle.try_cast(), nullptr); - ASSERT_EQ(handle.try_cast(), base); - ASSERT_EQ(handle.try_cast(), &derived); - ASSERT_EQ(std::as_const(handle).try_cast(), base); - ASSERT_EQ(std::as_const(handle).try_cast(), &derived); + ASSERT_EQ(handle.data(), nullptr); + ASSERT_EQ(handle.data(), base); + ASSERT_EQ(handle.data(), &derived); + ASSERT_EQ(std::as_const(handle).data(), base); + ASSERT_EQ(std::as_const(handle).data(), &derived); ASSERT_EQ(handle.data(), &derived); ASSERT_EQ(std::as_const(handle).data(), &derived); } @@ -903,7 +961,7 @@ TEST_F(Meta, MetaCtor) { ASSERT_FALSE(empty); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); @@ -937,7 +995,7 @@ TEST_F(Meta, MetaCtorFunc) { ASSERT_FALSE(empty); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); @@ -961,7 +1019,7 @@ TEST_F(Meta, MetaCtorMetaAnyArgs) { auto any = ctor.invoke(base_type{}, entt::meta_any{42}, entt::meta_any{'c'}); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); } @@ -976,7 +1034,7 @@ TEST_F(Meta, MetaCtorCastAndConvert) { auto any = ctor.invoke(entt::meta_any{derived_type{}}, entt::meta_any{42.}, entt::meta_any{'c'}); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); } @@ -986,7 +1044,7 @@ TEST_F(Meta, MetaCtorFuncMetaAnyArgs) { auto any = ctor.invoke(base_type{}, entt::meta_any{42}); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); } @@ -1001,7 +1059,7 @@ TEST_F(Meta, MetaCtorFuncCastAndConvert) { auto any = ctor.invoke(entt::meta_any{derived_type{}}, entt::meta_any{42.}); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); } @@ -1028,7 +1086,8 @@ TEST_F(Meta, MetaDtorMetaAnyArg) { } TEST_F(Meta, MetaDtorMetaAnyInvalidArg) { - ASSERT_FALSE(entt::resolve().dtor().invoke(int{})); + auto instance = 0; + ASSERT_FALSE(entt::resolve().dtor().invoke(instance)); } @@ -1154,12 +1213,13 @@ TEST_F(Meta, MetaDataGetMetaAnyArg) { const auto value = data.get(any); ASSERT_TRUE(value); - ASSERT_TRUE(value.can_cast()); + ASSERT_TRUE(value.cast()); ASSERT_EQ(value.cast(), 99); } TEST_F(Meta, MetaDataGetInvalidArg) { - ASSERT_FALSE(entt::resolve().data("i"_hs).get(0)); + auto instance = 0; + ASSERT_FALSE(entt::resolve().data("i"_hs).get(instance)); } TEST_F(Meta, MetaDataSetMetaAnyArg) { @@ -1274,17 +1334,17 @@ TEST_F(Meta, MetaDataArrayStatic) { ASSERT_TRUE(data.is_static()); ASSERT_TRUE(data.type().is_array()); ASSERT_EQ(data.type().extent(), 3); - ASSERT_EQ(data.get(nullptr, 0).cast(), 3); - ASSERT_EQ(data.get(nullptr, 1).cast(), 5); - ASSERT_EQ(data.get(nullptr, 2).cast(), 7); - ASSERT_FALSE(data.set(nullptr, 0, 'c')); - ASSERT_EQ(data.get(nullptr, 0).cast(), 3); - ASSERT_TRUE(data.set(nullptr, 0, data.get(nullptr, 0).cast()+2)); - ASSERT_TRUE(data.set(nullptr, 1, data.get(nullptr, 1).cast()+2)); - ASSERT_TRUE(data.set(nullptr, 2, data.get(nullptr, 2).cast()+2)); - ASSERT_EQ(data.get(nullptr, 0).cast(), 5); - ASSERT_EQ(data.get(nullptr, 1).cast(), 7); - ASSERT_EQ(data.get(nullptr, 2).cast(), 9); + ASSERT_EQ(data.get({}, 0).cast(), 3); + ASSERT_EQ(data.get({}, 1).cast(), 5); + ASSERT_EQ(data.get({}, 2).cast(), 7); + ASSERT_FALSE(data.set({}, 0, 'c')); + ASSERT_EQ(data.get({}, 0).cast(), 3); + ASSERT_TRUE(data.set({}, 0, data.get({}, 0).cast()+2)); + ASSERT_TRUE(data.set({}, 1, data.get({}, 1).cast()+2)); + ASSERT_TRUE(data.set({}, 2, data.get({}, 2).cast()+2)); + ASSERT_EQ(data.get({}, 0).cast(), 5); + ASSERT_EQ(data.get({}, 1).cast(), 7); + ASSERT_EQ(data.get({}, 2).cast(), 9); } TEST_F(Meta, MetaDataArray) { @@ -1411,7 +1471,7 @@ TEST_F(Meta, MetaFuncRetVoid) { auto any = func.invoke(instance, 5); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(func_type::value, 25); func.prop([](auto prop) { @@ -1481,7 +1541,7 @@ TEST_F(Meta, MetaFuncStaticRetVoid) { auto any = func.invoke({}, 42); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_EQ(any.type(), entt::resolve()); ASSERT_EQ(func_type::value, 42); func.prop([](auto *prop) { @@ -1501,7 +1561,9 @@ TEST_F(Meta, MetaFuncStaticRetVoid) { TEST_F(Meta, MetaFuncMetaAnyArgs) { auto func = entt::resolve().func("f1"_hs); - auto any = func.invoke(func_type{}, entt::meta_any{3}); + func_type instance; + + auto any = func.invoke(instance, entt::meta_any{3}); ASSERT_TRUE(any); ASSERT_EQ(any.type(), entt::resolve()); @@ -1510,12 +1572,16 @@ TEST_F(Meta, MetaFuncMetaAnyArgs) { TEST_F(Meta, MetaFuncInvalidArgs) { auto func = entt::resolve().func("f1"_hs); - ASSERT_FALSE(func.invoke(empty_type{}, entt::meta_any{'c'})); + empty_type instance; + + ASSERT_FALSE(func.invoke(instance, entt::meta_any{'c'})); } TEST_F(Meta, MetaFuncCastAndConvert) { auto func = entt::resolve().func("f3"_hs); - auto any = func.invoke(func_type{}, derived_type{}, 0, 3.); + func_type instance; + + auto any = func.invoke(instance, derived_type{}, 0, 3.); ASSERT_TRUE(any); ASSERT_EQ(any.type(), entt::resolve()); @@ -1640,7 +1706,7 @@ TEST_F(Meta, MetaTypeConstruct) { auto any = type.construct(base_type{}, 42, 'c'); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); } @@ -1650,7 +1716,7 @@ TEST_F(Meta, MetaTypeConstructMetaAnyArgs) { auto any = type.construct(entt::meta_any{base_type{}}, entt::meta_any{42}, entt::meta_any{'c'}); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); } @@ -1672,42 +1738,46 @@ TEST_F(Meta, MetaTypeConstructCastAndConvert) { auto any = type.construct(entt::meta_any{derived_type{}}, entt::meta_any{42.}, entt::meta_any{'c'}); ASSERT_TRUE(any); - ASSERT_TRUE(any.can_cast()); + ASSERT_TRUE(any.try_cast()); ASSERT_EQ(any.cast().i, 42); ASSERT_EQ(any.cast().c, 'c'); } TEST_F(Meta, MetaTypeDestroyDtor) { auto type = entt::resolve(); + empty_type instance; ASSERT_EQ(empty_type::counter, 0); - ASSERT_TRUE(type.destroy(empty_type{})); + ASSERT_TRUE(type.destroy(instance)); ASSERT_EQ(empty_type::counter, 1); } TEST_F(Meta, MetaTypeDestroyDtorInvalidArg) { auto type = entt::resolve(); + auto instance = 'c'; ASSERT_EQ(empty_type::counter, 0); - ASSERT_FALSE(type.destroy('c')); + ASSERT_FALSE(type.destroy(instance)); ASSERT_EQ(empty_type::counter, 0); } TEST_F(Meta, MetaTypeDestroyDtorCastAndConvert) { auto type = entt::resolve(); + fat_type instance{}; ASSERT_EQ(empty_type::counter, 0); - ASSERT_FALSE(type.destroy(fat_type{})); + ASSERT_FALSE(type.destroy(instance)); ASSERT_EQ(empty_type::counter, 0); - ASSERT_FALSE(entt::resolve().destroy(42.)); } TEST_F(Meta, MetaTypeDestroyNoDtor) { - ASSERT_TRUE(entt::resolve().destroy('c')); + auto instance = 'c'; + ASSERT_TRUE(entt::resolve().destroy(instance)); } TEST_F(Meta, MetaTypeDestroyNoDtorInvalidArg) { - ASSERT_FALSE(entt::resolve().destroy(42)); + auto instance = 42; + ASSERT_FALSE(entt::resolve().destroy(instance)); } TEST_F(Meta, MetaTypeDestroyNoDtorVoid) { @@ -1715,7 +1785,8 @@ TEST_F(Meta, MetaTypeDestroyNoDtorVoid) { } TEST_F(Meta, MetaTypeDestroyNoDtorCastAndConvert) { - ASSERT_FALSE(entt::resolve().destroy(42.)); + auto instance = 42.; + ASSERT_FALSE(entt::resolve().destroy(instance)); } TEST_F(Meta, MetaDataFromBase) { @@ -1853,8 +1924,8 @@ TEST_F(Meta, Unregister) { entt::meta_any any{42.}; ASSERT_TRUE(any); - ASSERT_FALSE(any.can_convert()); - ASSERT_TRUE(any.can_convert()); + ASSERT_FALSE(any.convert()); + ASSERT_TRUE(any.convert()); ASSERT_FALSE(entt::resolve("derived"_hs)); ASSERT_TRUE(entt::resolve("my_type"_hs));