diff --git a/src/entt/core/any.hpp b/src/entt/core/any.hpp index ad6186ef0..fb0701eec 100644 --- a/src/entt/core/any.hpp +++ b/src/entt/core/any.hpp @@ -290,6 +290,40 @@ public: return *this; } + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + bool has_value() const noexcept { + return (vtable != nullptr); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @param req Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + bool has_value(const type_info &req) const noexcept { + return (descriptor() == req); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @tparam Type Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + template + bool has_value() const noexcept { + static_assert(std::is_same_v, Type>, "Invalid type"); + constexpr const type_info &(*other)() noexcept = &type_id; + // it could be a call across boundaries, but still for the same type + return (descriptor == other) || has_value(other()); + } + /** * @brief Returns the object type info if any, `type_id()` otherwise. * @return The object type info if any, `type_id()` otherwise. @@ -327,9 +361,7 @@ public: */ template [[nodiscard]] const Type *data() const noexcept { - constexpr const type_info &(*other)() noexcept = &type_id>; - // it could be a call across boundaries, but still for the same type - return static_cast((descriptor == other) ? data() : data(type_id>())); + return has_value>() ? static_cast(data()) : nullptr; } /** @@ -381,7 +413,7 @@ public: * @return True in case of success, false otherwise. */ bool assign(const basic_any &other) { - if(vtable && (mode != any_policy::cref) && ((descriptor == other.descriptor) || (info() == other.info()))) { + if(vtable && (mode != any_policy::cref) && ((descriptor == other.descriptor) || has_value(other.info()))) { return (vtable(request::assign, *this, other.data()) != nullptr); } @@ -391,7 +423,7 @@ public: /*! @copydoc assign */ // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) bool assign(basic_any &&other) { - if(vtable && (mode != any_policy::cref) && ((descriptor == other.descriptor) || (info() == other.info()))) { + if(vtable && (mode != any_policy::cref) && ((descriptor == other.descriptor) || has_value(other.info()))) { if(auto *val = other.data(); val) { return (vtable(request::transfer, *this, val) != nullptr); } @@ -419,7 +451,7 @@ public: * @return False if the wrapper is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return vtable != nullptr; + return has_value(); } /** diff --git a/test/entt/core/any.cpp b/test/entt/core/any.cpp index 749554665..b737c5e77 100644 --- a/test/entt/core/any.cpp +++ b/test/entt/core/any.cpp @@ -57,6 +57,24 @@ TEST(Any, Empty) { ASSERT_EQ(any.data(), nullptr); } +TEST(Any, HasValue) { + entt::any any{}; + + ASSERT_FALSE(any.has_value()); + ASSERT_FALSE(any.has_value(entt::type_id())); + ASSERT_FALSE(any.has_value(entt::type_id())); + ASSERT_FALSE(any.has_value()); + ASSERT_FALSE(any.has_value()); + + any = 2; + + ASSERT_TRUE(any.has_value()); + ASSERT_FALSE(any.has_value(entt::type_id())); + ASSERT_TRUE(any.has_value(entt::type_id())); + ASSERT_FALSE(any.has_value()); + ASSERT_TRUE(any.has_value()); +} + TEST(Any, SBO) { entt::any any{'c'};