diff --git a/src/entt/core/any.hpp b/src/entt/core/any.hpp index 9dde07b91..efa230550 100644 --- a/src/entt/core/any.hpp +++ b/src/entt/core/any.hpp @@ -23,28 +23,15 @@ namespace entt { */ template class basic_any { - enum class operation: std::uint8_t { COPY, MOVE, DTOR, COMP, ADDR }; + enum class operation: std::uint8_t { COPY, MOVE, DTOR, COMP, GET, TYPE }; enum class policy: std::uint8_t { OWNER, REF, CREF }; using storage_type = std::aligned_storage_t; - using vtable_type = const void *(const operation, const basic_any &, void *); + using vtable_type = const void *(const operation, const basic_any &, const void *); template static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; - template - [[nodiscard]] static constexpr policy type_to_policy() { - if constexpr(std::is_lvalue_reference_v) { - if constexpr(std::is_const_v>) { - return policy::CREF; - } else { - return policy::REF; - } - } else { - return policy::OWNER; - } - } - template [[nodiscard]] static bool compare(const void *lhs, const void *rhs) { if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { @@ -55,58 +42,63 @@ class basic_any { } template - static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] void *to) { - static_assert(std::is_same_v>, Type>, "Invalid type"); + static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) { + static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); + const Type *instance = nullptr; - if constexpr(!std::is_void_v) { - const Type *instance = (in_situ && from.mode == policy::OWNER) - ? ENTT_LAUNDER(reinterpret_cast(&from.storage)) - : static_cast(from.instance); - - switch(op) { - case operation::COPY: - if constexpr(std::is_copy_constructible_v) { - static_cast(to)->emplace(*instance); - } - break; - case operation::MOVE: - if constexpr(in_situ) { - if(from.mode == policy::OWNER) { - return new (&static_cast(to)->storage) Type{std::move(*const_cast(instance))}; - } - } - - return (static_cast(to)->instance = std::exchange(const_cast(from).instance, nullptr)); - case operation::DTOR: - if constexpr(in_situ) { - instance->~Type(); - } else if constexpr(std::is_array_v) { - delete[] instance; - } else { - delete instance; - } - break; - case operation::COMP: - return compare(instance, (*static_cast(to))->data()) ? to : nullptr; - case operation::ADDR: - return instance; + if constexpr(in_situ) { + instance = (from.mode == policy::OWNER) ? ENTT_LAUNDER(reinterpret_cast(&from.storage)) : static_cast(from.instance); + } else { + instance = static_cast(from.instance); + } + + switch(op) { + case operation::COPY: + if constexpr(std::is_copy_constructible_v) { + static_cast(const_cast(to))->initialize(*instance); } + break; + case operation::MOVE: + if constexpr(in_situ) { + if(from.mode == policy::OWNER) { + return new (&static_cast(const_cast(to))->storage) Type{std::move(*const_cast(instance))}; + } + } + + return (static_cast(const_cast(to))->instance = std::exchange(const_cast(from).instance, nullptr)); + case operation::DTOR: + if constexpr(in_situ) { + instance->~Type(); + } else if constexpr(std::is_array_v) { + delete[] instance; + } else { + delete instance; + } + break; + case operation::COMP: + { + const auto info = type_id(); + auto *value = static_cast(to)->data(&info); + return (value && compare(instance, value)) ? to : nullptr; + } + case operation::GET: + return (!to || (*static_cast(to) == type_id())) ? instance : nullptr; + case operation::TYPE: + *static_cast(const_cast(to)) = type_id(); + break; } return nullptr; } - void destroy_if_owner() { - if(mode == policy::OWNER) { - vtable(operation::DTOR, *this, nullptr); - } - } - template void initialize([[maybe_unused]] Args &&... args) { if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + mode = std::is_const_v> ? policy::CREF : policy::REF; instance = (std::addressof(args), ...); } else if constexpr(in_situ) { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { @@ -127,7 +119,6 @@ class basic_any { basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT : instance{other.data()}, vtable{other.vtable}, - descriptor{other.descriptor}, mode{pol} {} @@ -140,8 +131,7 @@ public: /*! @brief Default constructor. */ basic_any() ENTT_NOEXCEPT : instance{}, - vtable{&basic_vtable}, - descriptor{type_id()}, + vtable{}, mode{policy::OWNER} {} @@ -153,10 +143,7 @@ public: */ template explicit basic_any(std::in_place_type_t, Args &&... args) - : instance{}, - vtable{&basic_vtable>>}, - descriptor{type_id>>()}, - mode{type_to_policy()} + : basic_any{} { initialize(std::forward(args)...); } @@ -168,10 +155,7 @@ public: */ template, basic_any>>> basic_any(Type &&value) - : instance{}, - vtable{&basic_vtable>}, - descriptor{type_id>()}, - mode{policy::OWNER} + : basic_any{} { initialize>(std::forward(value)); } @@ -183,7 +167,9 @@ public: basic_any(const basic_any &other) : basic_any{} { - other.vtable(operation::COPY, other, this); + if(other.vtable) { + other.vtable(operation::COPY, other, this); + } } /** @@ -193,15 +179,18 @@ public: basic_any(basic_any &&other) ENTT_NOEXCEPT : instance{}, vtable{other.vtable}, - descriptor{other.descriptor}, mode{other.mode} { - other.vtable(operation::MOVE, other, this); + if(other.vtable) { + other.vtable(operation::MOVE, other, this); + } } /*! @brief Frees the internal storage, whatever it means. */ ~basic_any() { - destroy_if_owner(); + if(vtable && mode == policy::OWNER) { + vtable(operation::DTOR, *this, nullptr); + } } /** @@ -211,7 +200,11 @@ public: */ basic_any & operator=(const basic_any &other) { reset(); - other.vtable(operation::COPY, other, this); + + if(other.vtable) { + other.vtable(operation::COPY, other, this); + } + return *this; } @@ -221,11 +214,14 @@ public: * @return This any object. */ basic_any & operator=(basic_any &&other) ENTT_NOEXCEPT { - destroy_if_owner(); - other.vtable(operation::MOVE, other, this); - vtable = other.vtable; - descriptor = other.descriptor; - mode = other.mode; + reset(); + + if(other.vtable) { + other.vtable(operation::MOVE, other, this); + vtable = other.vtable; + mode = other.mode; + } + return *this; } @@ -247,20 +243,27 @@ public: * @return The object type if any, `type_id()` otherwise. */ [[nodiscard]] type_info type() const ENTT_NOEXCEPT { - return descriptor; + if(vtable) { + type_info info{}; + vtable(operation::TYPE, *this, &info); + return info; + } + + return type_id(); } /** * @brief Returns an opaque pointer to the contained instance. + * @param req Optional expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void * data() const ENTT_NOEXCEPT { - return vtable(operation::ADDR, *this, nullptr); + [[nodiscard]] const void * data(const type_info *req = nullptr) const ENTT_NOEXCEPT { + return vtable ? vtable(operation::GET, *this, req) : nullptr; } /*! @copydoc data */ - [[nodiscard]] void * data() ENTT_NOEXCEPT { - return mode == policy::CREF ? nullptr : const_cast(vtable(operation::ADDR, *this, nullptr)); + [[nodiscard]] void * data(const type_info *req = nullptr) ENTT_NOEXCEPT { + return (!vtable || mode == policy::CREF) ? nullptr : const_cast(vtable(operation::GET, *this, req)); } /** @@ -271,18 +274,17 @@ public: */ template void emplace(Args &&... args) { - destroy_if_owner(); + reset(); initialize(std::forward(args)...); - vtable = &basic_vtable>>; - descriptor = type_id(); - mode = type_to_policy(); } /*! @brief Destroys contained object */ void reset() { - destroy_if_owner(); - vtable = &basic_vtable; - descriptor = type_id(); + if(vtable && mode == policy::OWNER) { + vtable(operation::DTOR, *this, nullptr); + } + + vtable = nullptr; mode = policy::OWNER; } @@ -291,7 +293,7 @@ public: * @return False if the wrapper is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return descriptor != type_id(); + return vtable != nullptr; } /** @@ -300,8 +302,7 @@ public: * @return False if the two objects differ in their content, true otherwise. */ bool operator==(const basic_any &other) const ENTT_NOEXCEPT { - const basic_any *trampoline = &other; - return descriptor == other.descriptor && (vtable(operation::COMP, *this, &trampoline) || !other.data()); + return (!vtable && !other.vtable) || (vtable && other.vtable && vtable(operation::COMP, *this, &other)); } /** @@ -328,7 +329,6 @@ public: private: union { const void *instance; storage_type storage; }; vtable_type *vtable; - type_info descriptor; policy mode; }; @@ -393,23 +393,17 @@ Type any_cast(basic_any &&data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { - if(data->type() == type_id>>()) { - return static_cast(data->data()); - } - - return nullptr; + const auto info = type_id>>(); + return static_cast(data->data(&info)); } /*! @copydoc any_cast */ template Type * any_cast(basic_any *data) ENTT_NOEXCEPT { - if(data->type() == type_id>>()) { - // last attempt to make wrappers for const references return their values - return static_cast(static_cast, Type> *>(data)->data()); - } - - return nullptr; + const auto info = type_id>>(); + // last attempt to make wrappers for const references return their values + return static_cast(static_cast, Type> *>(data)->data(&info)); }