Просмотр исходного кода

any: typed ::data with fallback (perf)

skypjack 5 месяцев назад
Родитель
Сommit
80eba8afb4
2 измененных файлов с 84 добавлено и 13 удалено
  1. 35 11
      src/entt/core/any.hpp
  2. 49 2
      test/entt/core/any.cpp

+ 35 - 11
src/entt/core/any.hpp

@@ -118,7 +118,7 @@ class basic_any {
             using plain_type = std::remove_const_t<std::remove_reference_t<Type>>;
 
             vtable = basic_vtable<plain_type>;
-            descriptor = &type_id<plain_type>();
+            descriptor = &type_id<plain_type>;
 
             if constexpr(std::is_lvalue_reference_v<Type>) {
                 static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
@@ -295,7 +295,7 @@ public:
      * @return The object type info if any, `type_id<void>()` otherwise.
      */
     [[nodiscard]] const type_info &info() const noexcept {
-        return (descriptor == nullptr) ? type_id<void>() : *descriptor;
+        return descriptor();
     }
 
     /*! @copydoc info */
@@ -320,6 +320,18 @@ public:
         return (info() == req) ? data() : nullptr;
     }
 
+    /**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @tparam Type Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+    template<typename Type>
+    [[nodiscard]] const Type *data() const noexcept {
+        constexpr const type_info &(*other)() noexcept = &type_id<std::remove_const_t<Type>>;
+        // it could be a call across boundaries, but still for the same type
+        return static_cast<const Type *>((descriptor == other) ? data() : data(type_id<std::remove_const_t<Type>>()));
+    }
+
     /**
      * @brief Returns an opaque pointer to the contained instance.
      * @return An opaque pointer the contained instance, if any.
@@ -337,6 +349,20 @@ public:
         return (mode == any_policy::cref) ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
     }
 
+    /**
+     * @brief Returns an opaque pointer to the contained instance.
+     * @tparam Type Expected type.
+     * @return An opaque pointer the contained instance, if any.
+     */
+    template<typename Type>
+    [[nodiscard]] Type *data() noexcept {
+        if constexpr(std::is_const_v<Type>) {
+            return std::as_const(*this).data<std::remove_const_t<Type>>();
+        } else {
+            return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(std::as_const(*this).data<std::remove_const_t<Type>>());
+        }
+    }
+
     /**
      * @brief Replaces the contained object by creating a new instance directly.
      * @tparam Type Type of object to use to initialize the wrapper.
@@ -355,7 +381,7 @@ public:
      * @return True in case of success, false otherwise.
      */
     bool assign(const basic_any &other) {
-        if(vtable && (mode != any_policy::cref) && (*descriptor == other.info())) {
+        if(vtable && (mode != any_policy::cref) && ((descriptor == other.descriptor) || (info() == other.info()))) {
             return (vtable(request::assign, *this, other.data()) != nullptr);
         }
 
@@ -365,7 +391,7 @@ public:
     /*! @copydoc assign */
     // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
     bool assign(basic_any &&other) {
-        if(vtable && (mode != any_policy::cref) && (*descriptor == other.info())) {
+        if(vtable && (mode != any_policy::cref) && ((descriptor == other.descriptor) || (info() == other.info()))) {
             if(auto *val = other.data(); val) {
                 return (vtable(request::transfer, *this, val) != nullptr);
             }
@@ -384,7 +410,7 @@ public:
 
         instance = nullptr;
         vtable = nullptr;
-        descriptor = nullptr;
+        descriptor = &type_id<void>;
         mode = any_policy::empty;
     }
 
@@ -402,7 +428,7 @@ public:
      * @return False if the two objects differ in their content, true otherwise.
      */
     [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
-        if(vtable && *descriptor == other.info()) {
+        if(vtable && info() == other.info()) {
             return (vtable(request::compare, *this, other.data()) != nullptr);
         }
 
@@ -453,7 +479,7 @@ private:
         storage_type storage;
     };
     vtable_type *vtable{};
-    const type_info *descriptor{};
+    const type_info &(*descriptor)() noexcept {&type_id<void>};
     any_policy mode{any_policy::empty};
 };
 
@@ -501,8 +527,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
 /*! @copydoc any_cast */
 template<typename Type, std::size_t Len, std::size_t Align>
 [[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
-    const auto &info = type_id<std::remove_const_t<Type>>();
-    return static_cast<const Type *>(data->data(info));
+    return data->data<std::remove_const_t<Type>>();
 }
 
 /*! @copydoc any_cast */
@@ -512,8 +537,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
         // last attempt to make wrappers for const references return their values
         return any_cast<Type>(&std::as_const(*data));
     } else {
-        const auto &info = type_id<Type>();
-        return static_cast<Type *>(data->data(info));
+        return data->data<Type>();
     }
 }
 

+ 49 - 2
test/entt/core/any.cpp

@@ -1443,6 +1443,53 @@ TEST(Any, CompareVoid) {
     ASSERT_FALSE(entt::any{} != any);
 }
 
+TEST(Any, Data) {
+    entt::any any{2};
+    const auto &cany = any;
+    entt::any empty{};
+
+    ASSERT_EQ(empty.data(), nullptr);
+    ASSERT_NE(any.data(), nullptr);
+    ASSERT_NE(cany.data(), nullptr);
+    ASSERT_NE(any.as_ref().data(), nullptr);
+    ASSERT_EQ(cany.as_ref().data(), nullptr);
+
+    ASSERT_EQ(empty.data<char>(), nullptr);
+    ASSERT_EQ(any.data<char>(), nullptr);
+    ASSERT_EQ(cany.data<char>(), nullptr);
+    ASSERT_EQ(any.data<const char>(), nullptr);
+    ASSERT_EQ(cany.data<const char>(), nullptr);
+    ASSERT_EQ(any.as_ref().data<char>(), nullptr);
+    ASSERT_EQ(cany.as_ref().data<char>(), nullptr);
+    ASSERT_EQ(any.as_ref().data<const char>(), nullptr);
+    ASSERT_EQ(cany.as_ref().data<const char>(), nullptr);
+
+    ASSERT_EQ(empty.data<int>(), nullptr);
+    ASSERT_NE(any.data<int>(), nullptr);
+    ASSERT_NE(cany.data<int>(), nullptr);
+    ASSERT_NE(any.data<const int>(), nullptr);
+    ASSERT_NE(cany.data<const int>(), nullptr);
+    ASSERT_NE(any.as_ref().data<int>(), nullptr);
+    ASSERT_EQ(cany.as_ref().data<int>(), nullptr);
+    ASSERT_NE(any.as_ref().data<const int>(), nullptr);
+    ASSERT_NE(cany.as_ref().data<const int>(), nullptr);
+
+    const auto &char_info = entt::type_id<char>();
+    const auto &int_info = entt::type_id<int>();
+
+    ASSERT_EQ(empty.data(char_info), nullptr);
+    ASSERT_EQ(any.data(char_info), nullptr);
+    ASSERT_EQ(cany.data(char_info), nullptr);
+    ASSERT_EQ(any.as_ref().data(char_info), nullptr);
+    ASSERT_EQ(cany.as_ref().data(char_info), nullptr);
+
+    ASSERT_EQ(empty.data(int_info), nullptr);
+    ASSERT_NE(any.data(int_info), nullptr);
+    ASSERT_NE(cany.data(int_info), nullptr);
+    ASSERT_NE(any.as_ref().data(int_info), nullptr);
+    ASSERT_EQ(cany.as_ref().data(int_info), nullptr);
+}
+
 TEST(Any, AnyCast) {
     entt::any any{2};
     const auto &cany = any;
@@ -1653,10 +1700,10 @@ TEST(Any, Array) {
     ASSERT_EQ(entt::any_cast<int *>(&any), nullptr);
 
     // NOLINTNEXTLINE(*-avoid-c-arrays)
-    entt::any_cast<int(&)[1]>(any)[0] = 2;
+    entt::any_cast<int (&)[1]>(any)[0] = 2;
 
     // NOLINTNEXTLINE(*-avoid-c-arrays)
-    ASSERT_EQ(entt::any_cast<const int(&)[1]>(std::as_const(any))[0], 2);
+    ASSERT_EQ(entt::any_cast<const int (&)[1]>(std::as_const(any))[0], 2);
 }
 
 TEST(Any, CopyMoveReference) {