1
0
Эх сурвалжийг харах

any:
* added configurable sbo size (0 -> forced dyn)
* reverted changes for forced dyn vtable

Michele Caini 5 жил өмнө
parent
commit
07565927f2
4 өөрчлөгдсөн 166 нэмэгдсэн , 161 устгасан
  1. 2 1
      TODO
  2. 155 159
      src/entt/core/any.hpp
  3. 8 0
      src/entt/core/fwd.hpp
  4. 1 1
      test/entt/core/any.cpp

+ 2 - 1
TODO

@@ -28,11 +28,12 @@ WIP:
 * HP: paginate pools
 * HP: headless (sparse set only) view
 * HP: write documentation for custom storages and views!!
-* HP: any/poly: configurable sbo size, compile-time policies like sbo-required.
+* HP: meta_any/poly: configurable sbo size.
 * HP: registry: use a poly object for pools, no more pool_data type.
 * HP: make runtime views use opaque storage and therefore return also elements.
 * HP: add exclude-only views to combine with packs
 * HP: entity-aware observer, add observer functions aside observer class
+* HP: any and the like: remove constructor that accepts reference wrapper, allow only in-place T&?
 * deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
 * pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
 * snapshot: support for range-based archives

+ 155 - 159
src/entt/core/any.hpp

@@ -2,11 +2,13 @@
 #define ENTT_CORE_ANY_HPP
 
 
+#include <cstddef>
 #include <functional>
 #include <new>
 #include <type_traits>
 #include <utility>
 #include "../config/config.h"
+#include "fwd.hpp"
 #include "type_info.hpp"
 #include "type_traits.hpp"
 
@@ -14,15 +16,19 @@
 namespace entt {
 
 
-/*! @brief A SBO friendly, type-safe container for single values of any type. */
-class any {
+/**
+ * @brief A SBO friendly, type-safe container for single values of any type.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
+ */
+template<std::size_t Len>
+class basic_any {
     enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE };
 
-    using storage_type = std::aligned_storage_t<sizeof(double[2])>;
-    using vtable_type = const void *(const operation, const any &, const void *);
+    using storage_type = std::aligned_storage_t<Len == 0u ? 1u : Len>;
+    using vtable_type = const void *(const operation, const basic_any &, const void *);
 
     template<typename Type>
-    static constexpr auto in_situ = sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;
+    static constexpr auto in_situ = (Len != 0u) && (sizeof(Type) <= sizeof(storage_type)) && std::is_nothrow_move_constructible_v<Type>;
 
     template<typename Type>
     [[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
@@ -38,123 +44,113 @@ class any {
         return *const_cast<Type *>(static_cast<const Type *>(to));
     }
 
-    static const void * vtable_void(const operation, const any &, const void *) {
-        return nullptr;
-    }
-
-    template<typename Type>
-    static const void * vtable_ref(const operation op, const any &from, const void *to) {
-        using base_type = std::remove_const_t<Type>;
-
-        switch(op) {
-        case operation::COPY:
-            if constexpr(std::is_copy_constructible_v<base_type>) {
-                as<any>(to).emplace<base_type>(*static_cast<const base_type *>(from.instance));
-            }
-            break;
-        case operation::MOVE:
-            as<any>(to).instance = from.instance;
-        case operation::DTOR:
-            break;
-        case operation::COMP:
-            return compare<base_type>(from.instance, to) ? to : nullptr;
-        case operation::ADDR:
-            return std::is_const_v<Type> ? nullptr : from.instance;
-        case operation::CADDR:
-            return from.instance;
-        case operation::REF:
-            as<any>(to).instance = from.instance;
-            as<any>(to).vtable = vtable_ref<Type>;
-            break;
-        case operation::CREF:
-            as<any>(to).instance = from.instance;
-            as<any>(to).vtable = vtable_ref<const Type>;
-            break;
-        case operation::TYPE:
-            as<type_info>(to) = type_id<base_type>();
-            break;
-        }
-
-        return nullptr;
-    }
-
-    template<typename Type>
-    static const void * vtable_in_situ(const operation op, const any &from, const void *to) {
-        #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
-        auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));
-        #else
-        auto *instance = const_cast<Type *>(reinterpret_cast<const Type *>(&from.storage));
-        #endif
-
-        switch(op) {
-        case operation::COPY:
-            if constexpr(std::is_copy_constructible_v<Type>) {
-                new (&as<any>(to).storage) Type{std::as_const(*instance)};
-                as<any>(to).vtable = from.vtable;
-            }
-            break;
-        case operation::MOVE:
-            new (&as<any>(to).storage) Type{std::move(*instance)};
-            [[fallthrough]];
-        case operation::DTOR:
-            instance->~Type();
-            break;
-        case operation::COMP:
-            return compare<Type>(instance, to) ? to : nullptr;
-        case operation::ADDR:
-        case operation::CADDR:
-            return instance;
-        case operation::REF:
-            as<any>(to).instance = instance;
-            as<any>(to).vtable = vtable_ref<Type>;
-            break;
-        case operation::CREF:
-            as<any>(to).instance = instance;
-            as<any>(to).vtable = vtable_ref<const Type>;
-            break;
-        case operation::TYPE:
-            as<type_info>(to) = type_id<Type>();
-            break;
-        }
-
-        return nullptr;
-    }
-
     template<typename Type>
-    static const void * vtable_default(const operation op, const any &from, const void *to) {
-        switch(op) {
-        case operation::COPY:
-            if constexpr(std::is_copy_constructible_v<Type>) {
-                as<any>(to).instance = new Type{*static_cast<const Type *>(from.instance)};
-                as<any>(to).vtable = from.vtable;
-            }
-            break;
-        case operation::MOVE:
-            as<any>(to).instance = from.instance;
-            break;
-        case operation::DTOR:
-            if constexpr(std::is_array_v<Type>) {
-                delete[] static_cast<const Type *>(from.instance);
+    static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &from, [[maybe_unused]] const void *to) {
+        if constexpr(!std::is_void_v<Type>) {
+            if constexpr(std::is_lvalue_reference_v<Type>) {
+                using base_type = std::remove_const_t<std::remove_reference_t<Type>>;
+
+                switch(op) {
+                case operation::COPY:
+                    if constexpr(std::is_copy_constructible_v<base_type>) {
+                        as<basic_any>(to).emplace<base_type>(*static_cast<const base_type *>(from.instance));
+                    }
+                    break;
+                case operation::MOVE:
+                    as<basic_any>(to).instance = from.instance;
+                case operation::DTOR:
+                    break;
+                case operation::COMP:
+                    return compare<base_type>(from.instance, to) ? to : nullptr;
+                case operation::ADDR:
+                    return std::is_const_v<std::remove_reference_t<Type>> ? nullptr : from.instance;
+                case operation::CADDR:
+                    return from.instance;
+                case operation::REF:
+                    as<basic_any>(to).instance = from.instance;
+                    as<basic_any>(to).vtable = basic_vtable<Type>;
+                    break;
+                case operation::CREF:
+                    as<basic_any>(to).instance = from.instance;
+                    as<basic_any>(to).vtable = basic_vtable<const base_type &>;
+                    break;
+                case operation::TYPE:
+                    as<type_info>(to) = type_id<base_type>();
+                    break;
+                }
+            } else if constexpr(in_situ<Type>) {
+                #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L
+                auto *instance = const_cast<Type *>(std::launder(reinterpret_cast<const Type *>(&from.storage)));
+                #else
+                auto *instance = const_cast<Type *>(reinterpret_cast<const Type *>(&from.storage));
+                #endif
+
+                switch(op) {
+                case operation::COPY:
+                    if constexpr(std::is_copy_constructible_v<Type>) {
+                        new (&as<basic_any>(to).storage) Type{std::as_const(*instance)};
+                        as<basic_any>(to).vtable = from.vtable;
+                    }
+                    break;
+                case operation::MOVE:
+                    new (&as<basic_any>(to).storage) Type{std::move(*instance)};
+                    [[fallthrough]];
+                case operation::DTOR:
+                    instance->~Type();
+                    break;
+                case operation::COMP:
+                    return compare<Type>(instance, to) ? to : nullptr;
+                case operation::ADDR:
+                case operation::CADDR:
+                    return instance;
+                case operation::REF:
+                    as<basic_any>(to).instance = instance;
+                    as<basic_any>(to).vtable = basic_vtable<Type &>;
+                    break;
+                case operation::CREF:
+                    as<basic_any>(to).instance = instance;
+                    as<basic_any>(to).vtable = basic_vtable<const Type &>;
+                    break;
+                case operation::TYPE:
+                    as<type_info>(to) = type_id<Type>();
+                    break;
+                }
             } else {
-                delete static_cast<const Type *>(from.instance);
+                switch(op) {
+                case operation::COPY:
+                    if constexpr(std::is_copy_constructible_v<Type>) {
+                        as<basic_any>(to).instance = new Type{*static_cast<const Type *>(from.instance)};
+                        as<basic_any>(to).vtable = from.vtable;
+                    }
+                    break;
+                case operation::MOVE:
+                    as<basic_any>(to).instance = from.instance;
+                    break;
+                case operation::DTOR:
+                    if constexpr(std::is_array_v<Type>) {
+                        delete[] static_cast<const Type *>(from.instance);
+                    } else {
+                        delete static_cast<const Type *>(from.instance);
+                    }
+                    break;
+                case operation::COMP:
+                    return compare<Type>(from.instance, to) ? to : nullptr;
+                case operation::ADDR:
+                case operation::CADDR:
+                    return from.instance;
+                case operation::REF:
+                    as<basic_any>(to).instance = from.instance;
+                    as<basic_any>(to).vtable = basic_vtable<Type &>;
+                    break;
+                case operation::CREF:
+                    as<basic_any>(to).instance = from.instance;
+                    as<basic_any>(to).vtable = basic_vtable<const Type &>;
+                    break;
+                case operation::TYPE:
+                    as<type_info>(to) = type_id<Type>();
+                    break;
+                }
             }
-            break;
-        case operation::COMP:
-            return compare<Type>(from.instance, to) ? to : nullptr;
-        case operation::ADDR:
-        case operation::CADDR:
-            return from.instance;
-        case operation::REF:
-            as<any>(to).instance = from.instance;
-            as<any>(to).vtable = vtable_ref<Type>;
-            break;
-        case operation::CREF:
-            as<any>(to).instance = from.instance;
-            as<any>(to).vtable = vtable_ref<const Type>;
-            break;
-        case operation::TYPE:
-            as<type_info>(to) = type_id<Type>();
-            break;
         }
 
         return nullptr;
@@ -162,8 +158,8 @@ class any {
 
 public:
     /*! @brief Default constructor. */
-    any() ENTT_NOEXCEPT
-        : any{std::in_place_type<void>}
+    basic_any() ENTT_NOEXCEPT
+        : basic_any{std::in_place_type<void>}
     {}
 
     /**
@@ -173,21 +169,18 @@ public:
      * @param args Parameters to use to construct the instance.
      */
     template<typename Type, typename... Args>
-    explicit any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
-        : vtable{&vtable_void},
+    explicit basic_any(std::in_place_type_t<Type>, [[maybe_unused]] Args &&... args)
+        : vtable{&basic_vtable<Type>},
           instance{}
     {
         if constexpr(!std::is_void_v<Type>) {
             if constexpr(std::is_lvalue_reference_v<Type>) {
                 static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...));
                 instance = (&args, ...);
-                vtable = vtable_ref<std::remove_reference_t<Type>>;
             } else if constexpr(in_situ<Type>) {
                 new (&storage) Type(std::forward<Args>(args)...);
-                vtable = vtable_in_situ<Type>;
             } else {
                 instance = new Type(std::forward<Args>(args)...);
-                vtable = vtable_default<Type>;
             }
         }
     }
@@ -198,8 +191,8 @@ public:
      * @param value An instance of an object to use to initialize the wrapper.
      */
     template<typename Type>
-    any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
-        : any{std::in_place_type<Type &>, value.get()}
+    basic_any(std::reference_wrapper<Type> value) ENTT_NOEXCEPT
+        : basic_any{std::in_place_type<Type &>, value.get()}
     {}
 
     /**
@@ -207,17 +200,17 @@ public:
      * @tparam Type Type of object to use to initialize the wrapper.
      * @param value An instance of an object to use to initialize the wrapper.
      */
-    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, any>>>
-    any(Type &&value)
-        : any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)}
+    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
+    basic_any(Type &&value)
+        : basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)}
     {}
 
     /**
      * @brief Copy constructor.
      * @param other The instance to copy from.
      */
-    any(const any &other)
-        : any{}
+    basic_any(const basic_any &other)
+        : basic_any{}
     {
         other.vtable(operation::COPY, other, this);
     }
@@ -226,15 +219,15 @@ public:
      * @brief Move constructor.
      * @param other The instance to move from.
      */
-    any(any &&other) ENTT_NOEXCEPT
-        : any{}
+    basic_any(basic_any &&other) ENTT_NOEXCEPT
+        : basic_any{}
     {
         other.vtable(operation::MOVE, other, this);
-        vtable = std::exchange(other.vtable, &vtable_void);
+        vtable = std::exchange(other.vtable, &basic_vtable<void>);
     }
 
     /*! @brief Frees the internal storage, whatever it means. */
-    ~any() {
+    ~basic_any() {
         vtable(operation::DTOR, *this, nullptr);
     }
 
@@ -243,7 +236,7 @@ public:
      * @param other The instance to assign from.
      * @return This any object.
      */
-    any & operator=(any other) {
+    basic_any & operator=(basic_any other) {
         swap(*this, other);
         return *this;
     }
@@ -279,12 +272,12 @@ public:
      */
     template<typename Type, typename... Args>
     void emplace(Args &&... args) {
-        *this = any{std::in_place_type<Type>, std::forward<Args>(args)...};
+        *this = basic_any{std::in_place_type<Type>, std::forward<Args>(args)...};
     }
 
     /*! @brief Destroys contained object */
     void reset() {
-        *this = any{};
+        *this = basic_any{};
     }
 
     /**
@@ -300,7 +293,7 @@ public:
      * @param other Wrapper with which to compare.
      * @return False if the two objects differ in their content, true otherwise.
      */
-    bool operator==(const any &other) const ENTT_NOEXCEPT {
+    bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
         return type() == other.type() && (vtable(operation::COMP, *this, other.data()) == other.data());
     }
 
@@ -309,8 +302,8 @@ public:
      * @param lhs A valid any object.
      * @param rhs A valid any object.
      */
-    friend void swap(any &lhs, any &rhs) {
-        any tmp{};
+    friend void swap(basic_any &lhs, basic_any &rhs) {
+        basic_any tmp{};
         lhs.vtable(operation::MOVE, lhs, &tmp);
         rhs.vtable(operation::MOVE, rhs, &lhs);
         lhs.vtable(operation::MOVE, tmp, &rhs);
@@ -321,15 +314,15 @@ public:
      * @brief Aliasing constructor.
      * @return An any that shares a reference to an unmanaged object.
      */
-    [[nodiscard]] any as_ref() ENTT_NOEXCEPT {
-        any ref{};
+    [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
+        basic_any ref{};
         vtable(operation::REF, *this, &ref);
         return ref;
     }
 
     /*! @copydoc as_ref */
-    [[nodiscard]] any as_ref() const ENTT_NOEXCEPT {
-        any ref{};
+    [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
+        basic_any ref{};
         vtable(operation::CREF, *this, &ref);
         return ref;
     }
@@ -342,23 +335,26 @@ private:
 
 /**
  * @brief Checks if two wrappers differ in their content.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
  * @param lhs A wrapper, either empty or not.
  * @param rhs A wrapper, either empty or not.
  * @return True if the two wrappers differ in their content, false otherwise.
  */
-[[nodiscard]] inline bool operator!=(const any &lhs, const any &rhs) ENTT_NOEXCEPT {
+template<std::size_t Len>
+[[nodiscard]] inline bool operator!=(const basic_any<Len> &lhs, const basic_any<Len> &rhs) ENTT_NOEXCEPT {
     return !(lhs == rhs);
 }
 
 
 /**
  * @brief Performs type-safe access to the contained object.
+ * @tparam Len Size of the storage reserved for the small buffer optimization.
  * @tparam Type Type to which conversion is required.
  * @param data Target any object.
  * @return The element converted to the requested type.
  */
-template<typename Type>
-Type any_cast(const any &data) ENTT_NOEXCEPT {
+template<typename Type, std::size_t Len>
+Type any_cast(const basic_any<Len> &data) ENTT_NOEXCEPT {
     const auto * const instance = any_cast<std::remove_reference_t<Type>>(&data);
     ENTT_ASSERT(instance);
     return static_cast<Type>(*instance);
@@ -366,8 +362,8 @@ Type any_cast(const any &data) ENTT_NOEXCEPT {
 
 
 /*! @copydoc any_cast */
-template<typename Type>
-Type any_cast(any &data) ENTT_NOEXCEPT {
+template<typename Type, std::size_t Len>
+Type any_cast(basic_any<Len> &data) ENTT_NOEXCEPT {
     // forces const on non-reference types to make them work also with wrappers for const references
     auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
     ENTT_ASSERT(instance);
@@ -376,8 +372,8 @@ Type any_cast(any &data) ENTT_NOEXCEPT {
 
 
 /*! @copydoc any_cast */
-template<typename Type>
-Type any_cast(any &&data) ENTT_NOEXCEPT {
+template<typename Type, std::size_t Len>
+Type any_cast(basic_any<Len> &&data) ENTT_NOEXCEPT {
     // forces const on non-reference types to make them work also with wrappers for const references
     auto * const instance = any_cast<std::conditional_t<std::is_reference_v<Type>, std::remove_reference_t<Type>, const Type>>(&data);
     ENTT_ASSERT(instance);
@@ -386,17 +382,17 @@ Type any_cast(any &&data) ENTT_NOEXCEPT {
 
 
 /*! @copydoc any_cast */
-template<typename Type>
-const Type * any_cast(const any *data) ENTT_NOEXCEPT {
+template<typename Type, std::size_t Len>
+const Type * any_cast(const basic_any<Len> *data) ENTT_NOEXCEPT {
     return (data->type() == type_id<Type>() ? static_cast<const Type *>(data->data()) : nullptr);
 }
 
 
 /*! @copydoc any_cast */
-template<typename Type>
-Type * any_cast(any *data) ENTT_NOEXCEPT {
+template<typename Type, std::size_t Len>
+Type * any_cast(basic_any<Len> *data) ENTT_NOEXCEPT {
     // last attempt to make wrappers for const references return their values
-    return (data->type() == type_id<Type>() ? static_cast<Type *>(static_cast<constness_as_t<any, Type> *>(data)->data()) : nullptr);
+    return (data->type() == type_id<Type>() ? static_cast<Type *>(static_cast<constness_as_t<basic_any<Len>, Type> *>(data)->data()) : nullptr);
 }
 
 

+ 8 - 0
src/entt/core/fwd.hpp

@@ -8,10 +8,18 @@
 namespace entt {
 
 
+template<std::size_t = sizeof(double[2])>
+class basic_any;
+
+
 /*! @brief Alias declaration for type identifiers. */
 using id_type = ENTT_ID_TYPE;
 
 
+/*! @brief Alias declaration for the most common use case. */
+using any = basic_any<>;
+
+
 }
 
 

+ 1 - 1
test/entt/core/any.cpp

@@ -801,7 +801,7 @@ TEST(Any, AnyCast) {
     ASSERT_EQ(*entt::any_cast<int>(&cany), 42);
     ASSERT_EQ(entt::any_cast<int &>(any), 42);
     ASSERT_EQ(entt::any_cast<const int &>(cany), 42);
-    ASSERT_EQ(entt::any_cast<int>(42), 42);
+    ASSERT_EQ(entt::any_cast<int>(entt::any{42}), 42);
 }
 
 TEST(Any, NotCopyableType) {