From f63f36a411c080df24782b7ce2ef8e38af81e77f Mon Sep 17 00:00:00 2001 From: skypjack Date: Thu, 9 Oct 2025 15:52:43 +0200 Subject: [PATCH] any: basic_any_storage, prepare for vtable-less optimizations --- src/entt/core/any.hpp | 74 +++++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/entt/core/any.hpp b/src/entt/core/any.hpp index ff95cb5ff..c4f16d6bb 100644 --- a/src/entt/core/any.hpp +++ b/src/entt/core/any.hpp @@ -28,26 +28,42 @@ enum class any_request : std::uint8_t { template struct basic_any_storage { - // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) - alignas(Align) std::byte data[Len]; + basic_any_storage() + : instance{} {} + + basic_any_storage(const void *elem) + : instance{elem} {} + + union { + const void *instance; + alignas(Align) std::byte buffer[Len]; + }; }; template -struct basic_any_storage<0u, Align> {}; +struct basic_any_storage<0u, Align> { + basic_any_storage() + : instance{} {} + + basic_any_storage(const void *elem) + : instance{elem} {} + + const void *instance{}; +}; } // namespace internal /*! @endcond */ /** * @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. + * @tparam Len Size of the buffer reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template -class basic_any { +class basic_any: private internal::basic_any_storage { using request = internal::any_request; using vtable_type = const void *(const request, const basic_any &, const void *); - using storage_type = internal::basic_any_storage; + using base_type = internal::basic_any_storage; template // NOLINTNEXTLINE(bugprone-sizeof-expression) @@ -59,7 +75,7 @@ class basic_any { const Type *elem = nullptr; if constexpr(in_situ) { - elem = (value.mode == any_policy::embedded) ? reinterpret_cast(&value.storage) : static_cast(value.instance); + elem = (value.mode == any_policy::embedded) ? reinterpret_cast(&value.buffer) : static_cast(value.instance); } else { elem = static_cast(value.instance); } @@ -103,7 +119,7 @@ class basic_any { ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy"); if constexpr(in_situ) { // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion) - return ::new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(elem))}; + return ::new(&static_cast(const_cast(other))->buffer) Type{std::move(*const_cast(elem))}; } [[fallthrough]]; case request::get: @@ -129,33 +145,33 @@ class basic_any { static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); mode = std::is_const_v> ? any_policy::cref : any_policy::ref; // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) - instance = (std::addressof(args), ...); + this->instance = (std::addressof(args), ...); } else if constexpr(in_situ) { mode = any_policy::embedded; if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { - ::new(&storage) plain_type{std::forward(args)...}; + ::new(&this->buffer) plain_type{std::forward(args)...}; } else { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) - ::new(&storage) plain_type(std::forward(args)...); + ::new(&this->buffer) plain_type(std::forward(args)...); } } else { mode = any_policy::dynamic; if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { - instance = new plain_type{std::forward(args)...}; + this->instance = new plain_type{std::forward(args)...}; } else if constexpr(std::is_array_v) { static_assert(sizeof...(Args) == 0u, "Invalid arguments"); - instance = new plain_type[std::extent_v](); + this->instance = new plain_type[std::extent_v](); } else { - instance = new plain_type(std::forward(args)...); + this->instance = new plain_type(std::forward(args)...); } } } } basic_any(const basic_any &other, const any_policy pol) noexcept - : instance{other.data()}, + : base_type{other.data()}, vtable{other.vtable}, descriptor{other.descriptor}, mode{pol} {} @@ -167,7 +183,7 @@ class basic_any { } public: - /*! @brief Size of the internal storage. */ + /*! @brief Size of the internal buffer. */ static constexpr auto length = Len; /*! @brief Alignment requirement. */ static constexpr auto alignment = Align; @@ -184,7 +200,7 @@ public: */ template explicit basic_any(std::in_place_type_t, Args &&...args) - : instance{} { + : base_type{} { initialize(std::forward(args)...); } @@ -195,7 +211,7 @@ public: */ template explicit basic_any(std::in_place_t, Type *value) - : instance{} { + : base_type{} { static_assert(!std::is_const_v && !std::is_void_v, "Non-const non-void pointer required"); if(value != nullptr) { @@ -229,18 +245,18 @@ public: * @param other The instance to move from. */ basic_any(basic_any &&other) noexcept - : instance{}, + : base_type{}, vtable{other.vtable}, descriptor{other.descriptor}, mode{other.mode} { if(other.mode == any_policy::embedded) { other.vtable(request::move, other, this); } else if(other.mode != any_policy::empty) { - instance = std::exchange(other.instance, nullptr); + this->instance = std::exchange(other.instance, nullptr); } } - /*! @brief Frees the internal storage, whatever it means. */ + /*! @brief Frees the internal buffer, whatever it means. */ ~basic_any() { destroy_if_owner(); } @@ -274,7 +290,7 @@ public: if(other.mode == any_policy::embedded) { other.vtable(request::move, other, this); } else if(other.mode != any_policy::empty) { - instance = std::exchange(other.instance, nullptr); + this->instance = std::exchange(other.instance, nullptr); } vtable = other.vtable; @@ -349,7 +365,7 @@ public: * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data() const noexcept { - return (mode == any_policy::embedded) ? vtable(request::get, *this, nullptr) : instance; + return (mode == any_policy::embedded) ? vtable(request::get, *this, nullptr) : this->instance; } /** @@ -445,7 +461,7 @@ public: void reset() { destroy_if_owner(); - instance = nullptr; + this->instance = nullptr; vtable = nullptr; descriptor = &type_id; mode = any_policy::empty; @@ -511,10 +527,6 @@ public: } private: - union { - const void *instance; - storage_type storage; - }; vtable_type *vtable{}; const type_info &(*descriptor)() noexcept {&type_id}; any_policy mode{any_policy::empty}; @@ -523,7 +535,7 @@ private: /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. - * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Len Size of the buffer reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param data Target any object. * @return The element converted to the requested type. @@ -581,7 +593,7 @@ template /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @tparam Type Type of object to use to initialize the wrapper. - * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Len Size of the buffer reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. @@ -594,7 +606,7 @@ template::length, std::size_t Align /** * @brief Forwards its argument and avoids copies for lvalue references. - * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Len Size of the buffer reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance.