diff --git a/src/entt/core/any.hpp b/src/entt/core/any.hpp index bde585d46..2618fe278 100644 --- a/src/entt/core/any.hpp +++ b/src/entt/core/any.hpp @@ -19,12 +19,16 @@ namespace entt { /** * @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 Align Optional alignment requirement. */ -template +template class basic_any { + static_assert(sizeof...(Align) == 0u || Len); + enum class operation { COPY, MOVE, DTOR, COMP, ADDR, CADDR, REF, CREF, TYPE }; - using storage_type = std::aligned_storage_t; + // cannot use std::aligned_storage_t with parameter packs because of an issue of msvc + using storage_type = typename std::aligned_storage::type; using vtable_type = const void *(const operation, const basic_any &, const void *); template @@ -337,12 +341,13 @@ private: /** * @brief Checks if two wrappers differ in their content. * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. * @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. */ -template -[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } @@ -351,11 +356,12 @@ template * @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 Align Optional alignment requirement. * @param data Target any object. * @return The element converted to the requested type. */ -template -Type any_cast(const basic_any &data) ENTT_NOEXCEPT { +template +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { const auto * const instance = any_cast>(&data); ENTT_ASSERT(instance); return static_cast(*instance); @@ -363,8 +369,8 @@ Type any_cast(const basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ -template -Type any_cast(basic_any &data) ENTT_NOEXCEPT { +template +Type any_cast(basic_any &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::remove_reference_t, const Type>>(&data); ENTT_ASSERT(instance); @@ -373,8 +379,8 @@ Type any_cast(basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ -template -Type any_cast(basic_any &&data) ENTT_NOEXCEPT { +template +Type any_cast(basic_any &&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::remove_reference_t, const Type>>(&data); ENTT_ASSERT(instance); @@ -383,17 +389,17 @@ Type any_cast(basic_any &&data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ -template -const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { +template +const Type * any_cast(const basic_any *data) ENTT_NOEXCEPT { return (data->type() == type_id() ? static_cast(data->data()) : nullptr); } /*! @copydoc any_cast */ -template -Type * any_cast(basic_any *data) ENTT_NOEXCEPT { +template +Type * any_cast(basic_any *data) ENTT_NOEXCEPT { // last attempt to make wrappers for const references return their values - return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); + return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); } diff --git a/src/entt/core/fwd.hpp b/src/entt/core/fwd.hpp index d1498288b..afbf4240a 100644 --- a/src/entt/core/fwd.hpp +++ b/src/entt/core/fwd.hpp @@ -8,7 +8,7 @@ namespace entt { -template +template class basic_any; @@ -17,7 +17,7 @@ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ -using any = basic_any<>; +using any = basic_any; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3284fa042..a39e7aae0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -58,7 +58,14 @@ function(SETUP_TARGET TARGET_NAME) ) endif() - target_compile_definitions(${TARGET_NAME} PRIVATE ENTT_STANDALONE ${ARGN}) + target_compile_definitions( + ${TARGET_NAME} + PRIVATE + _ENABLE_EXTENDED_ALIGNED_STORAGE + ENTT_STANDALONE + NOMINMAX + ${ARGN} + ) endfunction() add_library(odr OBJECT odr.cpp) @@ -80,8 +87,8 @@ endfunction() function(SETUP_PLUGIN_TEST TEST_NAME) add_library(_${TEST_NAME} MODULE $ lib/${TEST_NAME}/plugin.cpp) - SETUP_TARGET(_${TEST_NAME} NOMINMAX ${ARGVN}) - SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp NOMINMAX PLUGIN="$" ${ARGVN}) + SETUP_TARGET(_${TEST_NAME} ${ARGVN}) + SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp PLUGIN="$" ${ARGVN}) target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR}) target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR}) target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS}) diff --git a/test/entt/core/any.cpp b/test/entt/core/any.cpp index 8a9f6eadb..ab9179f94 100644 --- a/test/entt/core/any.cpp +++ b/test/entt/core/any.cpp @@ -887,17 +887,26 @@ TEST(Any, SBOVsZeroedSBOSize) { ASSERT_EQ(valid, same.data()); } -TEST(Any, NoSBOAlignment) { +TEST(Any, Alignment) { static constexpr auto alignment = alignof(over_aligned); - entt::basic_any target[2] = { over_aligned{}, over_aligned{} }; - const auto *data = target[0].data(); - ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[0u])) % alignment) == 0u); - ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[1u])) % alignment) == 0u); + auto test = [](auto *target, auto cb) { + const auto *data = target[0].data(); - std::swap(target[0], target[1]); + ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[0u])) % alignment) == 0u); + ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[1u])) % alignment) == 0u); - ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[0u])) % alignment) == 0u); - ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[1u])) % alignment) == 0u); - ASSERT_EQ(data, target[1].data()); + std::swap(target[0], target[1]); + + ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[0u])) % alignment) == 0u); + ASSERT_TRUE((reinterpret_cast(entt::any_cast(&target[1u])) % alignment) == 0u); + + cb(data, target[1].data()); + }; + + entt::basic_any nosbo[2] = { over_aligned{}, over_aligned{} }; + test(nosbo, [](auto *pre, auto *post) { ASSERT_EQ(pre, post); }); + + entt::basic_any sbo[2] = { over_aligned{}, over_aligned{} }; + test(sbo, [](auto *pre, auto *post) { ASSERT_NE(pre, post); }); }