diff --git a/single_include/entt/entt.hpp b/single_include/entt/entt.hpp index 8b5e0bc7f2..191659042e 100644 --- a/single_include/entt/entt.hpp +++ b/single_include/entt/entt.hpp @@ -18,8 +18,8 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -75,6 +75,8 @@ # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -122,8 +124,8 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -164,8 +166,8 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -221,6 +223,8 @@ # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -265,6 +269,7 @@ #include #include +#include #include #include // #include "../config/config.h" @@ -286,8 +291,8 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -343,6 +348,8 @@ # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -444,7 +451,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -686,7 +692,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -774,10 +781,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -786,6 +803,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -837,6 +906,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -957,7 +1109,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -1048,6 +1200,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -1144,6 +1300,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -1727,7 +1895,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -1748,7 +1916,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -1915,6 +2083,7 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #include #include +#include #include #include // #include "../config/config.h" @@ -1969,7 +2138,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -2211,7 +2379,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -2299,10 +2468,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -2311,6 +2490,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -2362,6 +2593,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -2482,7 +2796,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -2573,6 +2887,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -2669,6 +2987,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -2810,51 +3140,51 @@ class dense_map_iterator final { return {it->element.first, it->element.second}; } - template - friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -2912,13 +3242,13 @@ class dense_map_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -2948,14 +3278,14 @@ class dense_map { static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { - return fast_mod(sparse.second()(key), bucket_count()); + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template @@ -3146,7 +3476,6 @@ class dense_map { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -3167,11 +3496,6 @@ class dense_map { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -3520,7 +3844,7 @@ class dense_map { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); @@ -3845,51 +4169,51 @@ class dense_set_iterator final { return *operator->(); } - template - friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -3944,13 +4268,13 @@ class dense_set_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -3986,7 +4310,7 @@ class dense_set { template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { - return fast_mod(sparse.second()(value), bucket_count()); + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template @@ -4159,7 +4483,6 @@ class dense_set { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -4180,11 +4503,6 @@ class dense_set { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -4440,7 +4758,7 @@ class dense_set { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); @@ -4672,7 +4990,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -4705,7 +5023,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -4714,14 +5032,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -4736,13 +5054,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -4843,14 +5161,15 @@ struct radix_sort { template void operator()(It first, It last, Getter getter = Getter{}) const { if(first < last) { - static constexpr auto mask = (1 << Bit) - 1; - static constexpr auto buckets = 1 << Bit; - static constexpr auto passes = N / Bit; + constexpr auto passes = N / Bit; using value_type = typename std::iterator_traits::value_type; std::vector aux(std::distance(first, last)); auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + std::size_t index[buckets]{}; std::size_t count[buckets]{}; @@ -4911,8 +5230,8 @@ struct radix_sort { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -4968,6 +5287,8 @@ struct radix_sort { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -5018,7 +5339,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -5051,7 +5372,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -5060,14 +5381,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -5082,13 +5403,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -5238,7 +5559,7 @@ struct basic_hashed_string { template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; - using hs_traits = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -5250,10 +5571,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { - base_type base{str, 0u, hs_traits::offset}; + base_type base{str, 0u, traits_type::offset}; for(; str[base.length]; ++base.length) { - base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; } return base; @@ -5261,10 +5582,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { - base_type base{str, len, hs_traits::offset}; + base_type base{str, len, traits_type::offset}; for(size_type pos{}; pos < len; ++pos) { - base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; } return base; @@ -5775,6 +6096,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -5829,7 +6151,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -6071,7 +6392,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -6159,10 +6481,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -6171,6 +6503,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -6222,6 +6606,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -6342,7 +6809,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -6433,6 +6900,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -6529,6 +7000,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -6580,7 +7063,7 @@ class basic_any { }; template - static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { @@ -6649,17 +7132,17 @@ class basic_any { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { - static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); @@ -6949,7 +7432,7 @@ class basic_any { * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) noexcept { +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -6957,7 +7440,7 @@ Type any_cast(const basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) noexcept { +[[nodiscard]] Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); @@ -6966,7 +7449,7 @@ Type any_cast(basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) noexcept { +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); @@ -6982,14 +7465,14 @@ Type any_cast(basic_any &&data) noexcept { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) noexcept { +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template -Type *any_cast(basic_any *data) noexcept { +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); @@ -7009,7 +7492,7 @@ Type *any_cast(basic_any *data) noexcept { * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> -basic_any make_any(Args &&...args) { +[[nodiscard]] basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } @@ -7022,7 +7505,7 @@ basic_any make_any(Args &&...args) { * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> -basic_any forward_as_any(Type &&value) { +[[nodiscard]] basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } @@ -7550,7 +8033,7 @@ struct basic_hashed_string { template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; - using hs_traits = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -7562,10 +8045,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { - base_type base{str, 0u, hs_traits::offset}; + base_type base{str, 0u, traits_type::offset}; for(; str[base.length]; ++base.length) { - base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; } return base; @@ -7573,10 +8056,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { - base_type base{str, len, hs_traits::offset}; + base_type base{str, len, traits_type::offset}; for(size_type pos{}; pos < len; ++pos) { - base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; } return base; @@ -8164,7 +8647,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -8185,7 +8668,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -8478,7 +8961,7 @@ struct forward_apply: private Func { * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ - template + template constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v) : Func{std::forward(args)...} {} @@ -8488,13 +8971,13 @@ struct forward_apply: private Func { * @param args Parameters to forward to the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval(), args))) { return std::apply(static_cast(*this), std::forward(args)); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval(), args))) { return std::apply(static_cast(*this), std::forward(args)); } @@ -8797,6 +9280,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -8851,7 +9335,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -9093,7 +9576,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -9181,10 +9665,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -9193,6 +9687,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -9244,6 +9790,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -9364,7 +9993,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -9455,6 +10084,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -9551,6 +10184,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "core/utility.hpp" @@ -9573,7 +10218,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -9606,7 +10251,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -9615,14 +10260,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -9637,13 +10282,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -9681,8 +10326,8 @@ struct y_combinator { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -9738,6 +10383,8 @@ struct y_combinator { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -9768,78 +10415,11 @@ struct y_combinator { #endif - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; - -template -struct in_place_delete> - : std::true_type {}; - -template -struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; - -template -struct page_size>> - : std::integral_constant {}; - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Common way to access various properties of components. - * @tparam Type Type of component. - */ -template -struct component_traits { - static_assert(std::is_same_v, Type>, "Unsupported type"); - - /*! @brief Component type. */ - using type = Type; - - /*! @brief Pointer stability, default is `false`. */ - static constexpr bool in_place_delete = internal::in_place_delete::value; - /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ - static constexpr std::size_t page_size = internal::page_size::value; -}; - -/** - * @brief Helper variable template. - * @tparam Type Type of component. - */ -template -inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_traits::page_size == 0u); - -} // namespace entt - -#endif - -// #include "entity/entity.hpp" -#ifndef ENTT_ENTITY_ENTITY_HPP -#define ENTT_ENTITY_ENTITY_HPP - -#include -#include -#include -// #include "../config/config.h" - // #include "fwd.hpp" #ifndef ENTT_ENTITY_FWD_HPP #define ENTT_ENTITY_FWD_HPP +#include #include #include // #include "../core/fwd.hpp" @@ -9866,8 +10446,8 @@ inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_tra #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -9923,6 +10503,8 @@ inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_tra # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -9975,6 +10557,7 @@ using any = basic_any<>; #include #include +#include #include #include // #include "../config/config.h" @@ -10050,7 +10633,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -10292,7 +10874,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -10380,10 +10963,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -10392,6 +10985,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -10443,6 +11088,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -10563,7 +11291,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -10654,6 +11382,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -10750,6 +11482,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -10758,20 +11502,60 @@ namespace entt { /*! @brief Default entity identifier. */ enum class entity : id_type {}; +/*! @brief Storage deletion policy. */ +enum class deletion_policy : std::uint8_t { + /*! @brief Swap-and-pop deletion policy. */ + swap_and_pop = 0u, + /*! @brief In-place deletion policy. */ + in_place = 1u +}; + template> class basic_sparse_set; template, typename = void> class basic_storage; -template, typename = void> -struct storage_type; +template +class sigh_mixin; -template>> -struct storage_for; +/** + * @brief Provides a common way to define storage types. + * @tparam Type Storage value type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template, typename = void> +struct storage_type { + /*! @brief Type-to-storage conversion result. */ + using type = sigh_mixin>; +}; -template -class sigh_storage_mixin; +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_type_t = typename storage_type::type; + +/** + * Type-to-storage conversion utility that preserves constness. + * @tparam Type Storage value type, eventually const. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template>> +struct storage_for { + /*! @brief Type-to-storage conversion result. */ + using type = constness_as_t, Entity, Allocator>, Type>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_for_t = typename storage_for::type; template> class basic_registry; @@ -10785,7 +11569,7 @@ class basic_runtime_view; template class basic_group; -template +template> class basic_observer; template @@ -10808,7 +11592,10 @@ class basic_continuous_loader; * @tparam Type List of types. */ template -using exclude_t = type_list; +struct exclude_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr exclude_t() {} +}; /** * @brief Variable template for exclusion lists. @@ -10822,7 +11609,10 @@ inline constexpr exclude_t exclude{}; * @tparam Type List of types. */ template -using get_t = type_list; +struct get_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr get_t() {} +}; /** * @brief Variable template for lists of observed components. @@ -10836,7 +11626,10 @@ inline constexpr get_t get{}; * @tparam Type List of types. */ template -using owned_t = type_list; +struct owned_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr owned_t() {} +}; /** * @brief Variable template for lists of owned components. @@ -10845,6 +11638,39 @@ using owned_t = type_list; template inline constexpr owned_t owned{}; +/** + * @brief Applies a given _function_ to a get list and generate a new list. + * @tparam Type Types provided by the get list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting get list after applying the transform function. */ + using type = get_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an exclude list and generate a new list. + * @tparam Type Types provided by the exclude list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting exclude list after applying the transform function. */ + using type = exclude_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an owned list and generate a new list. + * @tparam Type Types provided by the owned list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting owned list after applying the transform function. */ + using type = owned_t::type...>; +}; + /*! @brief Alias declaration for the most common use case. */ using sparse_set = basic_sparse_set<>; @@ -10930,35 +11756,116 @@ namespace entt { namespace internal { +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: std::integral_constant {}; + +template +struct page_size>> + : std::integral_constant {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Component type. */ + using type = Type; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; +}; + +} // namespace entt + +#endif + +// #include "entity/entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +// waiting for C++20 (and std::popcount) +template +static constexpr int popcount(Type value) noexcept { + return value ? (int(value & 1) + popcount(value >> 1)) : 0; +} + template struct entt_traits; template struct entt_traits>> - : entt_traits> {}; + : entt_traits> { + using value_type = Type; +}; template struct entt_traits>> - : entt_traits {}; + : entt_traits { + using value_type = Type; +}; template<> struct entt_traits { + using value_type = std::uint32_t; + using entity_type = std::uint32_t; using version_type = std::uint16_t; static constexpr entity_type entity_mask = 0xFFFFF; static constexpr entity_type version_mask = 0xFFF; - static constexpr std::size_t entity_shift = 20u; }; template<> struct entt_traits { + using value_type = std::uint64_t; + using entity_type = std::uint64_t; using version_type = std::uint32_t; static constexpr entity_type entity_mask = 0xFFFFFFFF; static constexpr entity_type version_mask = 0xFFFFFFFF; - static constexpr std::size_t entity_shift = 32u; }; } // namespace internal @@ -10969,24 +11876,28 @@ struct entt_traits { */ /** - * @brief Entity traits. - * @tparam Type Type of identifier. + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. */ -template -class entt_traits: internal::entt_traits { - using base_type = internal::entt_traits; +template +class basic_entt_traits { + static constexpr auto length = internal::popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask"); + static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask"); public: /*! @brief Value type. */ - using value_type = Type; + using value_type = typename Traits::value_type; /*! @brief Underlying entity type. */ - using entity_type = typename base_type::entity_type; + using entity_type = typename Traits::entity_type; /*! @brief Underlying version type. */ - using version_type = typename base_type::version_type; - /*! @brief Reserved identifier. */ - static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift); - /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ - static constexpr auto page_size = ENTT_SPARSE_PAGE; + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; /** * @brief Converts an entity to its underlying type. @@ -11003,7 +11914,7 @@ class entt_traits: internal::entt_traits { * @return The integral representation of the entity part. */ [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { - return (to_integral(value) & base_type::entity_mask); + return (to_integral(value) & entity_mask); } /** @@ -11012,7 +11923,17 @@ class entt_traits: internal::entt_traits { * @return The integral representation of the version part. */ [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { - return (to_integral(value) >> base_type::entity_shift); + return static_cast(to_integral(value) >> length); + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_entity(value), static_cast(vers + (vers == version_mask))); } /** @@ -11026,7 +11947,7 @@ class entt_traits: internal::entt_traits { * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { - return value_type{(entity & base_type::entity_mask) | (static_cast(version) << base_type::entity_shift)}; + return value_type{(entity & entity_mask) | (static_cast(version) << length)}; } /** @@ -11040,11 +11961,23 @@ class entt_traits: internal::entt_traits { * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { - constexpr auto mask = (base_type::version_mask << base_type::entity_shift); - return value_type{(lhs & base_type::entity_mask) | (rhs & mask)}; + constexpr auto mask = (version_mask << length); + return value_type{(lhs & entity_mask) | (rhs & mask)}; } }; +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + /** * @copydoc entt_traits::to_integral * @tparam Entity The value type. @@ -11081,8 +12014,9 @@ struct null_t { */ template [[nodiscard]] constexpr operator Entity() const noexcept { - using entity_traits = entt_traits; - return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; } /** @@ -11111,8 +12045,8 @@ struct null_t { */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { - using entity_traits = entt_traits; - return entity_traits::to_entity(entity) == entity_traits::to_entity(*this); + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); } /** @@ -11160,8 +12094,9 @@ struct tombstone_t { */ template [[nodiscard]] constexpr operator Entity() const noexcept { - using entity_traits = entt_traits; - return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; } /** @@ -11190,8 +12125,8 @@ struct tombstone_t { */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { - using entity_traits = entt_traits; - return entity_traits::to_version(entity) == entity_traits::to_version(*this); + using traits_type = entt_traits; + return traits_type::to_version(entity) == traits_type::to_version(*this); } /** @@ -11261,6 +12196,8 @@ inline constexpr tombstone_t tombstone{}; #include // #include "../config/config.h" +// #include "../core/fwd.hpp" + // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP @@ -11460,84 +12397,55 @@ struct iterable_adaptor final { #endif -// #include "../core/type_traits.hpp" - -// #include "component.hpp" -#ifndef ENTT_ENTITY_COMPONENT_HPP -#define ENTT_ENTITY_COMPONENT_HPP +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP -#include +#include #include +#include // #include "../config/config.h" +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; - -template -struct in_place_delete> - : std::true_type {}; - -template -struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; - -template -struct page_size>> - : std::integral_constant {}; - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Common way to access various properties of components. - * @tparam Type Type of component. - */ -template -struct component_traits { - static_assert(std::is_same_v, Type>, "Unsupported type"); - - /*! @brief Component type. */ - using type = Type; - - /*! @brief Pointer stability, default is `false`. */ - static constexpr bool in_place_delete = internal::in_place_delete::value; - /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ - static constexpr std::size_t page_size = internal::page_size::value; -}; - -/** - * @brief Helper variable template. - * @tparam Type Type of component. - */ -template -inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_traits::page_size == 0u); +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif -} // namespace entt +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif #endif -// #include "entity.hpp" -#ifndef ENTT_ENTITY_ENTITY_HPP -#define ENTT_ENTITY_ENTITY_HPP +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP #include #include -#include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -11550,35 +12458,32 @@ namespace entt { namespace internal { -template -struct entt_traits; - -template -struct entt_traits>> - : entt_traits> {}; - -template -struct entt_traits>> - : entt_traits {}; +template +struct fnv1a_traits; template<> -struct entt_traits { - using entity_type = std::uint32_t; - using version_type = std::uint16_t; - - static constexpr entity_type entity_mask = 0xFFFFF; - static constexpr entity_type version_mask = 0xFFF; - static constexpr std::size_t entity_shift = 20u; +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; }; template<> -struct entt_traits { - using entity_type = std::uint64_t; - using version_type = std::uint32_t; +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; - static constexpr entity_type entity_mask = 0xFFFFFFFF; - static constexpr entity_type version_mask = 0xFFFFFFFF; - static constexpr std::size_t entity_shift = 32u; +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr; + size_type length; + hash_type hash; }; } // namespace internal @@ -11589,555 +12494,963 @@ struct entt_traits { */ /** - * @brief Entity traits. - * @tparam Type Type of identifier. + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. */ -template -class entt_traits: internal::entt_traits { - using base_type = internal::entt_traits; +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } public: - /*! @brief Value type. */ - using value_type = Type; - /*! @brief Underlying entity type. */ - using entity_type = typename base_type::entity_type; - /*! @brief Underlying version type. */ - using version_type = typename base_type::version_type; - /*! @brief Reserved identifier. */ - static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift); - /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ - static constexpr auto page_size = ENTT_SPARSE_PAGE; + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; /** - * @brief Converts an entity to its underlying type. - * @param value The value to convert. - * @return The integral representation of the given value. + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. */ - [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { - return static_cast(value); + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; } /** - * @brief Returns the entity part once converted to the underlying type. - * @param value The value to convert. - * @return The integral representation of the entity part. + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. */ - [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { - return (to_integral(value) & base_type::entity_mask); + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; } /** - * @brief Returns the version part once converted to the underlying type. - * @param value The value to convert. - * @return The integral representation of the version part. + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. */ - [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { - return (to_integral(value) >> base_type::entity_shift); + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; } + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + /** - * @brief Constructs an identifier from its parts. + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. * - * If the version part is not provided, a tombstone is returned.
- * If the entity part is not provided, a null identifier is returned. + * @warning + * The lifetime of the string is not extended nor is it copied. * - * @param entity The entity part of the identifier. - * @param version The version part of the identifier. - * @return A properly constructed identifier. + * @param wrapper Helps achieving the purpose by relying on overloading. */ - [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { - return value_type{(entity & base_type::entity_mask) | (static_cast(version) << base_type::entity_shift)}; + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; } /** - * @brief Combines two identifiers in a single one. - * - * The returned identifier is a copy of the first element except for its - * version, which is taken from the second element. - * - * @param lhs The identifier from which to take the entity part. - * @param rhs The identifier from which to take the version part. - * @return A properly constructed identifier. + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. */ - [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { - constexpr auto mask = (base_type::version_mask << base_type::entity_shift); - return value_type{(lhs & base_type::entity_mask) | (rhs & mask)}; + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; } -}; -/** - * @copydoc entt_traits::to_integral - * @tparam Entity The value type. - */ -template -[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { - return entt_traits::to_integral(value); -} - -/** - * @copydoc entt_traits::to_entity - * @tparam Entity The value type. - */ -template -[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { - return entt_traits::to_entity(value); -} - -/** - * @copydoc entt_traits::to_version - * @tparam Entity The value type. - */ -template -[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { - return entt_traits::to_version(value); -} - -/*! @brief Null object for all identifiers. */ -struct null_t { /** - * @brief Converts the null object to identifiers of any type. - * @tparam Entity Type of identifier. - * @return The null representation for the given type. + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. */ - template - [[nodiscard]] constexpr operator Entity() const noexcept { - using entity_traits = entt_traits; - return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; } - /** - * @brief Compares two null objects. - * @param other A null object. - * @return True in all cases. - */ - [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { - return true; + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); } /** - * @brief Compares two null objects. - * @param other A null object. - * @return False in all cases. + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { - return false; + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); } +}; - /** - * @brief Compares a null object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @return False if the two elements differ, true otherwise. - */ - template - [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { - using entity_traits = entt_traits; - return entity_traits::to_entity(entity) == entity_traits::to_entity(*this); - } +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; - /** - * @brief Compares a null object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @return True if the two elements differ, false otherwise. - */ - template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { - return !(entity == *this); - } -}; +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; /** - * @brief Compares a null object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @param other A null object yet to be converted. - * @return False if the two elements differ, true otherwise. + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. */ -template -[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { - return other.operator==(entity); +template +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); } /** - * @brief Compares a null object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @param other A null object yet to be converted. - * @return True if the two elements differ, false otherwise. + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. */ -template -[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { - return !(other == entity); +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); } -/*! @brief Tombstone object for all identifiers. */ -struct tombstone_t { - /** - * @brief Converts the tombstone object to identifiers of any type. - * @tparam Entity Type of identifier. - * @return The tombstone representation for the given type. - */ - template - [[nodiscard]] constexpr operator Entity() const noexcept { - using entity_traits = entt_traits; - return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); - } - - /** - * @brief Compares two tombstone objects. - * @param other A tombstone object. - * @return True in all cases. - */ - [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { - return true; - } - - /** - * @brief Compares two tombstone objects. - * @param other A tombstone object. - * @return False in all cases. - */ - [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { - return false; - } - - /** - * @brief Compares a tombstone object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @return False if the two elements differ, true otherwise. - */ - template - [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { - using entity_traits = entt_traits; - return entity_traits::to_version(entity) == entity_traits::to_version(*this); - } - - /** - * @brief Compares a tombstone object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @return True if the two elements differ, false otherwise. - */ - template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { - return !(entity == *this); - } -}; - /** - * @brief Compares a tombstone object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @param other A tombstone object yet to be converted. - * @return False if the two elements differ, true otherwise. + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. */ -template -[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { - return other.operator==(entity); +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); } /** - * @brief Compares a tombstone object and an identifier of any type. - * @tparam Entity Type of identifier. - * @param entity Identifier with which to compare. - * @param other A tombstone object yet to be converted. - * @return True if the two elements differ, false otherwise. + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. */ -template -[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { - return !(other == entity); +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); } /** - * @brief Compile-time constant for null entities. - * - * There exist implicit conversions from this variable to identifiers of any - * allowed type. Similarly, there exist comparison operators between the null - * entity and any other identifier. + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. */ -inline constexpr null_t null{}; +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} /** - * @brief Compile-time constant for tombstone entities. - * - * There exist implicit conversions from this variable to identifiers of any - * allowed type. Similarly, there exist comparison operators between the - * tombstone entity and any other identifier. + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. */ -inline constexpr tombstone_t tombstone{}; +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} -} // namespace entt +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; -#endif +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; -// #include "fwd.hpp" +inline namespace literals { -// #include "sparse_set.hpp" -#ifndef ENTT_ENTITY_SPARSE_SET_HPP -#define ENTT_ENTITY_SPARSE_SET_HPP +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} -// #include "../core/algorithm.hpp" -#ifndef ENTT_CORE_ALGORITHM_HPP -#define ENTT_CORE_ALGORITHM_HPP +} // namespace literals -#include -#include -#include -#include -#include -// #include "utility.hpp" -#ifndef ENTT_CORE_UTILITY_HPP -#define ENTT_CORE_UTILITY_HPP +} // namespace entt + +#endif -#include -#include namespace entt { -/*! @brief Identity function object (waiting for C++20). */ -struct identity { - /*! @brief Indicates that this is a transparent function object. */ - using is_transparent = void; +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ - /** - * @brief Returns its argument unchanged. - * @tparam Type Type of the argument. - * @param value The actual argument. - * @return The submitted value as-is. - */ - template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { - return std::forward(value); +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; } }; -/** - * @brief Constant utility to disambiguate overloaded members of a class. - * @tparam Type Type of the desired overload. - * @tparam Class Type of class to which the member belongs. - * @param member A valid pointer to a member. - * @return Pointer to the member. - */ -template -[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { - return member; +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif } -/** - * @brief Constant utility to disambiguate overloaded functions. - * @tparam Func Function type of the desired overload. - * @param func A valid pointer to a function. - * @return Pointer to the function. - */ -template -[[nodiscard]] constexpr auto overload(Func *func) noexcept { - return func; +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; } -/** - * @brief Helper type for visitors. - * @tparam Func Types of function objects. - */ -template -struct overloaded: Func... { - using Func::operator()...; -}; +template +[[nodiscard]] static std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal /** - * @brief Deduction guide. - * @tparam Func Types of function objects. + * Internal details not to be documented. + * @endcond */ -template -overloaded(Func...) -> overloaded; /** - * @brief Basic implementation of a y-combinator. - * @tparam Func Type of a potentially recursive function. + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. */ -template -struct y_combinator { - /** - * @brief Constructs a y-combinator from a given function. - * @param recursive A potentially recursive function. - */ - constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) - : func{std::move(recursive)} {} - +template +struct ENTT_API type_index final { /** - * @brief Invokes a y-combinator and therefore its underlying function. - * @tparam Args Types of arguments to use to invoke the underlying function. - * @param args Parameters to use to invoke the underlying function. - * @return Return value of the underlying function, if any. + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. */ - template - constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { - return func(*this, std::forward(args)...); + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; } - /*! @copydoc operator()() */ - template - constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { - return func(*this, std::forward(args)...); + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); } - -private: - Func func; }; -} // namespace entt - +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); #endif + } - -namespace entt { + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; /** - * @brief Function object to wrap `std::sort` in a class type. - * - * Unfortunately, `std::sort` cannot be passed as template argument to a class - * template or a function template.
- * This class fills the gap by wrapping some flavors of `std::sort` in a - * function object. + * @brief Type name. + * @tparam Type Type for which to generate a name. */ -struct std_sort { +template +struct type_name final { /** - * @brief Sorts the elements in a range. - * - * Sorts the elements in a range using the given binary comparison function. - * - * @tparam It Type of random access iterator. - * @tparam Compare Type of comparison function object. - * @tparam Args Types of arguments to forward to the sort function. - * @param first An iterator to the first element of the range to sort. - * @param last An iterator past the last element of the range to sort. - * @param compare A valid comparison function object. - * @param args Arguments to forward to the sort function, if any. + * @brief Returns the name of a given type. + * @return The name of the given type. */ - template, typename... Args> - void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { - std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); } }; -/*! @brief Function object for performing insertion sort. */ -struct insertion_sort { +/*! @brief Implementation specific information about a type. */ +struct type_info final { /** - * @brief Sorts the elements in a range. - * - * Sorts the elements in a range using the given binary comparison function. - * - * @tparam It Type of random access iterator. - * @tparam Compare Type of comparison function object. - * @param first An iterator to the first element of the range to sort. - * @param last An iterator past the last element of the range to sort. - * @param compare A valid comparison function object. + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. */ - template> - void operator()(It first, It last, Compare compare = Compare{}) const { - if(first < last) { - for(auto it = first + 1; it < last; ++it) { - auto value = std::move(*it); - auto pre = it; - - for(; pre > first && compare(value, *(pre - 1)); --pre) { - *pre = std::move(*(pre - 1)); - } + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} - *pre = std::move(value); - } - } + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; } -}; -/** - * @brief Function object for performing LSD radix sort. - * @tparam Bit Number of bits processed per pass. - * @tparam N Maximum number of bits to sort. + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. */ -template -struct radix_sort { - static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +// waiting for C++20 (and std::popcount) +template +static constexpr int popcount(Type value) noexcept { + return value ? (int(value & 1) + popcount(value >> 1)) : 0; +} + +template +struct entt_traits; + +template +struct entt_traits>> + : entt_traits> { + using value_type = Type; +}; + +template +struct entt_traits>> + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = std::uint32_t; + + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + + static constexpr entity_type entity_mask = 0xFFFFF; + static constexpr entity_type version_mask = 0xFFF; +}; + +template<> +struct entt_traits { + using value_type = std::uint64_t; + + using entity_type = std::uint64_t; + using version_type = std::uint32_t; + + static constexpr entity_type entity_mask = 0xFFFFFFFF; + static constexpr entity_type version_mask = 0xFFFFFFFF; +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = internal::popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask"); + static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; /** - * @brief Sorts the elements in a range. + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + return static_cast(to_integral(value) >> length); + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_entity(value), static_cast(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. * - * Sorts the elements in a range using the given _getter_ to access the - * actual data to be sorted. + * If the version part is not provided, a tombstone is returned.
+ * If the entity part is not provided, a null identifier is returned. * - * This implementation is inspired by the online book - * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + return value_type{(entity & entity_mask) | (static_cast(version) << length)}; + } + + /** + * @brief Combines two identifiers in a single one. * - * @tparam It Type of random access iterator. - * @tparam Getter Type of _getter_ function object. - * @param first An iterator to the first element of the range to sort. - * @param last An iterator past the last element of the range to sort. - * @param getter A valid _getter_ function object. + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. */ - template - void operator()(It first, It last, Getter getter = Getter{}) const { - if(first < last) { - static constexpr auto mask = (1 << Bit) - 1; - static constexpr auto buckets = 1 << Bit; - static constexpr auto passes = N / Bit; + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + constexpr auto mask = (version_mask << length); + return value_type{(lhs & entity_mask) | (rhs & mask)}; + } +}; - using value_type = typename std::iterator_traits::value_type; - std::vector aux(std::distance(first, last)); +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; - auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { - std::size_t index[buckets]{}; - std::size_t count[buckets]{}; +/** + * @copydoc entt_traits::to_integral + * @tparam Entity The value type. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { + return entt_traits::to_integral(value); +} - for(auto it = from; it != to; ++it) { - ++count[(getter(*it) >> start) & mask]; - } +/** + * @copydoc entt_traits::to_entity + * @tparam Entity The value type. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { + return entt_traits::to_entity(value); +} - for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { - index[pos + 1u] = index[pos] + count[pos]; - } +/** + * @copydoc entt_traits::to_version + * @tparam Entity The value type. + */ +template +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { + return entt_traits::to_version(value); +} - for(auto it = from; it != to; ++it) { - out[index[(getter(*it) >> start) & mask]++] = std::move(*it); - } - }; +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } - for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { - part(first, last, aux.begin(), pass * Bit); - part(aux.begin(), aux.end(), first, (pass + 1) * Bit); - } + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } - if constexpr(passes & 1) { - part(first, last, aux.begin(), (passes - 1) * Bit); - std::move(aux.begin(), aux.end(), first); - } - } + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { + return !(other == entity); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_version(entity) == traits_type::to_version(*this); + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); } }; +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { + return !(other == entity); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + } // namespace entt #endif -// #include "../core/any.hpp" -#ifndef ENTT_CORE_ANY_HPP -#define ENTT_CORE_ANY_HPP +// #include "fwd.hpp" + +// #include "sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP #include +#include #include #include #include +#include // #include "../config/config.h" -// #include "../core/utility.hpp" +// #include "../core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + +#include +#include +#include +#include +#include +// #include "utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP @@ -12157,7 +13470,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -12190,7 +13503,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -12199,14 +13512,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -12221,13 +13534,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -12240,387 +13553,265 @@ struct y_combinator { #endif -// #include "fwd.hpp" -// #include "type_info.hpp" -#ifndef ENTT_CORE_TYPE_INFO_HPP -#define ENTT_CORE_TYPE_INFO_HPP - -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/attribute.h" -#ifndef ENTT_CORE_ATTRIBUTE_H -#define ENTT_CORE_ATTRIBUTE_H - -#ifndef ENTT_EXPORT -# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER -# define ENTT_EXPORT __declspec(dllexport) -# define ENTT_IMPORT __declspec(dllimport) -# define ENTT_HIDDEN -# elif defined __GNUC__ && __GNUC__ >= 4 -# define ENTT_EXPORT __attribute__((visibility("default"))) -# define ENTT_IMPORT __attribute__((visibility("default"))) -# define ENTT_HIDDEN __attribute__((visibility("hidden"))) -# else /* Unsupported compiler */ -# define ENTT_EXPORT -# define ENTT_IMPORT -# define ENTT_HIDDEN -# endif -#endif - -#ifndef ENTT_API -# if defined ENTT_API_EXPORT -# define ENTT_API ENTT_EXPORT -# elif defined ENTT_API_IMPORT -# define ENTT_API ENTT_IMPORT -# else /* No API */ -# define ENTT_API -# endif -#endif - -#endif - -// #include "fwd.hpp" - -// #include "hashed_string.hpp" -#ifndef ENTT_CORE_HASHED_STRING_HPP -#define ENTT_CORE_HASHED_STRING_HPP - -#include -#include -// #include "fwd.hpp" - - -namespace entt { +namespace entt { /** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. */ - -namespace internal { - -template -struct fnv1a_traits; - -template<> -struct fnv1a_traits { - using type = std::uint32_t; - static constexpr std::uint32_t offset = 2166136261; - static constexpr std::uint32_t prime = 16777619; +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } }; -template<> -struct fnv1a_traits { - using type = std::uint64_t; - static constexpr std::uint64_t offset = 14695981039346656037ull; - static constexpr std::uint64_t prime = 1099511628211ull; -}; +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first + 1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; -template -struct basic_hashed_string { - using value_type = Char; - using size_type = std::size_t; - using hash_type = id_type; + for(; pre > first && compare(value, *(pre - 1)); --pre) { + *pre = std::move(*(pre - 1)); + } - const value_type *repr; - size_type length; - hash_type hash; + *pre = std::move(value); + } + } + } }; -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - /** - * @brief Zero overhead unique identifier. - * - * A hashed string is a compile-time tool that allows users to use - * human-readable identifiers in the codebase while using their numeric - * counterparts at runtime.
- * Because of that, a hashed string can also be used in constant expressions if - * required. - * - * @warning - * This class doesn't take ownership of user-supplied strings nor does it make a - * copy of them. - * - * @tparam Char Character type. + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. */ -template -class basic_hashed_string: internal::basic_hashed_string { - using base_type = internal::basic_hashed_string; - using hs_traits = internal::fnv1a_traits; - - struct const_wrapper { - // non-explicit constructor on purpose - constexpr const_wrapper(const Char *str) noexcept - : repr{str} {} - - const Char *repr; - }; - - // Fowler–Noll–Vo hash function v. 1a - the good - [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { - base_type base{str, 0u, hs_traits::offset}; +template +struct radix_sort { + static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); - for(; str[base.length]; ++base.length) { - base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; - } + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + constexpr auto passes = N / Bit; - return base; - } + using value_type = typename std::iterator_traits::value_type; + std::vector aux(std::distance(first, last)); - // Fowler–Noll–Vo hash function v. 1a - the good - [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { - base_type base{str, len, hs_traits::offset}; + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; - for(size_type pos{}; pos < len; ++pos) { - base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; - } + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; - return base; - } + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } -public: - /*! @brief Character type. */ - using value_type = typename base_type::value_type; - /*! @brief Unsigned integer type. */ - using size_type = typename base_type::size_type; - /*! @brief Unsigned integer type. */ - using hash_type = typename base_type::hash_type; + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } - /** - * @brief Returns directly the numeric representation of a string view. - * @param str Human-readable identifier. - * @param len Length of the string to hash. - * @return The numeric representation of the string. - */ - [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { - return basic_hashed_string{str, len}; - } + for(auto it = from; it != to; ++it) { + out[index[(getter(*it) >> start) & mask]++] = std::move(*it); + } + }; - /** - * @brief Returns directly the numeric representation of a string. - * @tparam N Number of characters of the identifier. - * @param str Human-readable identifier. - * @return The numeric representation of the string. - */ - template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { - return basic_hashed_string{str}; - } + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } - /** - * @brief Returns directly the numeric representation of a string. - * @param wrapper Helps achieving the purpose by relying on overloading. - * @return The numeric representation of the string. - */ - [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { - return basic_hashed_string{wrapper}; + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } } +}; - /*! @brief Constructs an empty hashed string. */ - constexpr basic_hashed_string() noexcept - : base_type{} {} +} // namespace entt - /** - * @brief Constructs a hashed string from a string view. - * @param str Human-readable identifier. - * @param len Length of the string to hash. - */ - constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept - : base_type{helper(str, len)} {} +#endif - /** - * @brief Constructs a hashed string from an array of const characters. - * @tparam N Number of characters of the identifier. - * @param str Human-readable identifier. - */ - template - constexpr basic_hashed_string(const value_type (&str)[N]) noexcept - : base_type{helper(str)} {} +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP - /** - * @brief Explicit constructor on purpose to avoid constructing a hashed - * string directly from a `const value_type *`. - * - * @warning - * The lifetime of the string is not extended nor is it copied. - * - * @param wrapper Helps achieving the purpose by relying on overloading. - */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept - : base_type{helper(wrapper.repr)} {} +#include +#include +#include +#include +// #include "../config/config.h" - /** - * @brief Returns the size a hashed string. - * @return The size of the hashed string. - */ - [[nodiscard]] constexpr size_type size() const noexcept { - return base_type::length; - } +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP - /** - * @brief Returns the human-readable representation of a hashed string. - * @return The string used to initialize the hashed string. - */ - [[nodiscard]] constexpr const value_type *data() const noexcept { - return base_type::repr; - } +#include +#include - /** - * @brief Returns the numeric representation of a hashed string. - * @return The numeric representation of the hashed string. - */ - [[nodiscard]] constexpr hash_type value() const noexcept { - return base_type::hash; - } +namespace entt { - /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const noexcept { - return data(); - } +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; /** - * @brief Returns the numeric representation of a hashed string. - * @return The numeric representation of the hashed string. + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. */ - [[nodiscard]] constexpr operator hash_type() const noexcept { - return value(); + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); } }; /** - * @brief Deduction guide. - * @tparam Char Character type. - * @param str Human-readable identifier. - * @param len Length of the string to hash. - */ -template -basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; - -/** - * @brief Deduction guide. - * @tparam Char Character type. - * @tparam N Number of characters of the identifier. - * @param str Human-readable identifier. - */ -template -basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; - -/** - * @brief Compares two hashed strings. - * @tparam Char Character type. - * @param lhs A valid hashed string. - * @param rhs A valid hashed string. - * @return True if the two hashed strings are identical, false otherwise. + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. */ -template -[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { - return lhs.value() == rhs.value(); +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; } /** - * @brief Compares two hashed strings. - * @tparam Char Character type. - * @param lhs A valid hashed string. - * @param rhs A valid hashed string. - * @return True if the two hashed strings differ, false otherwise. + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. */ -template -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { - return !(lhs == rhs); +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; } /** - * @brief Compares two hashed strings. - * @tparam Char Character type. - * @param lhs A valid hashed string. - * @param rhs A valid hashed string. - * @return True if the first element is less than the second, false otherwise. + * @brief Helper type for visitors. + * @tparam Func Types of function objects. */ -template -[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { - return lhs.value() < rhs.value(); -} +template +struct overloaded: Func... { + using Func::operator()...; +}; /** - * @brief Compares two hashed strings. - * @tparam Char Character type. - * @param lhs A valid hashed string. - * @param rhs A valid hashed string. - * @return True if the first element is less than or equal to the second, false - * otherwise. + * @brief Deduction guide. + * @tparam Func Types of function objects. */ -template -[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { - return !(rhs < lhs); -} +template +overloaded(Func...) -> overloaded; /** - * @brief Compares two hashed strings. - * @tparam Char Character type. - * @param lhs A valid hashed string. - * @param rhs A valid hashed string. - * @return True if the first element is greater than the second, false - * otherwise. + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. */ -template -[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { - return rhs < lhs; -} +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} -/** - * @brief Compares two hashed strings. - * @tparam Char Character type. - * @param lhs A valid hashed string. - * @param rhs A valid hashed string. - * @return True if the first element is greater than or equal to the second, - * false otherwise. - */ -template -[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { - return !(lhs < rhs); -} + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } -/*! @brief Aliases for common character types. */ -using hashed_string = basic_hashed_string; + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } -/*! @brief Aliases for common character types. */ -using hashed_wstring = basic_hashed_string; +private: + Func func; +}; -inline namespace literals { +} // namespace entt -/** - * @brief User defined literal for hashed strings. - * @param str The literal without its suffix. - * @return A properly initialized hashed string. - */ -[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { - return hashed_string{str}; -} +#endif -/** - * @brief User defined literal for hashed wstrings. - * @param str The literal without its suffix. - * @return A properly initialized hashed wstring. - */ -[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { - return hashed_wstring{str}; -} +// #include "fwd.hpp" -} // namespace literals +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP -} // namespace entt +#include +#include +#include +// #include "../config/config.h" -#endif +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" namespace entt { @@ -12893,6 +14084,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -12947,7 +14139,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -13189,7 +14380,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -13277,10 +14469,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -13289,8 +14491,60 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + /** - * @brief Concatenates multiple value lists. + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + +/** + * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. @@ -13340,6 +14594,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -13460,7 +14797,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -13551,6 +14888,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -13647,6 +14988,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -13698,7 +15051,7 @@ class basic_any { }; template - static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { @@ -13767,17 +15120,17 @@ class basic_any { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { - static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); @@ -14067,7 +15420,7 @@ class basic_any { * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) noexcept { +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -14075,7 +15428,7 @@ Type any_cast(const basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) noexcept { +[[nodiscard]] Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); @@ -14084,7 +15437,7 @@ Type any_cast(basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) noexcept { +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); @@ -14100,14 +15453,14 @@ Type any_cast(basic_any &&data) noexcept { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) noexcept { +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template -Type *any_cast(basic_any *data) noexcept { +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); @@ -14127,7 +15480,7 @@ Type *any_cast(basic_any *data) noexcept { * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> -basic_any make_any(Args &&...args) { +[[nodiscard]] basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } @@ -14140,7 +15493,7 @@ basic_any make_any(Args &&...args) { * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> -basic_any forward_as_any(Type &&value) { +[[nodiscard]] basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } @@ -14258,7 +15611,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -14279,7 +15632,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -14441,284 +15794,6 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #endif // #include "../core/type_info.hpp" -#ifndef ENTT_CORE_TYPE_INFO_HPP -#define ENTT_CORE_TYPE_INFO_HPP - -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/attribute.h" - -// #include "fwd.hpp" - -// #include "hashed_string.hpp" - - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -struct ENTT_API type_index final { - [[nodiscard]] static id_type next() noexcept { - static ENTT_MAYBE_ATOMIC(id_type) value{}; - return value++; - } -}; - -template -[[nodiscard]] constexpr auto stripped_type_name() noexcept { -#if defined ENTT_PRETTY_FUNCTION - std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; - auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); - auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); - return value; -#else - return std::string_view{""}; -#endif -} - -template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) noexcept { - constexpr auto value = stripped_type_name(); - return value; -} - -template -[[nodiscard]] static std::string_view type_name(char) noexcept { - static const auto value = stripped_type_name(); - return value; -} - -template().find_first_of('.')> -[[nodiscard]] static constexpr id_type type_hash(int) noexcept { - constexpr auto stripped = stripped_type_name(); - constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); - return value; -} - -template -[[nodiscard]] static id_type type_hash(char) noexcept { - static const auto value = [](const auto stripped) { - return hashed_string::value(stripped.data(), stripped.size()); - }(stripped_type_name()); - return value; -} - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Type sequential identifier. - * @tparam Type Type for which to generate a sequential identifier. - */ -template -struct ENTT_API type_index final { - /** - * @brief Returns the sequential identifier of a given type. - * @return The sequential identifier of a given type. - */ - [[nodiscard]] static id_type value() noexcept { - static const id_type value = internal::type_index::next(); - return value; - } - - /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const noexcept { - return value(); - } -}; - -/** - * @brief Type hash. - * @tparam Type Type for which to generate a hash value. - */ -template -struct type_hash final { - /** - * @brief Returns the numeric representation of a given type. - * @return The numeric representation of the given type. - */ -#if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() noexcept { - return internal::type_hash(0); -#else - [[nodiscard]] static constexpr id_type value() noexcept { - return type_index::value(); -#endif - } - - /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const noexcept { - return value(); - } -}; - -/** - * @brief Type name. - * @tparam Type Type for which to generate a name. - */ -template -struct type_name final { - /** - * @brief Returns the name of a given type. - * @return The name of the given type. - */ - [[nodiscard]] static constexpr std::string_view value() noexcept { - return internal::type_name(0); - } - - /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const noexcept { - return value(); - } -}; - -/*! @brief Implementation specific information about a type. */ -struct type_info final { - /** - * @brief Constructs a type info object for a given type. - * @tparam Type Type for which to construct a type info object. - */ - template - constexpr type_info(std::in_place_type_t) noexcept - : seq{type_index>>::value()}, - identifier{type_hash>>::value()}, - alias{type_name>>::value()} {} - - /** - * @brief Type index. - * @return Type index. - */ - [[nodiscard]] constexpr id_type index() const noexcept { - return seq; - } - - /** - * @brief Type hash. - * @return Type hash. - */ - [[nodiscard]] constexpr id_type hash() const noexcept { - return identifier; - } - - /** - * @brief Type name. - * @return Type name. - */ - [[nodiscard]] constexpr std::string_view name() const noexcept { - return alias; - } - -private: - id_type seq; - id_type identifier; - std::string_view alias; -}; - -/** - * @brief Compares the contents of two type info objects. - * @param lhs A type info object. - * @param rhs A type info object. - * @return True if the two type info objects are identical, false otherwise. - */ -[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { - return lhs.hash() == rhs.hash(); -} - -/** - * @brief Compares the contents of two type info objects. - * @param lhs A type info object. - * @param rhs A type info object. - * @return True if the two type info objects differ, false otherwise. - */ -[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { - return !(lhs == rhs); -} - -/** - * @brief Compares two type info objects. - * @param lhs A valid type info object. - * @param rhs A valid type info object. - * @return True if the first element is less than the second, false otherwise. - */ -[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { - return lhs.index() < rhs.index(); -} - -/** - * @brief Compares two type info objects. - * @param lhs A valid type info object. - * @param rhs A valid type info object. - * @return True if the first element is less than or equal to the second, false - * otherwise. - */ -[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { - return !(rhs < lhs); -} - -/** - * @brief Compares two type info objects. - * @param lhs A valid type info object. - * @param rhs A valid type info object. - * @return True if the first element is greater than the second, false - * otherwise. - */ -[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { - return rhs < lhs; -} - -/** - * @brief Compares two type info objects. - * @param lhs A valid type info object. - * @param rhs A valid type info object. - * @return True if the first element is greater than or equal to the second, - * false otherwise. - */ -[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { - return !(lhs < rhs); -} - -/** - * @brief Returns the type info object associated to a given type. - * - * The returned element refers to an object with static storage duration.
- * The type doesn't need to be a complete type. If the type is a reference, the - * result refers to the referenced type. In all cases, top-level cv-qualifiers - * are ignored. - * - * @tparam Type Type for which to generate a type info object. - * @return A reference to a properly initialized type info object. - */ -template -[[nodiscard]] const type_info &type_id() noexcept { - if constexpr(std::is_same_v>>) { - static type_info instance{std::in_place_type}; - return instance; - } else { - return type_id>>(); - } -} - -/*! @copydoc type_id */ -template -[[nodiscard]] const type_info &type_id(Type &&) noexcept { - return type_id>>(); -} - -} // namespace entt - -#endif // #include "entity.hpp" @@ -14798,6 +15873,10 @@ struct sparse_set_iterator final { return *operator->(); } + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } @@ -14807,38 +15886,38 @@ struct sparse_set_iterator final { difference_type offset; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } -template -[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } -template -[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { - return lhs.index() < rhs.index(); +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -14849,14 +15928,6 @@ template * @endcond */ -/*! @brief Sparse set deletion policy. */ -enum class deletion_policy : std::uint8_t { - /*! @brief Swap-and-pop deletion policy. */ - swap_and_pop = 0u, - /*! @brief In-place deletion policy. */ - in_place = 1u -}; - /** * @brief Basic sparse set implementation. * @@ -14864,10 +15935,6 @@ enum class deletion_policy : std::uint8_t { * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a * _packed_ one; one used for direct access through contiguous memory, the other * one used to get the data through an extra level of indirection.
- * This is largely used by the registry to offer users the fastest access ever - * to the components. Views and groups in general are almost entirely designed - * around sparse sets. - * * This type of data structure is widely documented in the literature and on the * web. This is nothing more than a customized implementation suitable for the * purpose of the framework. @@ -14877,7 +15944,7 @@ enum class deletion_policy : std::uint8_t { * no guarantees that entities are returned in the insertion order when iterate * a sparse set. Do not make assumption on the order in any case. * - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Entity A valid entity type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template @@ -14886,23 +15953,26 @@ class basic_sparse_set { static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector; - using entity_traits = entt_traits; [[nodiscard]] auto sparse_ptr(const Entity entt) const { - const auto pos = static_cast(entity_traits::to_entity(entt)); - const auto page = pos / entity_traits::page_size; - return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr; + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; } [[nodiscard]] auto &sparse_ref(const Entity entt) const { ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); - const auto pos = static_cast(entity_traits::to_entity(entt)); - return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)]; + const auto pos = static_cast(traits_type::to_entity(entt)); + return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - index(entt)); } [[nodiscard]] auto &assure_at_least(const Entity entt) { - const auto pos = static_cast(entity_traits::to_entity(entt)); - const auto page = pos / entity_traits::page_size; + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; if(!(page < sparse.size())) { sparse.resize(page + 1u, nullptr); @@ -14910,11 +15980,11 @@ class basic_sparse_set { if(!sparse[page]) { auto page_allocator{packed.get_allocator()}; - sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size); - std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null); + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, null); } - auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; + auto &elem = sparse[page][fast_mod(pos, traits_type::page_size)]; ENTT_ASSERT(elem == null, "Slot not available"); return elem; } @@ -14924,8 +15994,8 @@ class basic_sparse_set { for(auto &&page: sparse) { if(page != nullptr) { - std::destroy(page, page + entity_traits::page_size); - alloc_traits::deallocate(page_allocator, page, entity_traits::page_size); + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); page = nullptr; } } @@ -14936,13 +16006,28 @@ class basic_sparse_set { return nullptr; } - virtual void swap_at(const std::size_t, const std::size_t) {} - virtual void move_element(const std::size_t, const std::size_t) {} + virtual void swap_or_move(const std::size_t, const std::size_t) {} protected: /*! @brief Random access iterator type. */ using basic_iterator = internal::sparse_set_iterator; + /** + * @brief Swaps two items at specific locations. + * @param lhs A position to move from. + * @param rhs The other position to move from. + */ + void swap_at(const std::size_t lhs, const std::size_t rhs) { + const auto entity = static_cast(lhs); + const auto other = static_cast(rhs); + + sparse_ref(packed[lhs]) = traits_type::combine(other, traits_type::to_integral(packed[lhs])); + sparse_ref(packed[rhs]) = traits_type::combine(entity, traits_type::to_integral(packed[rhs])); + + using std::swap; + swap(packed[lhs], packed[rhs]); + } + /** * @brief Erases an entity from a sparse set. * @param it An iterator to the element to pop. @@ -14950,8 +16035,8 @@ class basic_sparse_set { void swap_and_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched"); auto &self = sparse_ref(*it); - const auto entt = entity_traits::to_entity(self); - sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back())); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); packed[static_cast(entt)] = packed.back(); // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((packed.back() = null, true), ""); @@ -14966,8 +16051,8 @@ class basic_sparse_set { */ void in_place_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched"); - const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null)); - packed[static_cast(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved)); + const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null)); + packed[static_cast(entt)] = std::exchange(free_list, traits_type::combine(entt, tombstone)); } protected: @@ -14988,6 +16073,23 @@ class basic_sparse_set { } } + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + if(const auto prev = std::exchange(free_list, tombstone); prev == null) { + for(auto first = begin(); !(first.index() < 0); ++first) { + sparse_ref(*first) = null; + } + } else { + for(auto first = begin(); !(first.index() < 0); ++first) { + if(*first != tombstone) { + sparse_ref(*first) = null; + } + } + } + + packed.clear(); + } + /** * @brief Assigns an entity to a sparse set. * @param entt A valid identifier. @@ -14999,25 +16101,27 @@ class basic_sparse_set { if(auto &elem = assure_at_least(entt); free_list == null || force_back) { packed.push_back(entt); - elem = entity_traits::combine(static_cast(packed.size() - 1u), entity_traits::to_integral(entt)); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); return begin(); } else { - const auto pos = static_cast(entity_traits::to_entity(free_list)); - elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt)); + const auto pos = static_cast(traits_type::to_entity(free_list)); + elem = traits_type::combine(traits_type::to_integral(free_list), traits_type::to_integral(entt)); free_list = std::exchange(packed[pos], entt); return --(end() - pos); } } public: - /*! @brief Allocator type. */ - using allocator_type = Allocator; + /*! @brief Entity traits. */ + using traits_type = entt_traits; /*! @brief Underlying entity identifier. */ - using entity_type = typename entity_traits::value_type; + using entity_type = typename traits_type::value_type; /*! @brief Underlying version type. */ - using version_type = typename entity_traits::version_type; + using version_type = typename traits_type::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; /*! @brief Pointer type to contained entities. */ using pointer = typename packed_container_type::const_pointer; /*! @brief Random access iterator type. */ @@ -15027,7 +16131,7 @@ class basic_sparse_set { /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ - using const_reverse_iterator = reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; /*! @brief Default constructor. */ basic_sparse_set() @@ -15051,14 +16155,14 @@ class basic_sparse_set { /** * @brief Constructs an empty container with the given value type, policy * and allocator. - * @param value Returned value type, if any. + * @param elem Returned value type, if any. * @param pol Type of deletion policy. * @param allocator The allocator to use (possibly default-constructed). */ - explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) : sparse{allocator}, packed{allocator}, - info{&value}, + info{&elem}, free_list{tombstone}, mode{pol} {} @@ -15175,7 +16279,7 @@ class basic_sparse_set { * @return Extent of the sparse set. */ [[nodiscard]] size_type extent() const noexcept { - return sparse.size() * entity_traits::page_size; + return sparse.size() * traits_type::page_size; } /** @@ -15200,6 +16304,14 @@ class basic_sparse_set { return packed.empty(); } + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (free_list == null); + } + /** * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. @@ -15211,8 +16323,7 @@ class basic_sparse_set { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first entity of the internal packed - * array. If the sparse set is empty, the returned iterator will be equal to + * If the sparse set is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity of the sparse set. @@ -15229,11 +16340,6 @@ class basic_sparse_set { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last entity in - * a sparse set. Attempting to dereference the returned iterator results in - * undefined behavior. - * * @return An iterator to the element following the last entity of a sparse * set. */ @@ -15249,9 +16355,8 @@ class basic_sparse_set { /** * @brief Returns a reverse iterator to the beginning. * - * The returned iterator points to the first entity of the reversed internal - * packed array. If the sparse set is empty, the returned iterator will be - * equal to `rend()`. + * If the sparse set is empty, the returned iterator will be equal to + * `rend()`. * * @return An iterator to the first entity of the reversed internal packed * array. @@ -15267,11 +16372,6 @@ class basic_sparse_set { /** * @brief Returns a reverse iterator to the end. - * - * The returned iterator points to the element following the last entity in - * the reversed sparse set. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last entity of the * reversed sparse set. */ @@ -15291,7 +16391,7 @@ class basic_sparse_set { * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { - return contains(entt) ? --(end() - index(entt)) : end(); + return contains(entt) ? to_iterator(entt) : end(); } /** @@ -15301,9 +16401,9 @@ class basic_sparse_set { */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); - constexpr auto cap = entity_traits::to_entity(null); + constexpr auto cap = traits_type::to_entity(null); // testing versions permits to avoid accessing the packed array - return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap); + return elem && (((~cap & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); } /** @@ -15314,8 +16414,8 @@ class basic_sparse_set { */ [[nodiscard]] version_type current(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); - constexpr auto fallback = entity_traits::to_version(tombstone); - return elem ? entity_traits::to_version(*elem) : fallback; + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; } /** @@ -15330,7 +16430,7 @@ class basic_sparse_set { */ [[nodiscard]] size_type index(const entity_type entt) const noexcept { ENTT_ASSERT(contains(entt), "Set does not contain entity"); - return static_cast(entity_traits::to_entity(sparse_ref(entt))); + return static_cast(traits_type::to_entity(sparse_ref(entt))); } /** @@ -15362,13 +16462,13 @@ class basic_sparse_set { * @param entt A valid identifier. * @return An opaque pointer to the element assigned to the entity, if any. */ - [[nodiscard]] const void *get(const entity_type entt) const noexcept { + [[nodiscard]] const void *value(const entity_type entt) const noexcept { return get_at(index(entt)); } - /*! @copydoc get */ - [[nodiscard]] void *get(const entity_type entt) noexcept { - return const_cast(std::as_const(*this).get(entt)); + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(entt)); } /** @@ -15379,28 +16479,12 @@ class basic_sparse_set { * results in undefined behavior. * * @param entt A valid identifier. - * @param value Optional opaque value to forward to mixins, if any. + * @param elem Optional opaque element to forward to mixins, if any. * @return Iterator pointing to the emplaced element in case of success, the * `end()` iterator otherwise. */ - iterator emplace(const entity_type entt, const void *value = nullptr) { - return try_emplace(entt, false, value); - } - - /** - * @brief Bump the version number of an entity. - * - * @warning - * Attempting to bump the version of an entity that doesn't belong to the - * sparse set results in undefined behavior. - * - * @param entt A valid identifier. - */ - void bump(const entity_type entt) { - auto &entity = sparse_ref(entt); - ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); - entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt)); - packed[static_cast(entity_traits::to_entity(entity))] = entt; + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, false, elem); } /** @@ -15417,7 +16501,7 @@ class basic_sparse_set { * success, the `end()` iterator otherwise. */ template - iterator insert(It first, It last) { + iterator push(It first, It last) { for(auto it = first; it != last; ++it) { try_emplace(*it, true); } @@ -15425,6 +16509,24 @@ class basic_sparse_set { return first == last ? end() : find(*first); } + /** + * @brief Bump the version number of an entity. + * + * @warning + * Attempting to bump the version of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &entity = sparse_ref(entt); + ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); + entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt)); + packed[static_cast(traits_type::to_entity(entity))] = entt; + return traits_type::to_version(entt); + } + /** * @brief Erases an entity from a sparse set. * @@ -15435,7 +16537,7 @@ class basic_sparse_set { * @param entt A valid identifier. */ void erase(const entity_type entt) { - const auto it = --(end() - index(entt)); + const auto it = to_iterator(entt); pop(it, it + 1u); } @@ -15479,29 +16581,45 @@ class basic_sparse_set { size_type remove(It first, It last) { size_type count{}; - for(; first != last; ++first) { - count += remove(*first); + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += std::distance(it, first); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } } return count; } - /*! @brief Removes all tombstones from the packed array of a sparse set. */ + /*! @brief Removes all tombstones from a sparse set. */ void compact() { size_type from = packed.size(); for(; from && packed[from - 1u] == tombstone; --from) {} - for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) { - if(const size_type to = entity_traits::to_entity(*it); to < from) { + for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[traits_type::to_entity(*it)])) { + if(const size_type to = traits_type::to_entity(*it); to < from) { --from; - move_element(from, to); + swap_or_move(from, to); - using std::swap; - swap(packed[from], packed[to]); + packed[to] = std::exchange(packed[from], tombstone); + const auto entity = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); - const auto entity = static_cast(to); - sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to])); - *it = entity_traits::combine(static_cast(from), entity_traits::reserved); + *it = traits_type::combine(static_cast(from), tombstone); for(; from && packed[from - 1u] == tombstone; --from) {} } } @@ -15524,21 +16642,12 @@ class basic_sparse_set { * @param rhs A valid identifier. */ void swap_elements(const entity_type lhs, const entity_type rhs) { - ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities"); - - auto &entt = sparse_ref(lhs); - auto &other = sparse_ref(rhs); - - const auto from = entity_traits::to_entity(entt); - const auto to = entity_traits::to_entity(other); + const auto from = index(lhs); + const auto to = index(rhs); - // basic no-leak guarantee (with invalid state) if swapping throws - swap_at(static_cast(from), static_cast(to)); - entt = entity_traits::combine(to, entity_traits::to_integral(packed[from])); - other = entity_traits::combine(from, entity_traits::to_integral(packed[to])); - - using std::swap; - swap(packed[from], packed[to]); + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, to); } /** @@ -15586,9 +16695,9 @@ class basic_sparse_set { const auto idx = index(packed[next]); const auto entt = packed[curr]; - swap_at(next, idx); - const auto entity = static_cast(curr); - sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr])); + swap_or_move(next, idx); + const auto entity = static_cast(curr); + sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr])); curr = std::exchange(next, idx); } } @@ -15616,48 +16725,35 @@ class basic_sparse_set { * @brief Sort entities according to their order in another sparse set. * * Entities that are part of both the sparse sets are ordered internally - * according to the order they have in `other`. All the other entities goes - * to the end of the list and there are no guarantees on their order.
- * In other terms, this function can be used to impose the same order on two - * sets by using one of them as a master and the other one as a slave. - * - * Iterating the sparse set with a couple of iterators returns elements in - * the expected order after a call to `respect`. See `begin` and `end` for - * more details. + * according to the order they have in `other`.
+ * All the other entities goes to the end of the list and there are no + * guarantees on their order. * * @param other The sparse sets that imposes the order of the entities. */ - void respect(const basic_sparse_set &other) { + void sort_as(const basic_sparse_set &other) { compact(); const auto to = other.end(); auto from = other.begin(); - for(size_type pos = packed.size() - 1; pos && from != to; ++from) { - if(contains(*from)) { - if(*from != packed[pos]) { + for(auto it = begin(); it.index() && from != to; ++from) { + if(const auto curr = *from; contains(curr)) { + if(const auto entt = *it; entt != curr) { // basic no-leak guarantee (with invalid state) if swapping throws - swap_elements(packed[pos], *from); + swap_elements(entt, curr); } - --pos; + ++it; } } } /*! @brief Clears a sparse set. */ void clear() { - if(const auto last = end(); free_list == null) { - pop(begin(), last); - } else { - for(auto &&entity: *this) { - // tombstone filter on itself - if(const auto it = find(entity); it != last) { - pop(it, it + 1u); - } - } - } - + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); free_list = tombstone; packed.clear(); } @@ -15698,15 +16794,21 @@ class basic_sparse_set { #include // #include "../config/config.h" -// #include "../core/compressed_pair.hpp" -#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP -#define ENTT_CORE_COMPRESSED_PAIR_HPP +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP #include -#include #include -#include -// #include "type_traits.hpp" +// #include "../config/config.h" + +// #include "fwd.hpp" namespace entt { @@ -15718,1381 +16820,2457 @@ namespace entt { namespace internal { -template -struct compressed_pair_element { - using reference = Type &; - using const_reference = const Type &; +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; - template>> - constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) - : value{} {} +template<> +struct in_place_delete: std::false_type {}; - template>, compressed_pair_element>>> - constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) - : value{std::forward(arg)} {} +template +struct in_place_delete> + : std::true_type {}; - template - constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) - : value{std::forward(std::get(args))...} {} +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; - [[nodiscard]] constexpr reference get() noexcept { - return value; - } +template<> +struct page_size: std::integral_constant {}; - [[nodiscard]] constexpr const_reference get() const noexcept { - return value; - } +template +struct page_size>> + : std::integral_constant {}; -private: - Type value; +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Component type. */ + using type = Type; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; }; -template -struct compressed_pair_element>>: Type { - using reference = Type &; - using const_reference = const Type &; - using base_type = Type; +} // namespace entt - template>> - constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) - : base_type{} {} +#endif - template>, compressed_pair_element>>> - constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) - : base_type{std::forward(arg)} {} +// #include "entity.hpp" - template - constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) - : base_type{std::forward(std::get(args))...} {} +// #include "fwd.hpp" - [[nodiscard]] constexpr reference get() noexcept { - return *this; - } +// #include "sparse_set.hpp" - [[nodiscard]] constexpr const_reference get() const noexcept { - return *this; - } -}; -} // namespace internal +namespace entt { /** + * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. - * @endcond */ -/** - * @brief A compressed pair. - * - * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to - * reduce its final size to a minimum. - * - * @tparam First The type of the first element that the pair stores. - * @tparam Second The type of the second element that the pair stores. - */ -template -class compressed_pair final - : internal::compressed_pair_element, - internal::compressed_pair_element { - using first_base = internal::compressed_pair_element; - using second_base = internal::compressed_pair_element; +namespace internal { + +template +class storage_iterator final { + friend storage_iterator; + + using container_type = std::remove_const_t; + using alloc_traits = std::allocator_traits; + + using iterator_traits = std::iterator_traits, + typename alloc_traits::template rebind_traits::element_type>::const_pointer, + typename alloc_traits::template rebind_traits::element_type>::pointer>>; public: - /*! @brief The type of the first element that the pair stores. */ - using first_type = First; - /*! @brief The type of the second element that the pair stores. */ - using second_type = Second; + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; - /** - * @brief Default constructor, conditionally enabled. - * - * This constructor is only available when the types that the pair stores - * are both at least default constructible. - * - * @tparam Dummy Dummy template parameter used for internal purposes. - */ - template && std::is_default_constructible_v>> - constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) - : first_base{}, - second_base{} {} + constexpr storage_iterator() noexcept = default; - /** - * @brief Copy constructor. - * @param other The instance to copy from. - */ - constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} - /** - * @brief Move constructor. - * @param other The instance to move from. - */ - constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + template, typename = std::enable_if_t> + constexpr storage_iterator(const storage_iterator, Size> &other) noexcept + : storage_iterator{other.payload, other.offset} {} - /** - * @brief Constructs a pair from its values. - * @tparam Arg Type of value to use to initialize the first element. - * @tparam Other Type of value to use to initialize the second element. - * @param arg Value to use to initialize the first element. - * @param other Value to use to initialize the second element. - */ - template - constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) - : first_base{std::forward(arg)}, - second_base{std::forward(other)} {} + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } - /** - * @brief Constructs a pair by forwarding the arguments to its parts. - * @tparam Args Types of arguments to use to initialize the first element. - * @tparam Other Types of arguments to use to initialize the second element. - * @param args Arguments to use to initialize the first element. - * @param other Arguments to use to initialize the second element. - */ - template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) - : first_base{std::move(args), std::index_sequence_for{}}, - second_base{std::move(other), std::index_sequence_for{}} {} + constexpr storage_iterator operator++(int) noexcept { + storage_iterator orig = *this; + return ++(*this), orig; + } - /** - * @brief Copy assignment operator. - * @param other The instance to copy from. - * @return This compressed pair object. - */ - constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } - /** - * @brief Move assignment operator. - * @param other The instance to move from. - * @return This compressed pair object. - */ - constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + constexpr storage_iterator operator--(int) noexcept { + storage_iterator orig = *this; + return operator--(), orig; + } - /** - * @brief Returns the first element that a pair stores. - * @return The first element that a pair stores. - */ - [[nodiscard]] constexpr first_type &first() noexcept { - return static_cast(*this).get(); + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; } - /*! @copydoc first */ - [[nodiscard]] constexpr const first_type &first() const noexcept { - return static_cast(*this).get(); + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); } - /** - * @brief Returns the second element that a pair stores. - * @return The second element that a pair stores. - */ - [[nodiscard]] constexpr second_type &second() noexcept { - return static_cast(*this).get(); + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); } - /*! @copydoc second */ - [[nodiscard]] constexpr const second_type &second() const noexcept { - return static_cast(*this).get(); + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); } - /** - * @brief Swaps two compressed pair objects. - * @param other The compressed pair to swap with. - */ - constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { - using std::swap; - swap(first(), other.first()); - swap(second(), other.second()); + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = index() - value; + return (*payload)[pos / Size][fast_mod(pos, Size)]; } - /** - * @brief Extracts an element from the compressed pair. - * @tparam Index An integer value that is either 0 or 1. - * @return Returns a reference to the first element if `Index` is 0 and a - * reference to the second element if `Index` is 1. - */ - template - constexpr decltype(auto) get() noexcept { - if constexpr(Index == 0u) { - return first(); - } else { - static_assert(Index == 1u, "Index out of bounds"); - return second(); - } + [[nodiscard]] constexpr pointer operator->() const noexcept { + const auto pos = index(); + return (*payload)[pos / Size] + fast_mod(pos, Size); } - /*! @copydoc get */ - template - constexpr decltype(auto) get() const noexcept { - if constexpr(Index == 0u) { - return first(); - } else { - static_assert(Index == 1u, "Index out of bounds"); - return second(); - } + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; } + +private: + Container *payload; + difference_type offset; }; -/** - * @brief Deduction guide. - * @tparam Type Type of value to use to initialize the first element. - * @tparam Other Type of value to use to initialize the second element. - */ -template -compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} -/** - * @brief Swaps two compressed pair objects. - * @tparam First The type of the first element that the pairs store. - * @tparam Second The type of the second element that the pairs store. - * @param lhs A valid compressed pair object. - * @param rhs A valid compressed pair object. - */ -template -inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { - lhs.swap(rhs); +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); } -} // namespace entt +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} -// disable structured binding support for clang 6, it messes when specializing tuple_size -#if !defined __clang_major__ || __clang_major__ > 6 -namespace std { +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} -/** - * @brief `std::tuple_size` specialization for `compressed_pair`s. - * @tparam First The type of the first element that the pair stores. - * @tparam Second The type of the second element that the pair stores. - */ -template -struct tuple_size>: integral_constant {}; +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; +} -/** - * @brief `std::tuple_element` specialization for `compressed_pair`s. - * @tparam Index The index of the type to return. - * @tparam First The type of the first element that the pair stores. - * @tparam Second The type of the second element that the pair stores. - */ -template -struct tuple_element>: conditional { - static_assert(Index < 2u, "Index out of bounds"); -}; +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} -} // namespace std -#endif +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} -#endif +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; -// #include "../core/iterator.hpp" +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; -// #include "../core/memory.hpp" + constexpr extended_storage_iterator() + : it{} {} -// #include "../core/type_info.hpp" + constexpr extended_storage_iterator(It base, Other... other) + : it{base, other...} {} -// #include "component.hpp" + template && ...) && (std::is_constructible_v && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} -// #include "entity.hpp" + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get(it), (++std::get(it), ...), *this; + } -// #include "fwd.hpp" + constexpr extended_storage_iterator operator++(int) noexcept { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } -// #include "sparse_set.hpp" + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } -// #include "storage_mixin.hpp" -#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP -#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get(it), *std::get(it)...}; + } -#include -// #include "../config/config.h" + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } -// #include "../core/any.hpp" + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; -// #include "../signal/sigh.hpp" -#ifndef ENTT_SIGNAL_SIGH_HPP -#define ENTT_SIGNAL_SIGH_HPP +private: + std::tuple it; +}; -#include -#include -#include -#include -#include -// #include "delegate.hpp" -#ifndef ENTT_SIGNAL_DELEGATE_HPP -#define ENTT_SIGNAL_DELEGATE_HPP +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} -#include -#include -#include -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} -// #include "version.h" -#ifndef ENTT_CONFIG_VERSION_H -#define ENTT_CONFIG_VERSION_H +} // namespace internal -// #include "macro.h" -#ifndef ENTT_CONFIG_MACRO_H -#define ENTT_CONFIG_MACRO_H +/** + * Internal details not to be documented. + * @endcond + */ -#define ENTT_STR(arg) #arg -#define ENTT_XSTR(arg) ENTT_STR(arg) +/** + * @brief Basic storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Type Type of objects assigned to the entities. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; -#endif + static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } -#define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; -#define ENTT_VERSION \ - ENTT_XSTR(ENTT_VERSION_MAJOR) \ - "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); -#endif + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + return payload[idx] + fast_mod(pos, traits_type::page_size); + } -#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_CONSTEXPR -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) -# define ENTT_THROW -# define ENTT_TRY if(true) -# define ENTT_CATCH if(false) -#endif + template + auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { + const auto it = base_type::try_emplace(entt, force_back); -#ifdef ENTT_USE_ATOMIC -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else -# define ENTT_MAYBE_ATOMIC(Type) Type -#endif + ENTT_TRY { + auto elem = assure_at_least(static_cast(it.index())); + entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif + return it; + } -#ifndef ENTT_SPARSE_PAGE -# define ENTT_SPARSE_PAGE 4096 -#endif + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; -#ifndef ENTT_PACKED_PAGE -# define ENTT_PACKED_PAGE 1024 -#endif + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::at(pos) != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } -#ifdef ENTT_DISABLE_ASSERT -# undef ENTT_ASSERT -# define ENTT_ASSERT(condition, msg) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, msg) assert(condition) -#endif + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } -#ifdef ENTT_DISABLE_ASSERT -# undef ENTT_ASSERT_CONSTEXPR -# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) -#elif !defined ENTT_ASSERT_CONSTEXPR -# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) -#endif + payload.resize(from); + } -#ifdef ENTT_NO_ETO -# define ENTT_ETO_TYPE(Type) void -#else -# define ENTT_ETO_TYPE(Type) Type -#endif +private: + const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } -#ifdef ENTT_STANDARD_CPP -# define ENTT_NONSTD false -#else -# define ENTT_NONSTD true -# if defined __clang__ || defined __GNUC__ -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif defined _MSC_VER -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); -#if defined _MSC_VER -# pragma detect_mismatch("entt.version", ENTT_VERSION) -# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) -# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) -# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) -#endif + if constexpr(!is_pinned_type_v) { + auto &elem = element_at(from); -#endif + if constexpr(traits_type::in_place_delete) { + if(base_type::operator[](to) == tombstone) { + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + return; + } + } -// #include "../core/type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP + using std::swap; + swap(elem, element_at(to)); + } + } -#include -#include -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); -// #include "version.h" -#ifndef ENTT_CONFIG_VERSION_H -#define ENTT_CONFIG_VERSION_H + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } -// #include "macro.h" -#ifndef ENTT_CONFIG_MACRO_H -#define ENTT_CONFIG_MACRO_H + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; -#define ENTT_STR(arg) #arg -#define ENTT_XSTR(arg) ENTT_STR(arg) + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } + } -#endif + /** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value) { + if constexpr(std::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } +public: + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type to contained elements. */ + using pointer = typename container_type::pointer; + /*! @brief Constant pointer type to contained elements. */ + using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = internal::storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::storage_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; -#define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} -#define ENTT_VERSION \ - ENTT_XSTR(ENTT_VERSION_MAJOR) \ - "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator}, + payload{allocator} {} -#endif + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + payload{std::move(other.payload)} {} + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + } -#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_CONSTEXPR -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) -# define ENTT_THROW -# define ENTT_TRY if(true) -# define ENTT_CATCH if(false) -#endif + /*! @brief Default destructor. */ + ~basic_storage() override { + shrink_to_size(0u); + } -#ifdef ENTT_USE_ATOMIC -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else -# define ENTT_MAYBE_ATOMIC(Type) Type -#endif + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif + shrink_to_size(0u); + base_type::operator=(std::move(other)); + payload = std::move(other.payload); + return *this; + } -#ifndef ENTT_SPARSE_PAGE -# define ENTT_SPARSE_PAGE 4096 -#endif + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) { + using std::swap; + base_type::swap(other); + swap(payload, other.payload); + } -#ifndef ENTT_PACKED_PAGE -# define ENTT_PACKED_PAGE 1024 -#endif + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } -#ifdef ENTT_DISABLE_ASSERT -# undef ENTT_ASSERT -# define ENTT_ASSERT(condition, msg) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, msg) assert(condition) -#endif + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } -#ifdef ENTT_DISABLE_ASSERT -# undef ENTT_ASSERT_CONSTEXPR -# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) -#elif !defined ENTT_ASSERT_CONSTEXPR -# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) -#endif + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } -#ifdef ENTT_NO_ETO -# define ENTT_ETO_TYPE(Type) void -#else -# define ENTT_ETO_TYPE(Type) Type -#endif + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } -#ifdef ENTT_STANDARD_CPP -# define ENTT_NONSTD false -#else -# define ENTT_NONSTD true -# if defined __clang__ || defined __GNUC__ -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif defined _MSC_VER -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } -#if defined _MSC_VER -# pragma detect_mismatch("entt.version", ENTT_VERSION) -# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) -# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) -# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) -#endif + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } -#endif + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast(base_type::size()); + return const_iterator{&payload, pos}; + } -// #include "fwd.hpp" -#ifndef ENTT_CORE_FWD_HPP -#define ENTT_CORE_FWD_HPP + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } -#include -// #include "../config/config.h" + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast(base_type::size()); + return iterator{&payload, pos}; + } + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } -namespace entt { + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } -template -class basic_any; + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } -/*! @brief Alias declaration for type identifiers. */ -using id_type = ENTT_ID_TYPE; + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } -/*! @brief Alias declaration for the most common use case. */ -using any = basic_any<>; + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } -} // namespace entt + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } -#endif + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } -namespace entt { + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } -/** - * @brief Utility class to disambiguate overloaded functions. - * @tparam N Number of choices available. - */ -template -struct choice_t - // Unfortunately, doxygen cannot parse such a construct. - : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ -{}; + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } -/*! @copybrief choice_t */ -template<> -struct choice_t<0> {}; + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).get(entt)); + } -/** - * @brief Variable template for the choice trick. - * @tparam N Number of choices available. - */ -template -inline constexpr choice_t choice{}; + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } -/** - * @brief Identity type trait. - * - * Useful to establish non-deduced contexts in template argument deduction - * (waiting for C++20) or to provide types through function arguments. - * - * @tparam Type A type. - */ -template -struct type_identity { - /*! @brief Identity type. */ - using type = Type; -}; + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } -/** - * @brief Helper type. - * @tparam Type A type. - */ -template -using type_identity_t = typename type_identity::type; + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + const auto it = emplace_element(entt, false, Type{std::forward(args)...}); + return element_at(static_cast(it.index())); + } else { + const auto it = emplace_element(entt, false, std::forward(args)...); + return element_at(static_cast(it.index())); + } + } -/** - * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. - * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. - */ -template -struct size_of: std::integral_constant {}; + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template + value_type &patch(const entity_type entt, Func &&...func) { + const auto idx = base_type::index(entt); + auto &elem = element_at(idx); + (std::forward(func)(elem), ...); + return elem; + } -/*! @copydoc size_of */ -template -struct size_of> - : std::integral_constant {}; + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + * @return Iterator pointing to the last element inserted, if any. + */ + template + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } -/** - * @brief Helper variable template. - * @tparam Type The type of which to return the size. - */ -template -inline constexpr std::size_t size_of_v = size_of::value; + return begin(); + } -/** - * @brief Using declaration to be used to _repeat_ the same type a number of - * times equal to the size of a given parameter pack. - * @tparam Type A type to repeat. - */ -template -using unpack_as_type = Type; + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given range. + * + * @sa construct + * + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of objects. + * @return Iterator pointing to the first element inserted, if any. + */ + template::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } -/** - * @brief Helper variable template to be used to _repeat_ the same value a - * number of times equal to the size of a given parameter pack. - * @tparam Value A value to repeat. - */ -template -inline constexpr auto unpack_as_value = Value; + return begin(); + } -/** - * @brief Wraps a static constant. - * @tparam Value A static constant. - */ -template -using integral_constant = std::integral_constant; + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; + } -/** - * @brief Alias template to facilitate the creation of named values. - * @tparam Value A constant value at least convertible to `id_type`. - */ -template -using tag = integral_constant; + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; + } -/** - * @brief A class to use to push around lists of types, nothing more. - * @tparam Type Types provided by the type list. - */ -template -struct type_list { - /*! @brief Type list type. */ - using type = type_list; - /*! @brief Compile-time number of elements in the type list. */ - static constexpr auto size = sizeof...(Type); + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}}; + } + +private: + container_type payload; }; -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_element; +/*! @copydoc basic_storage */ +template +class basic_storage::page_size == 0u>> + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); -/** - * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Index Index of the type to return. - * @tparam First First type provided by the type list. - * @tparam Other Other types provided by the type list. - */ -template -struct type_list_element> - : type_list_element> {}; +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set>; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; -/** - * @brief Provides compile-time indexed access to the types of a type list. - * @tparam First First type provided by the type list. - * @tparam Other Other types provided by the type list. - */ -template -struct type_list_element<0u, type_list> { - /*! @brief Searched type. */ - using type = First; -}; + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} -/** - * @brief Helper type. - * @tparam Index Index of the type to return. - * @tparam List Type list to search into. - */ -template -using type_list_element_t = typename type_list_element::type; + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator} {} -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_index; + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; -/** - * @brief Provides compile-time type access to the types of a type list. - * @tparam Type Type to look for and for which to return the index. - * @tparam First First type provided by the type list. - * @tparam Other Other types provided by the type list. - */ -template -struct type_list_index> { - /*! @brief Unsigned integer type. */ - using value_type = std::size_t; - /*! @brief Compile-time position of the given type in the sublist. */ - static constexpr value_type value = 1u + type_list_index>::value; -}; + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} -/** - * @brief Provides compile-time type access to the types of a type list. - * @tparam Type Type to look for and for which to return the index. - * @tparam Other Other types provided by the type list. - */ -template -struct type_list_index> { - static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); - /*! @brief Unsigned integer type. */ - using value_type = std::size_t; - /*! @brief Compile-time position of the given type in the sublist. */ - static constexpr value_type value = 0u; + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{base_type::get_allocator()}; + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + return std::tuple{}; + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ + template + void emplace(const entity_type entt, Args &&...) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last, Args &&...) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}}; + } }; /** - * @brief Provides compile-time type access to the types of a type list. - * @tparam Type Type to look for and for which to return the index. + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. */ -template -struct type_list_index> { +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + using local_traits_type = entt_traits; + + auto entity_at(const std::size_t pos) const noexcept { + ENTT_ASSERT(pos < local_traits_type::to_entity(null), "Invalid element"); + return local_traits_type::combine(static_cast(pos), {}); + } + +private: + void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) override { + ENTT_ASSERT(((lhs < length) + (rhs < length)) != 1u, "Cross swapping is not supported"); + } + +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(; first != last; ++first) { + if(const auto pos = base_type::index(*first); pos < length) { + base_type::bump(local_traits_type::next(*first)); + + if(pos != --length) { + base_type::swap_at(pos, length); + } + } + } + } + + /*! @brief Erases all entities of a sparse set. */ + void pop_all() override { + length = 0u; + base_type::pop_all(); + } + + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(emplace(hint)); + } + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Entity; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; /*! @brief Unsigned integer type. */ - using value_type = std::size_t; - /*! @brief Compile-time position of the given type in the sublist. */ - static constexpr value_type value = 0u; -}; + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; -/** - * @brief Helper variable template. - * @tparam List Type list. - * @tparam Type Type to look for and for which to return the index. - */ -template -inline constexpr std::size_t type_list_index_v = type_list_index::value; + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} { + } -/** - * @brief Concatenates multiple type lists. - * @tparam Type Types provided by the first type list. - * @tparam Other Types provided by the second type list. - * @return A type list composed by the types of both the type lists. - */ -template -constexpr type_list operator+(type_list, type_list) { - return {}; -} + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy::swap_and_pop, allocator}, + length{} {} -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_cat; + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + length{std::exchange(other.length, size_type{})} {} -/*! @brief Concatenates multiple type lists. */ -template<> -struct type_list_cat<> { - /*! @brief A type list composed by the types of all the type lists. */ - using type = type_list<>; -}; + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator}, + length{std::exchange(other.length, size_type{})} {} -/** - * @brief Concatenates multiple type lists. - * @tparam Type Types provided by the first type list. - * @tparam Other Types provided by the second type list. - * @tparam List Other type lists, if any. - */ -template -struct type_list_cat, type_list, List...> { - /*! @brief A type list composed by the types of all the type lists. */ - using type = typename type_list_cat, List...>::type; -}; + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + base_type::operator=(std::move(other)); + length = std::exchange(other.length, size_type{}); + return *this; + } -/** - * @brief Concatenates multiple type lists. - * @tparam Type Types provided by the type list. - */ -template -struct type_list_cat> { - /*! @brief A type list composed by the types of all the type lists. */ - using type = type_list; + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) { + using std::swap; + base_type::swap(other); + swap(length, other.length); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type emplace() { + if(length == base_type::size()) { + return *base_type::try_emplace(entity_at(length++), true); + } + + return base_type::operator[](length++); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type emplace(const entity_type hint) { + if(hint == null || hint == tombstone) { + return emplace(); + } else if(const auto curr = local_traits_type::construct(local_traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) { + const auto pos = static_cast(local_traits_type::to_entity(hint)); + + while(!(pos < base_type::size())) { + base_type::try_emplace(entity_at(base_type::size()), true); + } + + base_type::swap_at(pos, length++); + } else if(const auto idx = base_type::index(curr); idx < length) { + return emplace(); + } else { + base_type::swap_at(idx, length++); + } + + base_type::bump(hint); + + return hint; + } + + /** + * @brief Updates a given identifier. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void insert(It first, It last) { + for(const auto sz = base_type::size(); first != last && length != sz; ++first, ++length) { + *first = base_type::operator[](length); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(entity_at(length++), true); + } + } + + /** + * @brief Makes all elements in a range contiguous. + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to pack. + * @param last An iterator past the last element of the range to pack. + * @return The number of elements within the newly created range. + */ + template + size_type pack(It first, It last) { + size_type len = length; + + for(; first != last; ++first, --len) { + const auto pos = base_type::index(*first); + ENTT_ASSERT(pos < length, "Invalid element"); + base_type::swap_at(pos, static_cast(len - 1u)); + } + + return (length - len); + } + + /** + * @brief Returns the number of elements considered still in use. + * @return The number of elements considered still in use. + */ + [[nodiscard]] size_type in_use() const noexcept { + return length; + } + + /** + * @brief Sets the number of elements considered still in use. + * @param len The number of elements considered still in use. + */ + void in_use(const size_type len) noexcept { + ENTT_ASSERT(!(len > base_type::size()), "Invalid length"); + length = len; + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::end() - length}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cend() - length}, internal::extended_storage_iterator{base_type::cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rbegin() + length}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crbegin() + length}}; + } + +private: + size_type length; }; -/** - * @brief Helper type. - * @tparam List Type lists to concatenate. - */ -template -using type_list_cat_t = typename type_list_cat::type; +} // namespace entt -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_unique; +#endif + + +namespace entt { /** - * @brief Removes duplicates types from a type list. - * @tparam Type One of the types provided by the given type list. - * @tparam Other The other types provided by the given type list. + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. */ -template -struct type_list_unique> { - /*! @brief A type list without duplicate types. */ - using type = std::conditional_t< - (std::is_same_v || ...), - typename type_list_unique>::type, - type_list_cat_t, typename type_list_unique>::type>>; + +namespace internal { + +template +class extended_group_iterator; + +template +class extended_group_iterator, get_t> { + template + auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(Type::traits_type::page_size == 0u) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(It from, const std::tuple &cpools) + : it{from}, + pools{cpools} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; }; -/*! @brief Removes duplicates types from a type list. */ -template<> -struct type_list_unique> { - /*! @brief A type list without duplicate types. */ - using type = type_list<>; +template +[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() = default; + virtual size_type owned(const id_type *, const size_type) const noexcept { + return 0u; + } }; -/** - * @brief Helper type. - * @tparam Type A type list. - */ -template -using type_list_unique_t = typename type_list_unique::type; +template +class group_handler; -/** - * @brief Provides the member constant `value` to true if a type list contains a - * given type, false otherwise. - * @tparam List Type list. - * @tparam Type Type to look for. - */ -template -struct type_list_contains; +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v...>, "Groups do not support in-place delete"); + static_assert(!std::disjunction_v..., std::is_const..., std::is_const...>, "Const storage type not allowed"); -/** - * @copybrief type_list_contains - * @tparam Type Types provided by the type list. - * @tparam Other Type to look for. - */ -template -struct type_list_contains, Other>: std::disjunction...> {}; + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; -/** - * @brief Helper variable template. - * @tparam List Type list. - * @tparam Type Type to look for. - */ -template -inline constexpr bool type_list_contains_v = type_list_contains::value; + void swap_elements(const std::size_t pos, const entity_type entt) { + std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools); + } -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_diff; + void push_on_construct(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt); + } + } -/** - * @brief Computes the difference between two type lists. - * @tparam Type Types provided by the first type list. - * @tparam Other Types provided by the second type list. - */ -template -struct type_list_diff, type_list> { - /*! @brief A type list that is the difference between the two type lists. */ - using type = type_list_cat_t, Type>, type_list<>, type_list>...>; + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt); + } + } + + void remove_if(const entity_type entt) { + if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) { + swap_elements(--len, entt); + } + } + +public: + using size_type = typename base_type::size_type; + + group_handler(Owned &...opool, Get &...gpool, Exclude &...epool) + : pools{&opool..., &gpool...}, + filter{&epool...}, + len{} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) { + push_on_construct(*first); + } + } + + size_type owned(const id_type *elem, const size_type length) const noexcept final { + size_type cnt = 0u; + + for(auto pos = 0u; pos < length; ++pos) { + cnt += ((elem[pos] == entt::type_hash::value()) || ...); + } + + return cnt; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + template + Type pools_as() const noexcept { + return pools; + } + + template + Type filter_as() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + std::size_t len; }; -/** - * @brief Helper type. - * @tparam List Type lists between which to compute the difference. - */ -template -using type_list_diff_t = typename type_list_diff::type; +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + +public: + using common_type = base_type; + + template + group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool) + : pools{&gpool...}, + filter{&epool...}, + elem{alloc} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + for(const auto entity: static_cast(*std::get<0>(pools))) { + push_on_construct(entity); + } + } -/*! @brief Primary template isn't defined on purpose. */ -template class> -struct type_list_transform; + common_type &handle() noexcept { + return elem; + } -/** - * @brief Applies a given _function_ to a type list and generate a new list. - * @tparam Type Types provided by the type list. - * @tparam Op Unary operation as template class with a type member named `type`. - */ -template class Op> -struct type_list_transform, Op> { - /*! @brief Resulting type list after applying the transform function. */ - using type = type_list::type...>; -}; + const common_type &handle() const noexcept { + return elem; + } -/** - * @brief Helper type. - * @tparam List Type list. - * @tparam Op Unary operation as template class with a type member named `type`. - */ -template class Op> -using type_list_transform_t = typename type_list_transform::type; + template + Type pools_as() const noexcept { + return pools; + } -/** - * @brief A class to use to push around lists of constant values, nothing more. - * @tparam Value Values provided by the value list. - */ -template -struct value_list { - /*! @brief Value list type. */ - using type = value_list; - /*! @brief Compile-time number of elements in the value list. */ - static constexpr auto size = sizeof...(Value); + template + Type filter_as() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + base_type elem; }; -/*! @brief Primary template isn't defined on purpose. */ -template -struct value_list_element; +} // namespace internal /** - * @brief Provides compile-time indexed access to the values of a value list. - * @tparam Index Index of the value to return. - * @tparam Value First value provided by the value list. - * @tparam Other Other values provided by the value list. + * Internal details not to be documented. + * @endcond */ -template -struct value_list_element> - : value_list_element> {}; /** - * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Value First value provided by the value list. - * @tparam Other Other values provided by the value list. + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. */ -template -struct value_list_element<0u, value_list> { - /*! @brief Searched value. */ - static constexpr auto value = Value; -}; +template +class basic_group; /** - * @brief Helper type. - * @tparam Index Index of the value to return. - * @tparam List Value list to search into. + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. */ -template -inline constexpr auto value_list_element_v = value_list_element::value; +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; -/** - * @brief Concatenates multiple value lists. - * @tparam Value Values provided by the first value list. - * @tparam Other Values provided by the second value list. - * @return A value list composed by the values of both the value lists. - */ -template -constexpr value_list operator+(value_list, value_list) { - return {}; -} + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; -/*! @brief Primary template isn't defined on purpose. */ -template -struct value_list_cat; + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template pools_as() : return_type{}; + } -/*! @brief Concatenates multiple value lists. */ -template<> -struct value_list_cat<> { - /*! @brief A value list composed by the values of all the value lists. */ - using type = value_list<>; -}; + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template filter_as() : return_type{}; + } -/** - * @brief Concatenates multiple value lists. - * @tparam Value Values provided by the first value list. - * @tparam Other Values provided by the second value list. - * @tparam List Other value lists, if any. - */ -template -struct value_list_cat, value_list, List...> { - /*! @brief A value list composed by the values of all the value lists. */ - using type = typename value_list_cat, List...>::type; -}; +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler, get_t...>, exclude_t...>>; -/** - * @brief Concatenates multiple value lists. - * @tparam Value Values provided by the value list. - */ -template -struct value_list_cat> { - /*! @brief A value list composed by the values of all the value lists. */ - using type = value_list; -}; + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} -/** - * @brief Helper type. - * @tparam List Value lists to concatenate. - */ -template -using value_list_cat_t = typename value_list_cat::type; + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} -/*! @brief Same as std::is_invocable, but with tuples. */ -template -struct is_applicable: std::false_type {}; + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } -/** - * @copybrief is_applicable - * @tparam Func A valid function type. - * @tparam Tuple Tuple-like type. - * @tparam Args The list of arguments to use to probe the function type. - */ -template class Tuple, typename... Args> -struct is_applicable>: std::is_invocable {}; + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } -/** - * @copybrief is_applicable - * @tparam Func A valid function type. - * @tparam Tuple Tuple-like type. - * @tparam Args The list of arguments to use to probe the function type. - */ -template class Tuple, typename... Args> -struct is_applicable>: std::is_invocable {}; + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Get); -/** - * @brief Helper variable template. - * @tparam Func A valid function type. - * @tparam Args The list of arguments to use to probe the function type. - */ -template -inline constexpr bool is_applicable_v = is_applicable::value; + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } -/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ -template -struct is_applicable_r: std::false_type {}; + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().size() : size_type{}; + } -/** - * @copybrief is_applicable_r - * @tparam Ret The type to which the return type of the function should be - * convertible. - * @tparam Func A valid function type. - * @tparam Args The list of arguments to use to probe the function type. - */ -template -struct is_applicable_r>: std::is_invocable_r {}; + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const noexcept { + return *this ? handle().capacity() : size_type{}; + } -/** - * @brief Helper variable template. - * @tparam Ret The type to which the return type of the function should be - * convertible. - * @tparam Func A valid function type. - * @tparam Args The list of arguments to use to probe the function type. - */ -template -inline constexpr bool is_applicable_r_v = is_applicable_r::value; + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } -/** - * @brief Provides the member constant `value` to true if a given type is - * complete, false otherwise. - * @tparam Type The type to test. - */ -template -struct is_complete: std::false_type {}; + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } -/*! @copydoc is_complete */ -template -struct is_complete>: std::true_type {}; + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? handle().begin() : iterator{}; + } -/** - * @brief Helper variable template. - * @tparam Type The type to test. - */ -template -inline constexpr bool is_complete_v = is_complete::value; + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } -/** - * @brief Provides the member constant `value` to true if a given type is an - * iterator, false otherwise. - * @tparam Type The type to test. - */ -template -struct is_iterator: std::false_type {}; + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().rend() : reverse_iterator{}; + } -namespace internal { + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } -template -struct has_iterator_category: std::false_type {}; + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } -template -struct has_iterator_category::iterator_category>>: std::true_type {}; + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } -} // namespace internal + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } -/** - * Internal details not to be documented. - * @endcond - */ + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } -/*! @copydoc is_iterator */ -template -struct is_iterator>, void>>> - : internal::has_iterator_category {}; + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } -/** - * @brief Helper variable template. - * @tparam Type The type to test. - */ -template -inline constexpr bool is_iterator_v = is_iterator::value; + /** + * @brief Returns the components assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the group results in + * undefined behavior. + * + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } -/** - * @brief Provides the member constant `value` to true if a given type is both - * an empty and non-final class, false otherwise. - * @tparam Type The type to test - */ -template -struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + /** + * @brief Returns the components assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the groups results in + * undefined behavior. + * + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); -/** - * @brief Helper variable template. - * @tparam Type The type to test. - */ -template -inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + if(*this) { + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; -/** - * @brief Provides the member constant `value` to true if `Type::is_transparent` - * is valid and denotes a type, false otherwise. - * @tparam Type The type to test. - */ -template -struct is_transparent: std::false_type {}; + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } + } -/*! @copydoc is_transparent */ -template -struct is_transparent>: std::true_type {}; + /** + * @brief Sort the shared pool of entities according to a given storage. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @param other The storage to use to impose the order. + */ + void sort_as(const common_type &other) const { + if(*this) { + descriptor->handle().sort_as(other); + } + } -/** - * @brief Helper variable template. - * @tparam Type The type to test. - */ -template -inline constexpr bool is_transparent_v = is_transparent::value; +private: + handler *descriptor; +}; /** - * @brief Provides the member constant `value` to true if a given type is - * equality comparable, false otherwise. - * @tparam Type The type to test. + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all components in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. */ -template -struct is_equality_comparable: std::false_type {}; +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; -namespace internal { + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template pools_as() : return_type{}; + } -template -struct has_tuple_size_value: std::false_type {}; + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template filter_as() : return_type{}; + } -template -struct has_tuple_size_value::value)>>: std::true_type {}; +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler...>, get_t...>, exclude_t...>>; -template -[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { - return (is_equality_comparable>::value && ...); -} + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} -template -[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { - return true; -} + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} -template -[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { - if constexpr(is_iterator_v) { - return true; - } else if constexpr(std::is_same_v) { - return maybe_equality_comparable(choice<0>); - } else { - return is_equality_comparable::value; + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); } -} -template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { - if constexpr(has_tuple_size_value::value) { - return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); - } else { - return maybe_equality_comparable(choice<1>); + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); } -} -} // namespace internal + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Owned) + sizeof...(Get); -/** - * Internal details not to be documented. - * @endcond - */ + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } -/*! @copydoc is_equality_comparable */ -template -struct is_equality_comparable() == std::declval())>> - : std::bool_constant(choice<2>)> {}; + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } -/** - * @brief Helper variable template. - * @tparam Type The type to test. - */ -template -inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } -/** - * @brief Transcribes the constness of a type to another type. - * @tparam To The type to which to transcribe the constness. - * @tparam From The type from which to transcribe the constness. - */ -template -struct constness_as { - /*! @brief The type resulting from the transcription of the constness. */ - using type = std::remove_const_t; -}; + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? (handle().end() - descriptor->length()) : iterator{}; + } -/*! @copydoc constness_as */ -template -struct constness_as { - /*! @brief The type resulting from the transcription of the constness. */ - using type = const To; -}; + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } -/** - * @brief Alias template to facilitate the transcription of the constness. - * @tparam To The type to which to transcribe the constness. - * @tparam From The type from which to transcribe the constness. - */ -template -using constness_as_t = typename constness_as::type; + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } -/** - * @brief Extracts the class of a non-static member object or function. - * @tparam Member A pointer to a non-static member object or function. - */ -template -class member_class { - static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; + } - template - static Class *clazz(Ret (Class::*)(Args...)); + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } - template - static Class *clazz(Ret (Class::*)(Args...) const); + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } - template - static Class *clazz(Type Class::*); + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } -public: - /*! @brief The class of the given non-static member object or function. */ - using type = std::remove_pointer_t()))>; -}; + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } -/** - * @brief Helper type. - * @tparam Member A pointer to a non-static member object or function. - */ -template -using member_class_t = typename member_class::type; + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } -/** - * @brief Extracts the n-th argument of a given function or member function. - * @tparam Index The index of the argument to extract. - * @tparam Candidate A valid function, member function or data member. - */ -template -class nth_argument { - template - static constexpr type_list pick_up(Ret (*)(Args...)); + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } - template - static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + /** + * @brief Returns the components assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the group results in + * undefined behavior. + * + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } - template - static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + /** + * @brief Returns the components assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the groups results in + * undefined behavior. + * + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); - template - static constexpr type_list pick_up(Type Class ::*); + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } -public: - /*! @brief N-th argument of the given function or member function. */ - using type = type_list_element_t; -}; + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); + } + } + } -/** - * @brief Helper type. - * @tparam Index The index of the argument to extract. - * @tparam Candidate A valid function, member function or data member. - */ -template -using nth_argument_t = typename nth_argument::type; + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return {{begin(), cpools}, {end(), cpools}}; + } -} // namespace entt + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are either owned types or not but still such that they are + * iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } -#endif + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + const auto cpools = pools(); -// #include "fwd.hpp" -#ifndef ENTT_SIGNAL_FWD_HPP -#define ENTT_SIGNAL_FWD_HPP + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; -#include + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } -namespace entt { + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; -template -class delegate; + std::apply(cb, cpools); + } -template> -class basic_dispatcher; +private: + handler *descriptor; +}; -template> -class emitter; +} // namespace entt -class connection; +#endif -struct scoped_connection; +// #include "entity/handle.hpp" +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP -template -class sink; +#include +#include +#include +#include +// #include "../core/iterator.hpp" -template> -class sigh; +// #include "../core/type_traits.hpp" -/*! @brief Alias declaration for the most common use case. */ -using dispatcher = basic_dispatcher<>; +// #include "entity.hpp" + +// #include "fwd.hpp" -/*! @brief Disambiguation tag for constructors and the like. */ -template -struct connect_arg_t { - /*! @brief Default constructor. */ - explicit connect_arg_t() = default; -}; + +namespace entt { /** - * @brief Constant of type connect_arg_t used to disambiguate calls. - * @tparam Candidate Element to connect (likely a free or member function). + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. */ -template -inline constexpr connect_arg_t connect_arg{}; -} // namespace entt +namespace internal { -#endif +template +class handle_storage_iterator final { + template + friend class handle_storage_iterator; + using underlying_type = std::remove_reference_t; + using entity_type = typename underlying_type::entity_type; -namespace entt { +public: + using value_type = typename std::iterator_traits::value_type; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr handle_storage_iterator() noexcept + : entt{null}, + it{}, + last{} {} -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ + constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept + : entt{value}, + it{from}, + last{to} { + while(it != last && !it->second.contains(entt)) { + ++it; + } + } -namespace internal { + constexpr handle_storage_iterator &operator++() noexcept { + while(++it != last && !it->second.contains(entt)) {} + return *this; + } -template -constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + constexpr handle_storage_iterator operator++(int) noexcept { + handle_storage_iterator orig = *this; + return ++(*this), orig; + } -template -constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + [[nodiscard]] constexpr reference operator*() const noexcept { + return *it; + } -template -constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } -template -constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + template + friend constexpr bool operator==(const handle_storage_iterator &, const handle_storage_iterator &) noexcept; -template -constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); +private: + entity_type entt; + It it; + It last; +}; -template -using function_pointer_t = decltype(function_pointer(std::declval()...)); +template +[[nodiscard]] constexpr bool operator==(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} -template -[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { - return std::index_sequence_for{}; +template +[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return !(lhs == rhs); } } // namespace internal @@ -17103,1253 +19281,1347 @@ template */ /** - * @brief Basic delegate implementation. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error unless the template parameter is a function type. - */ -template -class delegate; - -/** - * @brief Utility class to use to send around functions and members. - * - * Unmanaged delegate for function pointers and members. Users of this class are - * in charge of disconnecting instances before deleting them. + * @brief Non-owning handle to an entity. * - * A delegate can be used as a general purpose invoker without memory overhead - * for free functions possibly with payloads and bound or unbound members. + * Tiny wrapper around a registry and an entity. * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. + * @tparam Registry Basic registry type. + * @tparam Scope Types to which to restrict the scope of a handle. */ -template -class delegate { - template - [[nodiscard]] auto wrap(std::index_sequence) noexcept { - return [](const void *, Args... args) -> Ret { - [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); - }; +template +struct basic_handle { + /*! @brief Type of registry accepted by the handle. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename registry_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = typename registry_type::size_type; + + /*! @brief Constructs an invalid handle. */ + basic_handle() noexcept + : reg{}, + entt{null} {} + + /** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value A valid identifier. + */ + basic_handle(registry_type &ref, entity_type value) noexcept + : reg{&ref}, + entt{value} {} + + /** + * @brief Returns an iterable object to use to _visit_ a handle. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage.
+ * Returned storage are those that contain the entity associated with the + * handle. + * + * @return An iterable object to use to _visit_ the handle. + */ + [[nodiscard]] auto storage() const noexcept { + auto iterable = reg->storage(); + using iterator_type = internal::handle_storage_iterator; + return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}}; } - template - [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { - return [](const void *payload, Args... args) -> Ret { - [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); - }; + /** + * @brief Constructs a const handle from a non-const one. + * @tparam Other A valid entity type. + * @tparam Args Scope of the handle to construct. + * @return A const handle referring to the same registry and the same + * entity. + */ + template + operator basic_handle() const noexcept { + static_assert(std::is_same_v || std::is_same_v, Registry>, "Invalid conversion between different handles"); + static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); + + return reg ? basic_handle{*reg, entt} : basic_handle{}; } - template - [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { - return [](const void *payload, Args... args) -> Ret { - [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); - }; + /** + * @brief Converts a handle to its underlying entity. + * @return The contained identifier. + */ + [[nodiscard]] operator entity_type() const noexcept { + return entity(); } -public: - /*! @brief Function type of the contained target. */ - using function_type = Ret(const void *, Args...); - /*! @brief Function type of the delegate. */ - using type = Ret(Args...); - /*! @brief Return type of the delegate. */ - using result_type = Ret; + /** + * @brief Checks if a handle refers to non-null registry pointer and entity. + * @return True if the handle refers to non-null registry and entity, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return reg && reg->valid(entt); + } - /*! @brief Default constructor. */ - delegate() noexcept - : instance{nullptr}, - fn{nullptr} {} + /** + * @brief Checks if a handle refers to a valid entity or not. + * @return True if the handle refers to a valid entity, false otherwise. + */ + [[nodiscard]] bool valid() const { + return reg->valid(entt); + } /** - * @brief Constructs a delegate with a given object or payload, if any. - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload, if any. - * @param value_or_instance Optional valid object that fits the purpose. + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. */ - template - delegate(connect_arg_t, Type &&...value_or_instance) noexcept { - connect(std::forward(value_or_instance)...); + [[nodiscard]] registry_type *registry() const noexcept { + return reg; } /** - * @brief Constructs a delegate and connects an user defined function with - * optional payload. - * @param function Function to connect to the delegate. - * @param payload User defined arbitrary data. + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. */ - delegate(function_type *function, const void *payload = nullptr) noexcept { - connect(function, payload); + [[nodiscard]] entity_type entity() const noexcept { + return entt; + } + + /*! @brief Destroys the entity associated with a handle. */ + void destroy() { + reg->destroy(std::exchange(entt, null)); } /** - * @brief Connects a free function or an unbound member to a delegate. - * @tparam Candidate Function or member to connect to the delegate. + * @brief Destroys the entity associated with a handle. + * @param version A desired version upon destruction. */ - template - void connect() noexcept { - instance = nullptr; + void destroy(const version_type version) { + reg->destroy(std::exchange(entt, null), version); + } - if constexpr(std::is_invocable_r_v) { - fn = [](const void *, Args... args) -> Ret { - return Ret(std::invoke(Candidate, std::forward(args)...)); - }; - } else if constexpr(std::is_member_pointer_v) { - fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); - } else { - fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); - } + /** + * @brief Assigns the given component to a handle. + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace(entt, std::forward(args)...); } /** - * @brief Connects a free function with payload or a bound member to a - * delegate. - * - * The delegate isn't responsible for the connected object or the payload. - * Users must always guarantee that the lifetime of the instance overcomes - * the one of the delegate.
- * When used to connect a free function with payload, its signature must be - * such that the instance is the first argument before the ones used to - * define the delegate itself. - * - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid reference that fits the purpose. + * @brief Assigns or replaces the given component for a handle. + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. */ - template - void connect(Type &value_or_instance) noexcept { - instance = &value_or_instance; + template + decltype(auto) emplace_or_replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace_or_replace(entt, std::forward(args)...); + } - if constexpr(std::is_invocable_r_v) { - fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast *>(payload)); - return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); - }; - } else { - fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); - } + /** + * @brief Patches the given component for a handle. + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(Func &&...func) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template patch(entt, std::forward(func)...); } /** - * @brief Connects a free function with payload or a bound member to a - * delegate. - * - * @sa connect(Type &) - * - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid pointer that fits the purpose. + * @brief Replaces the given component for a handle. + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. */ - template - void connect(Type *value_or_instance) noexcept { - instance = value_or_instance; + template + decltype(auto) replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template replace(entt, std::forward(args)...); + } - if constexpr(std::is_invocable_r_v) { - fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast *>(payload)); - return Ret(std::invoke(Candidate, curr, std::forward(args)...)); - }; - } else { - fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); - } + /** + * @brief Removes the given components from a handle. + * @tparam Component Types of components to remove. + * @return The number of components actually removed. + */ + template + size_type remove() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template remove(entt); } /** - * @brief Connects an user defined function with optional payload to a - * delegate. - * - * The delegate isn't responsible for the connected object or the payload. - * Users must always guarantee that the lifetime of an instance overcomes - * the one of the delegate.
- * The payload is returned as the first argument to the target function in - * all cases. - * - * @param function Function to connect to the delegate. - * @param payload User defined arbitrary data. + * @brief Erases the given components from a handle. + * @tparam Component Types of components to erase. */ - void connect(function_type *function, const void *payload = nullptr) noexcept { - ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); - instance = payload; - fn = function; + template + void erase() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + reg->template erase(entt); } /** - * @brief Resets a delegate. - * - * After a reset, a delegate cannot be invoked anymore. + * @brief Checks if a handle has all the given components. + * @tparam Component Components for which to perform the check. + * @return True if the handle has all the components, false otherwise. */ - void reset() noexcept { - instance = nullptr; - fn = nullptr; + template + [[nodiscard]] decltype(auto) all_of() const { + return reg->template all_of(entt); } /** - * @brief Returns the instance or the payload linked to a delegate, if any. - * @return An opaque pointer to the underlying data. + * @brief Checks if a handle has at least one of the given components. + * @tparam Component Components for which to perform the check. + * @return True if the handle has at least one of the given components, + * false otherwise. */ - [[nodiscard]] const void *data() const noexcept { - return instance; + template + [[nodiscard]] decltype(auto) any_of() const { + return reg->template any_of(entt); } /** - * @brief Triggers a delegate. - * - * The delegate invokes the underlying function and returns the result. - * - * @warning - * Attempting to trigger an invalid delegate results in undefined - * behavior. - * - * @param args Arguments to use to invoke the underlying function. - * @return The value returned by the underlying function. + * @brief Returns references to the given components for a handle. + * @tparam Component Types of components to get. + * @return References to the components owned by the handle. */ - Ret operator()(Args... args) const { - ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); - return fn(instance, std::forward(args)...); + template + [[nodiscard]] decltype(auto) get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template get(entt); } /** - * @brief Checks whether a delegate actually stores a listener. - * @return False if the delegate is empty, true otherwise. + * @brief Returns a reference to the given component for a handle. + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the handle. */ - [[nodiscard]] explicit operator bool() const noexcept { - // no need to also test instance - return !(fn == nullptr); + template + [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template get_or_emplace(entt, std::forward(args)...); } /** - * @brief Compares the contents of two delegates. - * @param other Delegate with which to compare. - * @return False if the two contents differ, true otherwise. + * @brief Returns pointers to the given components for a handle. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the handle. */ - [[nodiscard]] bool operator==(const delegate &other) const noexcept { - return fn == other.fn && instance == other.instance; + template + [[nodiscard]] auto try_get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template try_get(entt); + } + + /** + * @brief Checks if a handle has components assigned. + * @return True if the handle has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan() const { + return reg->orphan(entt); } -private: - const void *instance; - function_type *fn; -}; +private: + registry_type *reg; + entity_type entt; +}; + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same registry and the same + * entity, false otherwise. + */ +template +[[nodiscard]] bool operator==(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); +} + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry and the same + * entity, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace entt + +#endif + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif -/** - * @brief Compares the contents of two delegates. - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - * @param lhs A valid delegate object. - * @param rhs A valid delegate object. - * @return True if the two contents differ, false otherwise. - */ -template -[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { - return !(lhs == rhs); -} +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif -/** - * @brief Deduction guide. - * @tparam Candidate Function or member to connect to the delegate. - */ -template -delegate(connect_arg_t) -> delegate>>; +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif -/** - * @brief Deduction guide. - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload. - */ -template -delegate(connect_arg_t, Type &&) -> delegate>>; +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif -/** - * @brief Deduction guide. - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - */ -template -delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif -} // namespace entt +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type #endif -// #include "fwd.hpp" +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif -namespace entt { +#endif -/** - * @brief Sink class. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error unless the template parameter is a function type. - * - * @tparam Type A valid signal handler type. - */ -template -class sink; +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP -/** - * @brief Unmanaged signal handler. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error unless the template parameter is a function type. - * - * @tparam Type A valid function type. - * @tparam Allocator Type of allocator used to manage memory and elements. - */ -template -class sigh; +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H -/** - * @brief Unmanaged signal handler. - * - * It works directly with references to classes and pointers to member functions - * as well as pointers to free functions. Users of this class are in charge of - * disconnecting instances before deleting them. - * - * This class serves mainly two purposes: - * - * * Creating signals to use later to notify a bunch of listeners. - * * Collecting results from a set of functions like in a voting system. - * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - * @tparam Allocator Type of allocator used to manage memory and elements. - */ -template -class sigh { - /*! @brief A sink is allowed to modify a signal. */ - friend class sink>; +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H - using alloc_traits = std::allocator_traits; - using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H -public: - /*! @brief Allocator type. */ - using allocator_type = Allocator; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Sink type. */ - using sink_type = sink>; +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) - /*! @brief Default constructor. */ - sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) - : sigh{allocator_type{}} {} +#endif - /** - * @brief Constructs a signal handler with a given allocator. - * @param allocator The allocator to use. - */ - explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) - : calls{allocator} {} - /** - * @brief Copy constructor. - * @param other The instance to copy from. - */ - sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) - : calls{other.calls} {} +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 - /** - * @brief Allocator-extended copy constructor. - * @param other The instance to copy from. - * @param allocator The allocator to use. - */ - sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) - : calls{other.calls, allocator} {} +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) - /** - * @brief Move constructor. - * @param other The instance to move from. - */ - sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) - : calls{std::move(other.calls)} {} +#endif - /** - * @brief Allocator-extended move constructor. - * @param other The instance to move from. - * @param allocator The allocator to use. - */ - sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) - : calls{std::move(other.calls), allocator} {} - /** - * @brief Copy assignment operator. - * @param other The instance to copy from. - * @return This signal handler. - */ - sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { - calls = other.calls; - return *this; - } +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif - /** - * @brief Move assignment operator. - * @param other The instance to move from. - * @return This signal handler. - */ - sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { - calls = std::move(other.calls); - return *this; - } +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif - /** - * @brief Exchanges the contents with those of a given signal handler. - * @param other Signal handler to exchange the content with. - */ - void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { - using std::swap; - swap(calls, other.calls); - } +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif - /** - * @brief Returns the associated allocator. - * @return The associated allocator. - */ - [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { - return calls.get_allocator(); - } +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif - /** - * @brief Number of listeners connected to the signal. - * @return Number of listeners currently connected. - */ - [[nodiscard]] size_type size() const noexcept { - return calls.size(); - } +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif - /** - * @brief Returns false if at least a listener is connected to the signal. - * @return True if the signal has no listeners connected, false otherwise. - */ - [[nodiscard]] bool empty() const noexcept { - return calls.empty(); - } +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif - /** - * @brief Triggers a signal. - * - * All the listeners are notified. Order isn't guaranteed. - * - * @param args Arguments to use to invoke listeners. - */ - void publish(Args... args) const { - for(auto &&call: std::as_const(calls)) { - call(args...); - } - } +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif - /** - * @brief Collects return values from the listeners. - * - * The collector must expose a call operator with the following properties: - * - * * The return type is either `void` or such that it's convertible to - * `bool`. In the second case, a true value will stop the iteration. - * * The list of parameters is empty if `Ret` is `void`, otherwise it - * contains a single element such that `Ret` is convertible to it. - * - * @tparam Func Type of collector to use, if any. - * @param func A valid function object. - * @param args Arguments to use to invoke listeners. - */ - template - void collect(Func func, Args... args) const { - for(auto &&call: calls) { - if constexpr(std::is_void_v) { - if constexpr(std::is_invocable_r_v) { - call(args...); - if(func()) { - break; - } - } else { - call(args...); - func(); - } - } else { - if constexpr(std::is_invocable_r_v) { - if(func(call(args...))) { - break; - } - } else { - func(call(args...)); - } - } - } - } +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); -private: - container_type calls; -}; +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif -/** - * @brief Connection class. - * - * Opaque object the aim of which is to allow users to release an already - * estabilished connection without having to keep a reference to the signal or - * the sink that generated it. - */ -class connection { - /*! @brief A sink is allowed to create connection objects. */ - template - friend class sink; +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif - connection(delegate fn, void *ref) - : disconnect{fn}, signal{ref} {} +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif -public: - /*! @brief Default constructor. */ - connection() - : disconnect{}, - signal{} {} +#endif - /** - * @brief Checks whether a connection is properly initialized. - * @return True if the connection is properly initialized, false otherwise. - */ - [[nodiscard]] explicit operator bool() const noexcept { - return static_cast(disconnect); - } +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP - /*! @brief Breaks the connection. */ - void release() { - if(disconnect) { - disconnect(signal); - disconnect.reset(); - } - } +#include +// #include "../config/config.h" -private: - delegate disconnect; - void *signal; -}; -/** - * @brief Scoped connection class. - * - * Opaque object the aim of which is to allow users to release an already - * estabilished connection without having to keep a reference to the signal or - * the sink that generated it.
- * A scoped connection automatically breaks the link between the two objects - * when it goes out of scope. - */ -struct scoped_connection { - /*! @brief Default constructor. */ - scoped_connection() = default; +namespace entt { - /** - * @brief Constructs a scoped connection from a basic connection. - * @param other A valid connection object. - */ - scoped_connection(const connection &other) - : conn{other} {} +template +class basic_any; - /*! @brief Default copy constructor, deleted on purpose. */ - scoped_connection(const scoped_connection &) = delete; +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; - /** - * @brief Move constructor. - * @param other The scoped connection to move from. - */ - scoped_connection(scoped_connection &&other) noexcept - : conn{std::exchange(other.conn, {})} {} +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; - /*! @brief Automatically breaks the link on destruction. */ - ~scoped_connection() { - conn.release(); - } +} // namespace entt - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This scoped connection. - */ - scoped_connection &operator=(const scoped_connection &) = delete; +#endif - /** - * @brief Move assignment operator. - * @param other The scoped connection to move from. - * @return This scoped connection. - */ - scoped_connection &operator=(scoped_connection &&other) noexcept { - conn = std::exchange(other.conn, {}); - return *this; - } - /** - * @brief Acquires a connection. - * @param other The connection object to acquire. - * @return This scoped connection. - */ - scoped_connection &operator=(connection other) { - conn = std::move(other); - return *this; - } +namespace entt { - /** - * @brief Checks whether a scoped connection is properly initialized. - * @return True if the connection is properly initialized, false otherwise. - */ - [[nodiscard]] explicit operator bool() const noexcept { - return static_cast(conn); - } +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // Unfortunately, doxygen cannot parse such a construct. + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; - /*! @brief Breaks the connection. */ - void release() { - conn.release(); - } +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; -private: - connection conn; -}; +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; /** - * @brief Sink class. - * - * A sink is used to connect listeners to signals and to disconnect them.
- * The function type for a listener is the one of the signal to which it - * belongs. - * - * The clear separation between a signal and a sink permits to store the former - * as private data member without exposing the publish functionality to the - * users of the class. + * @brief Identity type trait. * - * @warning - * Lifetime of a sink must not overcome that of the signal to which it refers. - * In any other case, attempting to use a sink results in undefined behavior. + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Type A type. */ -template -class sink> { - using signal_type = sigh; - using difference_type = typename signal_type::container_type::difference_type; - - template - static void release(Type value_or_instance, void *signal) { - sink{*static_cast(signal)}.disconnect(value_or_instance); - } +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; - template - static void release(void *signal) { - sink{*static_cast(signal)}.disconnect(); - } +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; - auto before(delegate call) { - const auto &calls = signal->calls; - const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; - sink other{*this}; - other.offset = calls.cend() - it; - return other; - } +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; -public: - /** - * @brief Constructs a sink that is allowed to modify a given signal. - * @param ref A valid reference to a signal object. - */ - sink(sigh &ref) noexcept - : offset{}, - signal{&ref} {} +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; - /** - * @brief Returns false if at least a listener is connected to the sink. - * @return True if the sink has no listeners connected, false otherwise. - */ - [[nodiscard]] bool empty() const noexcept { - return signal->calls.empty(); - } +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; - /** - * @brief Returns a sink that connects before a given free function or an - * unbound member. - * @tparam Function A valid free function pointer. - * @return A properly initialized sink object. - */ - template - [[nodiscard]] sink before() { - delegate call{}; - call.template connect(); - return before(std::move(call)); - } +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; - /** - * @brief Returns a sink that connects before a free function with payload - * or a bound member. - * @tparam Candidate Member or free function to look for. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - * @return A properly initialized sink object. - */ - template - [[nodiscard]] sink before(Type &&value_or_instance) { - delegate call{}; - call.template connect(value_or_instance); - return before(std::move(call)); - } +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; - /** - * @brief Returns a sink that connects before a given instance or specific - * payload. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - * @return A properly initialized sink object. - */ - template>, void>, sink>> - [[nodiscard]] sink before(Type &value_or_instance) { - return before(&value_or_instance); - } +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; - /** - * @brief Returns a sink that connects before a given instance or specific - * payload. - * @param value_or_instance A valid pointer that fits the purpose. - * @return A properly initialized sink object. - */ - [[nodiscard]] sink before(const void *value_or_instance) { - sink other{*this}; +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; - if(value_or_instance) { - const auto &calls = signal->calls; - const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { - return delegate.data() == value_or_instance; - }); +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; - other.offset = calls.cend() - it; - } +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; - return other; - } +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; - /** - * @brief Returns a sink that connects before anything else. - * @return A properly initialized sink object. - */ - [[nodiscard]] sink before() { - sink other{*this}; - other.offset = signal->calls.size(); - return other; - } +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; - /** - * @brief Connects a free function (with or without payload), a bound or an - * unbound member to a signal. - * - * The signal isn't responsible for the connected object or the payload, if - * any. Users must guarantee that the lifetime of the instance overcomes the - * one of the signal. On the other side, the signal handler performs - * checks to avoid multiple connections for the same function.
- * When used to connect a free function with payload, its signature must be - * such that the instance is the first argument before the ones used to - * define the signal itself. - * - * @tparam Candidate Function or member to connect to the signal. - * @tparam Type Type of class or type of payload, if any. - * @param value_or_instance A valid object that fits the purpose, if any. - * @return A properly initialized connection object. - */ - template - connection connect(Type &&...value_or_instance) { - disconnect(value_or_instance...); +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; - delegate call{}; - call.template connect(value_or_instance...); - signal->calls.insert(signal->calls.end() - offset, std::move(call)); +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; - delegate conn{}; - conn.template connect<&release>(value_or_instance...); - return {std::move(conn), signal}; - } +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; - /** - * @brief Disconnects a free function (with or without payload), a bound or - * an unbound member from a signal. - * @tparam Candidate Function or member to disconnect from the signal. - * @tparam Type Type of class or type of payload, if any. - * @param value_or_instance A valid object that fits the purpose, if any. - */ - template - void disconnect(Type &&...value_or_instance) { - auto &calls = signal->calls; - delegate call{}; - call.template connect(value_or_instance...); - calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); - } +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; - /** - * @brief Disconnects free functions with payload or bound members from a - * signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - */ - template>, void>>> - void disconnect(Type &value_or_instance) { - disconnect(&value_or_instance); - } +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; - /** - * @brief Disconnects free functions with payload or bound members from a - * signal. - * @param value_or_instance A valid object that fits the purpose. - */ - void disconnect(const void *value_or_instance) { - if(value_or_instance) { - auto &calls = signal->calls; - auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; }; - calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end()); - } - } +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} - /*! @brief Disconnects all the listeners from a signal. */ - void disconnect() { - signal->calls.clear(); - } +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; -private: - difference_type offset; - signal_type *signal; +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; }; /** - * @brief Deduction guide. - * - * It allows to deduce the signal handler type of a sink directly from the - * signal it refers to. - * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - * @tparam Allocator Type of allocator used to manage memory and elements. + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. */ -template -sink(sigh &) -> sink>; +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; -} // namespace entt +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; -#endif +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; -// #include "fwd.hpp" +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::is_same_v || ...), + typename type_list_unique>::type, + type_list_cat_t, typename type_list_unique>::type>>; +}; -namespace entt { +/*! @brief Removes duplicates types from a type list. */ +template<> +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = type_list<>; +}; /** - * @brief Mixin type used to add signal support to storage types. - * - * The function type of a listener is equivalent to: - * - * @code{.cpp} - * void(basic_registry &, entity_type); - * @endcode - * - * This applies to all signals made available. - * - * @tparam Type The type of the underlying storage. + * @brief Helper type. + * @tparam Type A type list. */ template -class sigh_storage_mixin final: public Type { - using basic_registry_type = basic_registry; - using sigh_type = sigh; - using basic_iterator = typename Type::basic_iterator; +using type_list_unique_t = typename type_list_unique::type; - void pop(basic_iterator first, basic_iterator last) override { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); +/** + * @brief Provides the member constant `value` to true if a type list contains a + * given type, false otherwise. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +struct type_list_contains; - for(; first != last; ++first) { - const auto entt = *first; - destruction.publish(*owner, entt); - const auto it = Type::find(entt); - Type::pop(it, it + 1u); - } - } +/** + * @copybrief type_list_contains + * @tparam Type Types provided by the type list. + * @tparam Other Type to look for. + */ +template +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; - basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::try_emplace(entt, force_back, value); - construction.publish(*owner, entt); - return Type::find(entt); - } +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; -public: - /*! @brief Allocator type. */ - using allocator_type = typename Type::allocator_type; - /*! @brief Underlying entity identifier. */ - using entity_type = typename Type::entity_type; - /*! @brief Expected registry type. */ - using registry_type = basic_registry_type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; - /*! @brief Default constructor. */ - sigh_storage_mixin() - : sigh_storage_mixin{allocator_type{}} {} +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; - /** - * @brief Constructs an empty storage with a given allocator. - * @param allocator The allocator to use. - */ - explicit sigh_storage_mixin(const allocator_type &allocator) - : Type{allocator}, - owner{}, - construction{allocator}, - destruction{allocator}, - update{allocator} {} +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; - /** - * @brief Move constructor. - * @param other The instance to move from. - */ - sigh_storage_mixin(sigh_storage_mixin &&other) noexcept - : Type{std::move(other)}, - owner{other.owner}, - construction{std::move(other.construction)}, - destruction{std::move(other.destruction)}, - update{std::move(other.update)} {} +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; - /** - * @brief Allocator-extended move constructor. - * @param other The instance to move from. - * @param allocator The allocator to use. - */ - sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept - : Type{std::move(other), allocator}, - owner{other.owner}, - construction{std::move(other.construction), allocator}, - destruction{std::move(other.destruction), allocator}, - update{std::move(other.update), allocator} {} +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; - /** - * @brief Move assignment operator. - * @param other The instance to move from. - * @return This storage. - */ - sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept { - Type::operator=(std::move(other)); - owner = other.owner; - construction = std::move(other.construction); - destruction = std::move(other.destruction); - update = std::move(other.update); - return *this; - } +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; - /** - * @brief Exchanges the contents with those of a given storage. - * @param other Storage to exchange the content with. - */ - void swap(sigh_storage_mixin &other) { - using std::swap; - Type::swap(other); - swap(owner, other.owner); - swap(construction, other.construction); - swap(destruction, other.destruction); - swap(update, other.update); - } +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; - /** - * @brief Returns a sink object. - * - * The sink returned by this function can be used to receive notifications - * whenever a new instance is created and assigned to an entity.
- * Listeners are invoked after the object has been assigned to the entity. - * - * @sa sink - * - * @return A temporary sink object. - */ - [[nodiscard]] auto on_construct() noexcept { - return sink{construction}; - } +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; - /** - * @brief Returns a sink object. - * - * The sink returned by this function can be used to receive notifications - * whenever an instance is explicitly updated.
- * Listeners are invoked after the object has been updated. - * - * @sa sink - * - * @return A temporary sink object. - */ - [[nodiscard]] auto on_update() noexcept { - return sink{update}; - } +/** + * @brief Helper type. + * @tparam Index Index of the value to return. + * @tparam List Value list to search into. + */ +template +inline constexpr auto value_list_element_v = value_list_element::value; - /** - * @brief Returns a sink object. - * - * The sink returned by this function can be used to receive notifications - * whenever an instance is removed from an entity and thus destroyed.
- * Listeners are invoked before the object has been removed from the entity. - * - * @sa sink - * - * @return A temporary sink object. - */ - [[nodiscard]] auto on_destroy() noexcept { - return sink{destruction}; - } +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; - /** - * @brief Assigns entities to a storage. - * @tparam Args Types of arguments to use to construct the object. - * @param entt A valid identifier. - * @param args Parameters to use to initialize the object. - * @return A reference to the newly created object. - */ - template - decltype(auto) emplace(const entity_type entt, Args &&...args) { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::emplace(entt, std::forward(args)...); - construction.publish(*owner, entt); - return this->get(entt); - } +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; - /** - * @brief Patches the given instance for an entity. - * @tparam Func Types of the function objects to invoke. - * @param entt A valid identifier. - * @param func Valid function objects. - * @return A reference to the patched instance. - */ - template - decltype(auto) patch(const entity_type entt, Func &&...func) { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::patch(entt, std::forward(func)...); - update.publish(*owner, entt); - return this->get(entt); - } +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; - /** - * @brief Assigns entities to a storage. - * @tparam It Type of input iterator. - * @tparam Args Types of arguments to use to construct the objects assigned - * to the entities. - * @param first An iterator to the first element of the range of entities. - * @param last An iterator past the last element of the range of entities. - * @param args Parameters to use to initialize the objects assigned to the - * entities. - */ - template - void insert(It first, It last, Args &&...args) { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::insert(first, last, std::forward(args)...); +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; - for(auto it = construction.empty() ? last : first; it != last; ++it) { - construction.publish(*owner, *it); - } - } +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; - /** - * @brief Forwards variables to derived classes, if any. - * @param value A variable wrapped in an opaque container. - */ - void bind(any value) noexcept final { - auto *reg = any_cast(&value); - owner = reg ? reg : owner; - Type::bind(std::move(value)); - } +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @return A value list composed by the values of both the value lists. + */ +template +constexpr value_list operator+(value_list, value_list) { + return {}; +} -private: - basic_registry_type *owner; - sigh_type construction; - sigh_type destruction; - sigh_type update; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_cat; + +/*! @brief Concatenates multiple value lists. */ +template<> +struct value_list_cat<> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list<>; }; -} // namespace entt +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + * @tparam List Other value lists, if any. + */ +template +struct value_list_cat, value_list, List...> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = typename value_list_cat, List...>::type; +}; -#endif +/** + * @brief Concatenates multiple value lists. + * @tparam Value Values provided by the value list. + */ +template +struct value_list_cat> { + /*! @brief A value list composed by the values of all the value lists. */ + using type = value_list; +}; +/** + * @brief Helper type. + * @tparam List Value lists to concatenate. + */ +template +using value_list_cat_t = typename value_list_cat::type; -namespace entt { +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; /** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; -namespace internal { +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; -template -class storage_iterator final { - friend storage_iterator; +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; - using container_type = std::remove_const_t; - using alloc_traits = std::allocator_traits; - using comp_traits = component_traits>; +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; - using iterator_traits = std::iterator_traits, - typename alloc_traits::template rebind_traits::element_type>::const_pointer, - typename alloc_traits::template rebind_traits::element_type>::pointer>>; +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; -public: - using value_type = typename iterator_traits::value_type; - using pointer = typename iterator_traits::pointer; - using reference = typename iterator_traits::reference; - using difference_type = typename iterator_traits::difference_type; - using iterator_category = std::random_access_iterator_tag; +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; - constexpr storage_iterator() noexcept = default; +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; - constexpr storage_iterator(Container *ref, const difference_type idx) noexcept - : packed{ref}, - offset{idx} {} +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; - template, typename = std::enable_if_t> - constexpr storage_iterator(const storage_iterator> &other) noexcept - : storage_iterator{other.packed, other.offset} {} +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; - constexpr storage_iterator &operator++() noexcept { - return --offset, *this; - } +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; - constexpr storage_iterator operator++(int) noexcept { - storage_iterator orig = *this; - return ++(*this), orig; - } +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; - constexpr storage_iterator &operator--() noexcept { - return ++offset, *this; - } +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; - constexpr storage_iterator operator--(int) noexcept { - storage_iterator orig = *this; - return operator--(), orig; - } +/** + * @copybrief is_applicable + * @tparam Func A valid function type. + * @tparam Tuple Tuple-like type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template class Tuple, typename... Args> +struct is_applicable>: std::is_invocable {}; - constexpr storage_iterator &operator+=(const difference_type value) noexcept { - offset -= value; - return *this; - } +/** + * @brief Helper variable template. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_v = is_applicable::value; - constexpr storage_iterator operator+(const difference_type value) const noexcept { - storage_iterator copy = *this; - return (copy += value); - } +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_type {}; - constexpr storage_iterator &operator-=(const difference_type value) noexcept { - return (*this += -value); - } +/** + * @copybrief is_applicable_r + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +struct is_applicable_r>: std::is_invocable_r {}; - constexpr storage_iterator operator-(const difference_type value) const noexcept { - return (*this + -value); - } +/** + * @brief Helper variable template. + * @tparam Ret The type to which the return type of the function should be + * convertible. + * @tparam Func A valid function type. + * @tparam Args The list of arguments to use to probe the function type. + */ +template +inline constexpr bool is_applicable_r_v = is_applicable_r::value; - [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { - const auto pos = index() - value; - return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; - } +/** + * @brief Provides the member constant `value` to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: std::false_type {}; - [[nodiscard]] constexpr pointer operator->() const noexcept { - const auto pos = index(); - return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); - } +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; - [[nodiscard]] constexpr reference operator*() const noexcept { - return *operator->(); - } +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; - [[nodiscard]] constexpr difference_type index() const noexcept { - return offset - 1; - } +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; -private: - Container *packed; - difference_type offset; -}; +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return rhs.index() - lhs.index(); -} +namespace internal { -template -[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return lhs.index() == rhs.index(); -} +template +struct has_iterator_category: std::false_type {}; -template -[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return !(lhs == rhs); -} +template +struct has_iterator_category::iterator_category>>: std::true_type {}; -template -[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return lhs.index() > rhs.index(); -} +} // namespace internal -template -[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return lhs.index() < rhs.index(); -} +/** + * Internal details not to be documented. + * @endcond + */ -template -[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return !(lhs > rhs); -} +/*! @copydoc is_iterator */ +template +struct is_iterator>, void>>> + : internal::has_iterator_category {}; -template -[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return !(lhs < rhs); -} +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; -template -class extended_storage_iterator final { - template - friend class extended_storage_iterator; +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; -public: - using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); - using pointer = input_iterator_pointer; - using reference = value_type; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; - constexpr extended_storage_iterator() - : it{} {} +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; - constexpr extended_storage_iterator(It base, Other... other) - : it{base, other...} {} +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; - template && ...) && (std::is_constructible_v && ...)>> - constexpr extended_storage_iterator(const extended_storage_iterator &other) - : it{other.it} {} +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; - constexpr extended_storage_iterator &operator++() noexcept { - return ++std::get(it), (++std::get(it), ...), *this; - } +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::false_type {}; - constexpr extended_storage_iterator operator++(int) noexcept { - extended_storage_iterator orig = *this; - return ++(*this), orig; - } +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ - [[nodiscard]] constexpr pointer operator->() const noexcept { - return operator*(); - } +namespace internal { - [[nodiscard]] constexpr reference operator*() const noexcept { - return {*std::get(it), *std::get(it)...}; - } +template +struct has_tuple_size_value: std::false_type {}; - template - friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; +template +struct has_tuple_size_value::value)>>: std::true_type {}; -private: - std::tuple it; -}; +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (is_equality_comparable>::value && ...); +} -template -[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { - return std::get<0>(lhs.it) == std::get<0>(rhs.it); +template +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { + return true; } -template -[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { - return !(lhs == rhs); +template +[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { + if constexpr(is_iterator_v) { + return true; + } else if constexpr(std::is_same_v) { + return maybe_equality_comparable(choice<0>); + } else { + return is_equality_comparable::value; + } +} + +template +[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { + if constexpr(has_tuple_size_value::value) { + return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(choice<1>); + } } } // namespace internal @@ -18359,731 +20631,538 @@ template * @endcond */ -/** - * @brief Basic storage implementation. - * - * Internal data structures arrange elements to maximize performance. There are - * no guarantees that objects are returned in the insertion order when iterate - * a storage. Do not make assumption on the order in any case. - * - * @warning - * Empty types aren't explicitly instantiated. Therefore, many of the functions - * normally available for non-empty types will not be available for empty ones. - * - * @tparam Type Type of objects assigned to the entities. - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Allocator Type of allocator used to manage memory and elements. - */ -template -class basic_storage: public basic_sparse_set::template rebind_alloc> { - using alloc_traits = std::allocator_traits; - static_assert(std::is_same_v, "Invalid value type"); - using underlying_type = basic_sparse_set>; - using container_type = std::vector>; - using comp_traits = component_traits; - - static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); - - [[nodiscard]] auto &element_at(const std::size_t pos) const { - return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; - } - - auto assure_at_least(const std::size_t pos) { - auto &&container = packed.first(); - const auto idx = pos / comp_traits::page_size; - - if(!(idx < container.size())) { - auto curr = container.size(); - container.resize(idx + 1u, nullptr); - - ENTT_TRY { - for(const auto last = container.size(); curr < last; ++curr) { - container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size); - } - } - ENTT_CATCH { - container.resize(curr); - ENTT_THROW; - } - } +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::bool_constant(choice<2>)> {}; - return container[idx] + fast_mod(pos, comp_traits::page_size); - } +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; - template - auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { - const auto it = base_type::try_emplace(entt, force_back); +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; - ENTT_TRY { - auto elem = assure_at_least(static_cast(it.index())); - entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward(args)...); - } - ENTT_CATCH { - base_type::pop(it, it + 1u); - ENTT_THROW; - } +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; - return it; - } +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; - void shrink_to_size(const std::size_t sz) { - for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { - if constexpr(comp_traits::in_place_delete) { - if(base_type::at(pos) != tombstone) { - std::destroy_at(std::addressof(element_at(pos))); - } - } else { - std::destroy_at(std::addressof(element_at(pos))); - } - } +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; - auto &&container = packed.first(); - auto page_allocator{packed.second()}; - const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size; +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); - for(auto pos = from, last = container.size(); pos < last; ++pos) { - alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size); - } + template + static Class *clazz(Ret (Class::*)(Args...)); - container.resize(from); - } + template + static Class *clazz(Ret (Class::*)(Args...) const); -private: - const void *get_at(const std::size_t pos) const final { - return std::addressof(element_at(pos)); - } + template + static Class *clazz(Type Class::*); - void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) final { - // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy - ENTT_ASSERT((lhs + 1u) && !is_pinned_type_v, "Pinned type"); +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; - if constexpr(!is_pinned_type_v) { - using std::swap; - swap(element_at(lhs), element_at(rhs)); - } - } +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; - void move_element([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) final { - // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy - ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); - if constexpr(!is_pinned_type_v) { - auto &elem = element_at(from); - entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem)); - std::destroy_at(std::addressof(elem)); - } - } + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); -protected: - /*! @brief Random access iterator type. */ - using basic_iterator = typename underlying_type::basic_iterator; + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); - /** - * @brief Erases entities from a sparse set. - * @param first An iterator to the first element of the range of entities. - * @param last An iterator past the last element of the range of entities. - */ - void pop(basic_iterator first, basic_iterator last) override { - for(; first != last; ++first) { - // cannot use first.index() because it would break with cross iterators - auto &elem = element_at(base_type::index(*first)); + template + static constexpr type_list pick_up(Type Class ::*); - if constexpr(comp_traits::in_place_delete) { - base_type::in_place_pop(first); - std::destroy_at(std::addressof(elem)); - } else { - auto &other = element_at(base_type::size() - 1u); - // destroying on exit allows reentrant destructors - [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); - std::destroy_at(std::addressof(other)); - base_type::swap_and_pop(first); - } - } - } +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; - /** - * @brief Assigns an entity to a storage. - * @param entt A valid identifier. - * @param value Optional opaque value. - * @param force_back Force back insertion. - * @return Iterator pointing to the emplaced element. - */ - basic_iterator try_emplace([[maybe_unused]] const Entity entt, const bool force_back, const void *value) override { - if(value) { - if constexpr(std::is_copy_constructible_v) { - return emplace_element(entt, force_back, *static_cast(value)); - } else { - return base_type::end(); - } - } else { - if constexpr(std::is_default_constructible_v) { - return emplace_element(entt, force_back); - } else { - return base_type::end(); - } - } - } +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member. + */ +template +using nth_argument_t = typename nth_argument::type; -public: - /*! @brief Base type. */ - using base_type = underlying_type; - /*! @brief Allocator type. */ - using allocator_type = Allocator; - /*! @brief Type of the objects assigned to entities. */ - using value_type = Type; - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Pointer type to contained elements. */ - using pointer = typename container_type::pointer; - /*! @brief Constant pointer type to contained elements. */ - using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; - /*! @brief Random access iterator type. */ - using iterator = internal::storage_iterator; - /*! @brief Constant random access iterator type. */ - using const_iterator = internal::storage_iterator; - /*! @brief Reverse iterator type. */ - using reverse_iterator = std::reverse_iterator; - /*! @brief Constant reverse iterator type. */ - using const_reverse_iterator = std::reverse_iterator; - /*! @brief Extended iterable storage proxy. */ - using iterable = iterable_adaptor>; - /*! @brief Constant extended iterable storage proxy. */ - using const_iterable = iterable_adaptor>; +} // namespace entt - /*! @brief Default constructor. */ - basic_storage() - : basic_storage{allocator_type{}} {} +template +struct std::tuple_size>: std::integral_constant::size> {}; - /** - * @brief Constructs an empty storage with a given allocator. - * @param allocator The allocator to use. - */ - explicit basic_storage(const allocator_type &allocator) - : base_type{type_id(), deletion_policy{comp_traits::in_place_delete}, allocator}, - packed{container_type{allocator}, allocator} {} +template +struct std::tuple_element>: entt::type_list_element> {}; - /** - * @brief Move constructor. - * @param other The instance to move from. - */ - basic_storage(basic_storage &&other) noexcept - : base_type{std::move(other)}, - packed{std::move(other.packed)} {} +template +struct std::tuple_size>: std::integral_constant::size> {}; - /** - * @brief Allocator-extended move constructor. - * @param other The instance to move from. - * @param allocator The allocator to use. - */ - basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept - : base_type{std::move(other), allocator}, - packed{container_type{std::move(other.packed.first()), allocator}, allocator} { - ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed"); - } +template +struct std::tuple_element>: entt::value_list_element> {}; - /*! @brief Default destructor. */ - ~basic_storage() override { - shrink_to_size(0u); - } +#endif - /** - * @brief Move assignment operator. - * @param other The instance to move from. - * @return This storage. - */ - basic_storage &operator=(basic_storage &&other) noexcept { - ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed"); +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP - shrink_to_size(0u); - base_type::operator=(std::move(other)); - packed.first() = std::move(other.packed.first()); - propagate_on_container_move_assignment(packed.second(), other.packed.second()); - return *this; - } +#include - /** - * @brief Exchanges the contents with those of a given storage. - * @param other Storage to exchange the content with. - */ - void swap(basic_storage &other) { - using std::swap; - underlying_type::swap(other); - propagate_on_container_swap(packed.second(), other.packed.second()); - swap(packed.first(), other.packed.first()); - } +namespace entt { - /** - * @brief Returns the associated allocator. - * @return The associated allocator. - */ - [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { - return allocator_type{packed.second()}; - } +template +class delegate; - /** - * @brief Increases the capacity of a storage. - * - * If the new capacity is greater than the current capacity, new storage is - * allocated, otherwise the method does nothing. - * - * @param cap Desired capacity. - */ - void reserve(const size_type cap) override { - if(cap != 0u) { - base_type::reserve(cap); - assure_at_least(cap - 1u); - } - } +template> +class basic_dispatcher; - /** - * @brief Returns the number of elements that a storage has currently - * allocated space for. - * @return Capacity of the storage. - */ - [[nodiscard]] size_type capacity() const noexcept override { - return packed.first().size() * comp_traits::page_size; - } +template> +class emitter; - /*! @brief Requests the removal of unused capacity. */ - void shrink_to_fit() override { - base_type::shrink_to_fit(); - shrink_to_size(base_type::size()); - } +class connection; - /** - * @brief Direct access to the array of objects. - * @return A pointer to the array of objects. - */ - [[nodiscard]] const_pointer raw() const noexcept { - return packed.first().data(); - } +struct scoped_connection; - /*! @copydoc raw */ - [[nodiscard]] pointer raw() noexcept { - return packed.first().data(); - } +template +class sink; - /** - * @brief Returns an iterator to the beginning. - * - * The returned iterator points to the first instance of the internal array. - * If the storage is empty, the returned iterator will be equal to `end()`. - * - * @return An iterator to the first instance of the internal array. - */ - [[nodiscard]] const_iterator cbegin() const noexcept { - const auto pos = static_cast(base_type::size()); - return const_iterator{&packed.first(), pos}; - } +template> +class sigh; - /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const noexcept { - return cbegin(); - } +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; - /*! @copydoc begin */ - [[nodiscard]] iterator begin() noexcept { - const auto pos = static_cast(base_type::size()); - return iterator{&packed.first(), pos}; - } +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; - /** - * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * - * @return An iterator to the element following the last instance of the - * internal array. - */ - [[nodiscard]] const_iterator cend() const noexcept { - return const_iterator{&packed.first(), {}}; - } +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template +inline constexpr connect_arg_t connect_arg{}; - /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const noexcept { - return cend(); - } +} // namespace entt - /*! @copydoc end */ - [[nodiscard]] iterator end() noexcept { - return iterator{&packed.first(), {}}; - } +#endif - /** - * @brief Returns a reverse iterator to the beginning. - * - * The returned iterator points to the first instance of the reversed - * internal array. If the storage is empty, the returned iterator will be - * equal to `rend()`. - * - * @return An iterator to the first instance of the reversed internal array. - */ - [[nodiscard]] const_reverse_iterator crbegin() const noexcept { - return std::make_reverse_iterator(cend()); - } - /*! @copydoc crbegin */ - [[nodiscard]] const_reverse_iterator rbegin() const noexcept { - return crbegin(); - } +namespace entt { - /*! @copydoc rbegin */ - [[nodiscard]] reverse_iterator rbegin() noexcept { - return std::make_reverse_iterator(end()); - } +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ - /** - * @brief Returns a reverse iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the reversed internal array. Attempting to dereference the returned - * iterator results in undefined behavior. - * - * @return An iterator to the element following the last instance of the - * reversed internal array. - */ - [[nodiscard]] const_reverse_iterator crend() const noexcept { - return std::make_reverse_iterator(cbegin()); - } +namespace internal { - /*! @copydoc crend */ - [[nodiscard]] const_reverse_iterator rend() const noexcept { - return crend(); - } +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); - /*! @copydoc rend */ - [[nodiscard]] reverse_iterator rend() noexcept { - return std::make_reverse_iterator(begin()); - } +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); - /** - * @brief Returns the object assigned to an entity. - * - * @warning - * Attempting to use an entity that doesn't belong to the storage results in - * undefined behavior. - * - * @param entt A valid identifier. - * @return The object assigned to the entity. - */ - [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { - return element_at(base_type::index(entt)); - } +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); - /*! @copydoc get */ - [[nodiscard]] value_type &get(const entity_type entt) noexcept { - return const_cast(std::as_const(*this).get(entt)); - } +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); - /** - * @brief Returns the object assigned to an entity as a tuple. - * @param entt A valid identifier. - * @return The object assigned to the entity as a tuple. - */ - [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { - return std::forward_as_tuple(get(entt)); - } +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); - /*! @copydoc get_as_tuple */ - [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { - return std::forward_as_tuple(get(entt)); - } +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); - /** - * @brief Assigns an entity to a storage and constructs its object. - * - * @warning - * Attempting to use an entity that already belongs to the storage results - * in undefined behavior. - * - * @tparam Args Types of arguments to use to construct the object. - * @param entt A valid identifier. - * @param args Parameters to use to construct an object for the entity. - * @return A reference to the newly created object. - */ - template - value_type &emplace(const entity_type entt, Args &&...args) { - if constexpr(std::is_aggregate_v) { - const auto it = emplace_element(entt, false, Type{std::forward(args)...}); - return element_at(static_cast(it.index())); - } else { - const auto it = emplace_element(entt, false, std::forward(args)...); - return element_at(static_cast(it.index())); - } - } +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} - /** - * @brief Updates the instance assigned to a given entity in-place. - * @tparam Func Types of the function objects to invoke. - * @param entt A valid identifier. - * @param func Valid function objects. - * @return A reference to the updated instance. - */ - template - value_type &patch(const entity_type entt, Func &&...func) { - const auto idx = base_type::index(entt); - auto &elem = element_at(idx); - (std::forward(func)(elem), ...); - return elem; - } +} // namespace internal - /** - * @brief Assigns one or more entities to a storage and constructs their - * objects from a given instance. - * - * @warning - * Attempting to assign an entity that already belongs to the storage - * results in undefined behavior. - * - * @tparam It Type of input iterator. - * @param first An iterator to the first element of the range of entities. - * @param last An iterator past the last element of the range of entities. - * @param value An instance of the object to construct. - */ - template - void insert(It first, It last, const value_type &value = {}) { - for(; first != last; ++first) { - emplace_element(*first, true, value); - } - } +/** + * Internal details not to be documented. + * @endcond + */ - /** - * @brief Assigns one or more entities to a storage and constructs their - * objects from a given range. - * - * @sa construct - * - * @tparam EIt Type of input iterator. - * @tparam CIt Type of input iterator. - * @param first An iterator to the first element of the range of entities. - * @param last An iterator past the last element of the range of entities. - * @param from An iterator to the first element of the range of objects. - */ - template::value_type, value_type>>> - void insert(EIt first, EIt last, CIt from) { - for(; first != last; ++first, ++from) { - emplace_element(*first, true, *from); - } - } +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - /** - * @brief Returns an iterable object to use to _visit_ a storage. - * - * The iterable object returns a tuple that contains the current entity and - * a reference to its component. - * - * @return An iterable object to use to _visit_ the storage. - */ - [[nodiscard]] iterable each() noexcept { - return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; } - /*! @copydoc each */ - [[nodiscard]] const_iterable each() const noexcept { - return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; } -private: - compressed_pair packed; -}; + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); -/*! @copydoc basic_storage */ -template -class basic_storage>> - : public basic_sparse_set::template rebind_alloc> { - using alloc_traits = std::allocator_traits; - static_assert(std::is_same_v, "Invalid value type"); - using underlying_type = basic_sparse_set>; - using comp_traits = component_traits; + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } public: - /*! @brief Base type. */ - using base_type = underlying_type; - /*! @brief Allocator type. */ - using allocator_type = Allocator; - /*! @brief Type of the objects assigned to entities. */ - using value_type = Type; - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Extended iterable storage proxy. */ - using iterable = iterable_adaptor>; - /*! @brief Constant extended iterable storage proxy. */ - using const_iterable = iterable_adaptor>; + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; /*! @brief Default constructor. */ - basic_storage() - : basic_storage{allocator_type{}} {} + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} /** - * @brief Constructs an empty container with a given allocator. - * @param allocator The allocator to use. + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. */ - explicit basic_storage(const allocator_type &allocator) - : base_type{type_id(), deletion_policy{comp_traits::in_place_delete}, allocator} {} + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } /** - * @brief Move constructor. - * @param other The instance to move from. + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. */ - basic_storage(basic_storage &&other) noexcept = default; + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } /** - * @brief Allocator-extended move constructor. - * @param other The instance to move from. - * @param allocator The allocator to use. + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. */ - basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept - : base_type{std::move(other), allocator} {} + template + void connect() noexcept { + instance = nullptr; - /** - * @brief Move assignment operator. - * @param other The instance to move from. - * @return This storage. - */ - basic_storage &operator=(basic_storage &&other) noexcept = default; + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } /** - * @brief Returns the associated allocator. - * @return The associated allocator. + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. */ - [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { - return allocator_type{base_type::get_allocator()}; + template + void connect(Type &value_or_instance) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } } /** - * @brief Returns the object assigned to an entity, that is `void`. + * @brief Connects a free function with payload or a bound member to a + * delegate. * - * @warning - * Attempting to use an entity that doesn't belong to the storage results in - * undefined behavior. + * @sa connect(Type &) * - * @param entt A valid identifier. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. */ - void get([[maybe_unused]] const entity_type entt) const noexcept { - ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + template + void connect(Type *value_or_instance) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } } /** - * @brief Returns an empty tuple. + * @brief Connects an user defined function with optional payload to a + * delegate. * - * @warning - * Attempting to use an entity that doesn't belong to the storage results in - * undefined behavior. + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. * - * @param entt A valid identifier. - * @return Returns an empty tuple. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. */ - [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { - ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); - return std::tuple{}; + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; } /** - * @brief Assigns an entity to a storage and constructs its object. - * - * @warning - * Attempting to use an entity that already belongs to the storage results - * in undefined behavior. + * @brief Resets a delegate. * - * @tparam Args Types of arguments to use to construct the object. - * @param entt A valid identifier. + * After a reset, a delegate cannot be invoked anymore. */ - template - void emplace(const entity_type entt, Args &&...) { - base_type::try_emplace(entt, false); + void reset() noexcept { + instance = nullptr; + fn = nullptr; } /** - * @brief Updates the instance assigned to a given entity in-place. - * @tparam Func Types of the function objects to invoke. - * @param entt A valid identifier. - * @param func Valid function objects. + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. */ - template - void patch([[maybe_unused]] const entity_type entt, Func &&...func) { - ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); - (std::forward(func)(), ...); + [[nodiscard]] function_type *target() const noexcept { + return fn; } /** - * @brief Assigns entities to a storage. - * @tparam It Type of input iterator. - * @tparam Args Types of optional arguments. - * @param first An iterator to the first element of the range of entities. - * @param last An iterator past the last element of the range of entities. + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. */ - template - void insert(It first, It last, Args &&...) { - for(; first != last; ++first) { - base_type::try_emplace(*first, true); - } + [[nodiscard]] const void *data() const noexcept { + return instance; } /** - * @brief Returns an iterable object to use to _visit_ a storage. + * @brief Triggers a delegate. * - * The iterable object returns a tuple that contains the current entity. + * The delegate invokes the underlying function and returns the result. * - * @return An iterable object to use to _visit_ the storage. + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. */ - [[nodiscard]] iterable each() noexcept { - return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); } - /*! @copydoc each */ - [[nodiscard]] const_iterable each() const noexcept { - return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; } + +private: + const void *instance; + function_type *fn; }; /** - * @brief Provides a common way to define storage types. - * @tparam Type Storage value type. - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Allocator Type of allocator used to manage memory and elements. + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. */ -template -struct storage_type { - /*! @brief Type-to-storage conversion result. */ - using type = sigh_storage_mixin>; -}; +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} /** - * @brief Helper type. - * @tparam Args Arguments to forward. + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. */ -template -using storage_type_t = typename storage_type::type; +template +delegate(connect_arg_t) -> delegate>>; /** - * Type-to-storage conversion utility that preserves constness. - * @tparam Type Storage value type, eventually const. - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Allocator Type of allocator used to manage memory and elements. + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. */ -template -struct storage_for { - /*! @brief Type-to-storage conversion result. */ - using type = constness_as_t, Entity, Allocator>, Type>; -}; +template +delegate(connect_arg_t, Type &&) -> delegate>>; /** - * @brief Helper type. - * @tparam Args Arguments to forward. + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. */ -template -using storage_for_t = typename storage_for::type; +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; } // namespace entt #endif +// #include "fwd.hpp" + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + namespace entt { @@ -19100,8 +21179,8 @@ class extended_group_iterator; template class extended_group_iterator, get_t> { template - auto index_to_element(Type &cpool) const { - if constexpr(ignore_as_empty_v) { + auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(Type::traits_type::page_size == 0u) { return std::make_tuple(); } else { return std::forward_as_tuple(cpool.rbegin()[it.index()]); @@ -19109,6 +21188,7 @@ class extended_group_iterator, get_t> { } public: + using iterator_type = It; using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; @@ -19140,6 +21220,10 @@ class extended_group_iterator, get_t> { return operator*(); } + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + template friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; @@ -19158,423 +21242,186 @@ template return !(lhs == rhs); } -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Group. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. - */ -template -class basic_group; - -/** - * @brief Non-owning group. - * - * A non-owning group returns all entities and only the entities that are at - * least in the given storage. Moreover, it's guaranteed that the entity list is - * tightly packed in memory for fast iterations. - * - * @b Important - * - * Iterators aren't invalidated if: - * - * * New elements are added to the storage. - * * The entity currently pointed is modified (for example, components are added - * or removed from it). - * * The entity currently pointed is destroyed. - * - * In all other cases, modifying the pools iterated by the group in any way - * invalidates all the iterators and using them results in undefined behavior. - * - * @tparam Get Types of storage _observed_ by the group. - * @tparam Exclude Types of storage used to filter the group. - */ -template -class basic_group, get_t, exclude_t> { - using underlying_type = std::common_type_t; - using basic_common_type = std::common_type_t; - - template - static constexpr std::size_t index_of = type_list_index_v, type_list>; - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = underlying_type; - /*! @brief Unsigned integer type. */ +struct group_descriptor { using size_type = std::size_t; - /*! @brief Common type among all storage types. */ - using base_type = basic_common_type; - /*! @brief Random access iterator type. */ - using iterator = typename base_type::iterator; - /*! @brief Reversed iterator type. */ - using reverse_iterator = typename base_type::reverse_iterator; - /*! @brief Iterable group type. */ - using iterable = iterable_adaptor, get_t>>; - - /*! @brief Default constructor to use to create empty, invalid groups. */ - basic_group() noexcept - : handler{} {} - - /** - * @brief Constructs a group from a set of storage classes. - * @param ref The actual entities to iterate. - * @param gpool Storage types to iterate _observed_ by the group. - */ - basic_group(basic_common_type &ref, Get &...gpool) noexcept - : handler{&ref}, - pools{&gpool...} {} - - /** - * @brief Returns a const reference to the underlying handler. - * @return A const reference to the underlying handler. - */ - [[nodiscard]] const base_type &handle() const noexcept { - return *handler; + virtual ~group_descriptor() = default; + virtual size_type owned(const id_type *, const size_type) const noexcept { + return 0u; } +}; - /** - * @brief Returns the storage for a given component type. - * @tparam Type Type of component of which to return the storage. - * @return The storage for the given component type. - */ - template - [[nodiscard]] decltype(auto) storage() const noexcept { - return storage>(); - } +template +class group_handler; - /** - * @brief Returns the storage for a given index. - * @tparam Index Index of the storage to return. - * @return The storage for the given index. - */ - template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); - } +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v...>, "Groups do not support in-place delete"); + static_assert(!std::disjunction_v..., std::is_const..., std::is_const...>, "Const storage type not allowed"); - /** - * @brief Returns the number of entities that are part of the group. - * @return Number of entities that are part of the group. - */ - [[nodiscard]] size_type size() const noexcept { - return *this ? handler->size() : size_type{}; - } + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; - /** - * @brief Returns the number of elements that a group has currently - * allocated space for. - * @return Capacity of the group. - */ - [[nodiscard]] size_type capacity() const noexcept { - return *this ? handler->capacity() : size_type{}; + void swap_elements(const std::size_t pos, const entity_type entt) { + std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools); } - /*! @brief Requests the removal of unused capacity. */ - void shrink_to_fit() { - if(*this) { - handler->shrink_to_fit(); + void push_on_construct(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt); } } - - /** - * @brief Checks whether a group is empty. - * @return True if the group is empty, false otherwise. - */ - [[nodiscard]] bool empty() const noexcept { - return !*this || handler->empty(); - } - - /** - * @brief Returns an iterator to the first entity of the group. - * - * The returned iterator points to the first entity of the group. If the - * group is empty, the returned iterator will be equal to `end()`. - * - * @return An iterator to the first entity of the group. - */ - [[nodiscard]] iterator begin() const noexcept { - return *this ? handler->begin() : iterator{}; - } - - /** - * @brief Returns an iterator that is past the last entity of the group. - * - * The returned iterator points to the entity following the last entity of - * the group. Attempting to dereference the returned iterator results in - * undefined behavior. - * - * @return An iterator to the entity following the last entity of the - * group. - */ - [[nodiscard]] iterator end() const noexcept { - return *this ? handler->end() : iterator{}; - } - - /** - * @brief Returns an iterator to the first entity of the reversed group. - * - * The returned iterator points to the first entity of the reversed group. - * If the group is empty, the returned iterator will be equal to `rend()`. - * - * @return An iterator to the first entity of the reversed group. - */ - [[nodiscard]] reverse_iterator rbegin() const noexcept { - return *this ? handler->rbegin() : reverse_iterator{}; - } - - /** - * @brief Returns an iterator that is past the last entity of the reversed - * group. - * - * The returned iterator points to the entity following the last entity of - * the reversed group. Attempting to dereference the returned iterator - * results in undefined behavior. - * - * @return An iterator to the entity following the last entity of the - * reversed group. - */ - [[nodiscard]] reverse_iterator rend() const noexcept { - return *this ? handler->rend() : reverse_iterator{}; - } - - /** - * @brief Returns the first entity of the group, if any. - * @return The first entity of the group if one exists, the null entity - * otherwise. - */ - [[nodiscard]] entity_type front() const noexcept { - const auto it = begin(); - return it != end() ? *it : null; + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt); + } } - /** - * @brief Returns the last entity of the group, if any. - * @return The last entity of the group if one exists, the null entity - * otherwise. - */ - [[nodiscard]] entity_type back() const noexcept { - const auto it = rbegin(); - return it != rend() ? *it : null; + void remove_if(const entity_type entt) { + if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) { + swap_elements(--len, entt); + } } - /** - * @brief Finds an entity. - * @param entt A valid identifier. - * @return An iterator to the given entity if it's found, past the end - * iterator otherwise. - */ - [[nodiscard]] iterator find(const entity_type entt) const noexcept { - const auto it = *this ? handler->find(entt) : iterator{}; - return it != end() && *it == entt ? it : end(); +public: + using size_type = typename base_type::size_type; + + group_handler(Owned &...opool, Get &...gpool, Exclude &...epool) + : pools{&opool..., &gpool...}, + filter{&epool...}, + len{} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) { + push_on_construct(*first); + } } - /** - * @brief Returns the identifier that occupies the given position. - * @param pos Position of the element to return. - * @return The identifier that occupies the given position. - */ - [[nodiscard]] entity_type operator[](const size_type pos) const { - return begin()[pos]; + size_type owned(const id_type *elem, const size_type length) const noexcept final { + size_type cnt = 0u; + + for(auto pos = 0u; pos < length; ++pos) { + cnt += ((elem[pos] == entt::type_hash::value()) || ...); + } + + return cnt; } - /** - * @brief Checks if a group is properly initialized. - * @return True if the group is properly initialized, false otherwise. - */ - [[nodiscard]] explicit operator bool() const noexcept { - return handler != nullptr; + [[nodiscard]] size_type length() const noexcept { + return len; } - /** - * @brief Checks if a group contains an entity. - * @param entt A valid identifier. - * @return True if the group contains the given entity, false otherwise. - */ - [[nodiscard]] bool contains(const entity_type entt) const noexcept { - return *this && handler->contains(entt); + template + Type pools_as() const noexcept { + return pools; } - /** - * @brief Returns the components assigned to the given entity. - * - * Prefer this function instead of `registry::get` during iterations. It has - * far better performance than its counterpart. - * - * @warning - * Attempting to use an invalid component type results in a compilation - * error. Attempting to use an entity that doesn't belong to the group - * results in undefined behavior. - * - * @tparam Type Types of components to get. - * @param entt A valid identifier. - * @return The components assigned to the entity. - */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { - return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); - } else if constexpr(sizeof...(Type) == 1) { - return (std::get>(pools)->get(entt), ...); - } else { - return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); + template + Type filter_as() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + std::size_t len; +}; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); } } - /** - * @brief Iterates entities and components and applies the given function - * object to them. - * - * The function object is invoked for each entity. It is provided with the - * entity itself and a set of references to non-empty components. The - * _constness_ of the components is as requested.
- * The signature of the function must be equivalent to one of the following - * forms: - * - * @code{.cpp} - * void(const entity_type, Type &...); - * void(Type &...); - * @endcode - * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - for(const auto entt: *this) { - if constexpr(is_applicable_v{}, std::declval().get({})))>) { - std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); - } else { - std::apply(func, get(entt)); - } + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); } } - /** - * @brief Returns an iterable object to use to _visit_ a group. - * - * The iterable object returns tuples that contain the current entity and a - * set of references to its non-empty components. The _constness_ of the - * components is as requested. - * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. - * - * @return An iterable object to use to _visit_ the group. - */ - [[nodiscard]] iterable each() const noexcept { - return iterable{{begin(), pools}, {end(), pools}}; + void remove_if(const entity_type entt) { + elem.remove(entt); } - /** - * @brief Sort a group according to the given comparison function. - * - * Sort the group so that iterating it with a couple of iterators returns - * entities and components in the expected order. See `begin` and `end` for - * more details. - * - * The comparison function object must return `true` if the first element - * is _less_ than the second one, `false` otherwise. The signature of the - * comparison function should be equivalent to one of the following: - * - * @code{.cpp} - * bool(std::tuple, std::tuple); - * bool(const Type &..., const Type &...); - * bool(const Entity, const Entity); - * @endcode - * - * Where `Type` are such that they are iterated by the group.
- * Moreover, the comparison function object shall induce a - * _strict weak ordering_ on the values. - * - * The sort function object must offer a member function template - * `operator()` that accepts three arguments: - * - * * An iterator to the first element of the range to sort. - * * An iterator past the last element of the range to sort. - * * A comparison function to use to compare the elements. - * - * @tparam Type Optional types of components to compare. - * @tparam Compare Type of comparison function object. - * @tparam Sort Type of sort function object. - * @tparam Args Types of arguments to forward to the sort function object. - * @param compare A valid comparison function object. - * @param algo A valid sort function object. - * @param args Arguments to forward to the sort function object, if any. - */ - template - void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { - if(*this) { - if constexpr(sizeof...(Type) == 0) { - static_assert(std::is_invocable_v, "Invalid comparison function"); - handler->sort(std::move(compare), std::move(algo), std::forward(args)...); - } else { - auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { - if constexpr(sizeof...(Type) == 1) { - return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); - } else { - return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); - } - }; +public: + using common_type = base_type; - handler->sort(std::move(comp), std::move(algo), std::forward(args)...); - } + template + group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool) + : pools{&gpool...}, + filter{&epool...}, + elem{alloc} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + for(const auto entity: static_cast(*std::get<0>(pools))) { + push_on_construct(entity); } } - /** - * @brief Sort the shared pool of entities according to the given component. - * - * Non-owning groups of the same type share with the registry a pool of - * entities with its own order that doesn't depend on the order of any pool - * of components. Users can order the underlying data structure so that it - * respects the order of the pool of the given component. - * - * @note - * The shared pool of entities and thus its order is affected by the changes - * to each and every pool that it tracks. Therefore changes to those pools - * can quickly ruin the order imposed to the pool of entities shared between - * the non-owning groups. - * - * @tparam Type Type of component to use to impose the order. - */ + common_type &handle() noexcept { + return elem; + } + + const common_type &handle() const noexcept { + return elem; + } + template - void sort() const { - if(*this) { - handler->respect(*std::get>(pools)); - } + Type pools_as() const noexcept { + return pools; + } + + template + Type filter_as() const noexcept { + return filter; } private: - base_type *const handler; - const std::tuple pools; + std::tuple pools; + std::tuple filter; + base_type elem; }; +} // namespace internal + /** - * @brief Owning group. - * - * Owning groups returns all entities and only the entities that are at - * least in the given storage. Moreover: + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Group. * - * * It's guaranteed that the entity list is tightly packed in memory for fast - * iterations. - * * It's guaranteed that all components in the owned storage are tightly packed - * in memory for even faster iterations and to allow direct access. - * * They stay true to the order of the owned storage and all instances have the - * same order in memory. + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_group; + +/** + * @brief Non-owning group. * - * The more types of storage are owned, the faster it is to iterate a group. + * A non-owning group returns all entities and only the entities that are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. * * @b Important * @@ -19586,19 +21433,28 @@ class basic_group, get_t, exclude_t> { * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way - * invalidates all the iterators and using them results in undefined behavior. + * invalidates all the iterators. * - * @tparam Owned Types of storage _owned_ by the group. * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. */ -template -class basic_group, get_t, exclude_t> { - using underlying_type = std::common_type_t; - using basic_common_type = std::common_type_t; +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; template - static constexpr std::size_t index_of = type_list_index_v, type_list>; + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template pools_as() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template filter_as() : return_type{}; + } public: /*! @brief Underlying entity identifier. */ @@ -19606,54 +21462,83 @@ class basic_group, get_t, exclude_t> { /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = basic_common_type; + using common_type = base_type; /*! @brief Random access iterator type. */ - using iterator = typename base_type::iterator; + using iterator = typename common_type::iterator; /*! @brief Reversed iterator type. */ - using reverse_iterator = typename base_type::reverse_iterator; + using reverse_iterator = typename common_type::reverse_iterator; /*! @brief Iterable group type. */ - using iterable = iterable_adaptor, get_t>>; + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler, get_t...>, exclude_t...>>; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() noexcept - : length{} {} + : descriptor{} {} /** * @brief Constructs a group from a set of storage classes. - * @param extent The actual number of entities to iterate. - * @param opool Storage types to iterate _owned_ by the group. - * @param gpool Storage types to iterate _observed_ by the group. + * @param ref A reference to a group handler. */ - basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept - : pools{&opool..., &gpool...}, - length{&extent} {} + basic_group(handler &ref) noexcept + : descriptor{&ref} {} /** - * @brief Returns the storage for a given component type. + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given component type, if any. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { + [[nodiscard]] auto *storage() const noexcept { return storage>(); } /** - * @brief Returns the storage for a given index. + * @brief Returns the storage for a given index, if any. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } } /** - * @brief Returns the number of entities that that are part of the group. - * @return Number of entities that that are part of the group. + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. */ [[nodiscard]] size_type size() const noexcept { - return *this ? *length : size_type{}; + return *this ? handle().size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } } /** @@ -19661,60 +21546,48 @@ class basic_group, get_t, exclude_t> { * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { - return !*this || !*length; + return !*this || handle().empty(); } /** * @brief Returns an iterator to the first entity of the group. * - * The returned iterator points to the first entity of the group. If the - * group is empty, the returned iterator will be equal to `end()`. + * If the group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const noexcept { - return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; + return *this ? handle().begin() : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. - * - * The returned iterator points to the entity following the last entity of - * the group. Attempting to dereference the returned iterator results in - * undefined behavior. - * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const noexcept { - return *this ? std::get<0>(pools)->base_type::end() : iterator{}; + return *this ? handle().end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * - * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { - return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; + return *this ? handle().rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. - * - * The returned iterator points to the entity following the last entity of - * the reversed group. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const noexcept { - return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; + return *this ? handle().rend() : reverse_iterator{}; } /** @@ -19744,8 +21617,7 @@ class basic_group, get_t, exclude_t> { * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { - const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; - return it != end() && it >= begin() && *it == entt ? it : end(); + return *this ? handle().find(entt) : iterator{}; } /** @@ -19762,7 +21634,7 @@ class basic_group, get_t, exclude_t> { * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return length != nullptr; + return descriptor != nullptr; } /** @@ -19771,891 +21643,602 @@ class basic_group, get_t, exclude_t> { * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { - return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); + return *this && handle().contains(entt); } /** * @brief Returns the components assigned to the given entity. * - * Prefer this function instead of `registry::get` during iterations. It has - * far better performance than its counterpart. - * * @warning - * Attempting to use an invalid component type results in a compilation - * error. Attempting to use an entity that doesn't belong to the group - * results in undefined behavior. + * Attempting to use an entity that doesn't belong to the group results in + * undefined behavior. * - * @tparam Type Types of components to get. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. - */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { - return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); - } else if constexpr(sizeof...(Type) == 1) { - return (std::get>(pools)->get(entt), ...); - } else { - return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); - } - } - - /** - * @brief Iterates entities and components and applies the given function - * object to them. - * - * The function object is invoked for each entity. It is provided with the - * entity itself and a set of references to non-empty components. The - * _constness_ of the components is as requested.
- * The signature of the function must be equivalent to one of the following - * forms: - * - * @code{.cpp} - * void(const entity_type, Type &...); - * void(Type &...); - * @endcode - * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - for(auto args: each()) { - if constexpr(is_applicable_v{}, std::declval().get({})))>) { - std::apply(func, args); - } else { - std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); - } - } - } - - /** - * @brief Returns an iterable object to use to _visit_ a group. - * - * The iterable object returns tuples that contain the current entity and a - * set of references to its non-empty components. The _constness_ of the - * components is as requested. - * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. - * - * @return An iterable object to use to _visit_ the group. - */ - [[nodiscard]] iterable each() const noexcept { - return {{begin(), pools}, {end(), pools}}; - } - - /** - * @brief Sort a group according to the given comparison function. - * - * Sort the group so that iterating it with a couple of iterators returns - * entities and components in the expected order. See `begin` and `end` for - * more details. - * - * The comparison function object must return `true` if the first element - * is _less_ than the second one, `false` otherwise. The signature of the - * comparison function should be equivalent to one of the following: - * - * @code{.cpp} - * bool(std::tuple, std::tuple); - * bool(const Type &, const Type &); - * bool(const Entity, const Entity); - * @endcode - * - * Where `Type` are either owned types or not but still such that they are - * iterated by the group.
- * Moreover, the comparison function object shall induce a - * _strict weak ordering_ on the values. - * - * The sort function object must offer a member function template - * `operator()` that accepts three arguments: - * - * * An iterator to the first element of the range to sort. - * * An iterator past the last element of the range to sort. - * * A comparison function to use to compare the elements. - * - * @tparam Type Optional types of components to compare. - * @tparam Compare Type of comparison function object. - * @tparam Sort Type of sort function object. - * @tparam Args Types of arguments to forward to the sort function object. - * @param compare A valid comparison function object. - * @param algo A valid sort function object. - * @param args Arguments to forward to the sort function object, if any. - */ - template - void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { - if constexpr(sizeof...(Type) == 0) { - static_assert(std::is_invocable_v, "Invalid comparison function"); - std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); - } else { - auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { - if constexpr(sizeof...(Type) == 1) { - return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); - } else { - return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); - } - }; - - std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); - } - - std::apply([this](auto *head, auto *...other) { - for(auto next = *length; next; --next) { - const auto pos = next - 1; - [[maybe_unused]] const auto entt = head->data()[pos]; - (other->swap_elements(other->data()[pos], entt), ...); - } - }, - pools); - } - -private: - const std::tuple pools; - const size_type *const length; -}; - -} // namespace entt - -#endif - -// #include "entity/handle.hpp" -#ifndef ENTT_ENTITY_HANDLE_HPP -#define ENTT_ENTITY_HANDLE_HPP - -#include -#include -#include -#include -// #include "../core/iterator.hpp" - -// #include "../core/type_traits.hpp" - -// #include "entity.hpp" - -// #include "fwd.hpp" - - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -class handle_storage_iterator final { - template - friend class handle_storage_iterator; - - using underlying_type = std::remove_reference_t; - using entity_type = typename underlying_type::entity_type; - -public: - using value_type = typename std::iterator_traits::value_type; - using pointer = input_iterator_pointer; - using reference = value_type; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; - - constexpr handle_storage_iterator() noexcept - : entt{null}, - it{}, - last{} {} - - constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept - : entt{value}, - it{from}, - last{to} { - while(it != last && !it->second.contains(entt)) { ++it; } - } - - constexpr handle_storage_iterator &operator++() noexcept { - while(++it != last && !it->second.contains(entt)) {} - return *this; - } - - constexpr handle_storage_iterator operator++(int) noexcept { - handle_storage_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] constexpr reference operator*() const noexcept { - return *it; - } - - [[nodiscard]] constexpr pointer operator->() const noexcept { - return operator*(); - } - - template - friend constexpr bool operator==(const handle_storage_iterator &, const handle_storage_iterator &) noexcept; - -private: - entity_type entt; - It it; - It last; -}; - -template -[[nodiscard]] constexpr bool operator==(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { - return lhs.it == rhs.it; -} - -template -[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { - return !(lhs == rhs); -} - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Non-owning handle to an entity. - * - * Tiny wrapper around a registry and an entity. - * - * @tparam Registry Basic registry type. - * @tparam Scope Types to which to restrict the scope of a handle. - */ -template -struct basic_handle { - /*! @brief Type of registry accepted by the handle. */ - using registry_type = Registry; - /*! @brief Underlying entity identifier. */ - using entity_type = typename registry_type::entity_type; - /*! @brief Underlying version type. */ - using version_type = typename registry_type::version_type; - /*! @brief Unsigned integer type. */ - using size_type = typename registry_type::size_type; - - /*! @brief Constructs an invalid handle. */ - basic_handle() noexcept - : reg{}, - entt{null} {} - - /** - * @brief Constructs a handle from a given registry and entity. - * @param ref An instance of the registry class. - * @param value A valid identifier. - */ - basic_handle(registry_type &ref, entity_type value) noexcept - : reg{&ref}, - entt{value} {} - - /** - * @brief Returns an iterable object to use to _visit_ a handle. - * - * The iterable object returns a pair that contains the name and a reference - * to the current storage.
- * Returned storage are those that contain the entity associated with the - * handle. - * - * @return An iterable object to use to _visit_ the handle. - */ - [[nodiscard]] auto storage() const noexcept { - auto iterable = reg->storage(); - using iterator_type = internal::handle_storage_iterator; - return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}}; + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); } /** - * @brief Constructs a const handle from a non-const one. - * @tparam Other A valid entity type (see entt_traits for more details). - * @tparam Args Scope of the handle to construct. - * @return A const handle referring to the same registry and the same - * entity. + * @brief Returns the components assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the groups results in + * undefined behavior. + * + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. */ - template - operator basic_handle() const noexcept { - static_assert(std::is_same_v || std::is_same_v, Registry>, "Invalid conversion between different handles"); - static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); - return reg ? basic_handle{*reg, entt} : basic_handle{}; + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } } /** - * @brief Converts a handle to its underlying entity. - * @return The contained identifier. + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. */ - [[nodiscard]] operator entity_type() const noexcept { - return entity(); + template + void each(Func func) const { + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } } /** - * @brief Checks if a handle refers to non-null registry pointer and entity. - * @return True if the handle refers to non-null registry and entity, false otherwise. + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. */ - [[nodiscard]] explicit operator bool() const noexcept { - return reg && reg->valid(entt); + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return iterable{{begin(), cpools}, {end(), cpools}}; } /** - * @brief Checks if a handle refers to a valid entity or not. - * @return True if the handle refers to a valid entity, false otherwise. + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. */ - [[nodiscard]] bool valid() const { - return reg->valid(entt); + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); } /** - * @brief Returns a pointer to the underlying registry, if any. - * @return A pointer to the underlying registry, if any. + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. */ - [[nodiscard]] registry_type *registry() const noexcept { - return reg; + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + if(*this) { + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } } /** - * @brief Returns the entity associated with a handle. - * @return The entity associated with the handle. + * @brief Sort the shared pool of entities according to a given storage. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @param other The storage to use to impose the order. */ - [[nodiscard]] entity_type entity() const noexcept { - return entt; + void sort_as(const common_type &other) const { + if(*this) { + descriptor->handle().sort_as(other); + } } - /*! @brief Destroys the entity associated with a handle. */ - void destroy() { - reg->destroy(entt); +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all components in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template pools_as() : return_type{}; } - /** - * @brief Destroys the entity associated with a handle. - * @param version A desired version upon destruction. - */ - void destroy(const version_type version) { - reg->destroy(entt, version); + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->template filter_as() : return_type{}; } +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler...>, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + /** - * @brief Assigns the given component to a handle. - * @tparam Component Type of component to create. - * @tparam Args Types of arguments to use to construct the component. - * @param args Parameters to use to initialize the component. - * @return A reference to the newly created component. + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. */ - template - decltype(auto) emplace(Args &&...args) const { - static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template emplace(entt, std::forward(args)...); - } + basic_group(handler &ref) noexcept + : descriptor{&ref} {} /** - * @brief Assigns or replaces the given component for a handle. - * @tparam Component Type of component to assign or replace. - * @tparam Args Types of arguments to use to construct the component. - * @param args Parameters to use to initialize the component. - * @return A reference to the newly created component. + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. */ - template - decltype(auto) emplace_or_replace(Args &&...args) const { - static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template emplace_or_replace(entt, std::forward(args)...); + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); } /** - * @brief Patches the given component for a handle. - * @tparam Component Type of component to patch. - * @tparam Func Types of the function objects to invoke. - * @param func Valid function objects. - * @return A reference to the patched component. + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. */ - template - decltype(auto) patch(Func &&...func) const { - static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template patch(entt, std::forward(func)...); + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); } /** - * @brief Replaces the given component for a handle. - * @tparam Component Type of component to replace. - * @tparam Args Types of arguments to use to construct the component. - * @param args Parameters to use to initialize the component. - * @return A reference to the component being replaced. + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. */ - template - decltype(auto) replace(Args &&...args) const { - static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template replace(entt, std::forward(args)...); + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Owned) + sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } } /** - * @brief Removes the given components from a handle. - * @tparam Component Types of components to remove. - * @return The number of components actually removed. + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. */ - template - size_type remove() const { - static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); - return reg->template remove(entt); + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; } /** - * @brief Erases the given components from a handle. - * @tparam Component Types of components to erase. + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. */ - template - void erase() const { - static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); - reg->template erase(entt); + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); } /** - * @brief Checks if a handle has all the given components. - * @tparam Component Components for which to perform the check. - * @return True if the handle has all the components, false otherwise. + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. */ - template - [[nodiscard]] decltype(auto) all_of() const { - return reg->template all_of(entt); + [[nodiscard]] iterator begin() const noexcept { + return *this ? (handle().end() - descriptor->length()) : iterator{}; } /** - * @brief Checks if a handle has at least one of the given components. - * @tparam Component Components for which to perform the check. - * @return True if the handle has at least one of the given components, - * false otherwise. + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. */ - template - [[nodiscard]] decltype(auto) any_of() const { - return reg->template any_of(entt); + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; } /** - * @brief Returns references to the given components for a handle. - * @tparam Component Types of components to get. - * @return References to the components owned by the handle. + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. */ - template - [[nodiscard]] decltype(auto) get() const { - static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); - return reg->template get(entt); + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; } /** - * @brief Returns a reference to the given component for a handle. - * @tparam Component Type of component to get. - * @tparam Args Types of arguments to use to construct the component. - * @param args Parameters to use to initialize the component. - * @return Reference to the component owned by the handle. + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. */ - template - [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { - static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template get_or_emplace(entt, std::forward(args)...); + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; } /** - * @brief Returns pointers to the given components for a handle. - * @tparam Component Types of components to get. - * @return Pointers to the components owned by the handle. + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. */ - template - [[nodiscard]] auto try_get() const { - static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); - return reg->template try_get(entt); + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; } /** - * @brief Checks if a handle has components assigned. - * @return True if the handle has no components assigned, false otherwise. + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. */ - [[nodiscard]] bool orphan() const { - return reg->orphan(entt); - } - -private: - registry_type *reg; - entity_type entt; -}; - -/** - * @brief Compares two handles. - * @tparam Args Scope of the first handle. - * @tparam Other Scope of the second handle. - * @param lhs A valid handle. - * @param rhs A valid handle. - * @return True if both handles refer to the same registry and the same - * entity, false otherwise. - */ -template -[[nodiscard]] bool operator==(const basic_handle &lhs, const basic_handle &rhs) noexcept { - return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); -} - -/** - * @brief Compares two handles. - * @tparam Args Scope of the first handle. - * @tparam Other Scope of the second handle. - * @param lhs A valid handle. - * @param rhs A valid handle. - * @return False if both handles refer to the same registry and the same - * entity, true otherwise. - */ -template -[[nodiscard]] bool operator!=(const basic_handle &lhs, const basic_handle &rhs) noexcept { - return !(lhs == rhs); -} - -} // namespace entt - -#endif - -// #include "entity/helper.hpp" -#ifndef ENTT_ENTITY_HELPER_HPP -#define ENTT_ENTITY_HELPER_HPP - -#include -#include -// #include "../core/fwd.hpp" - -// #include "../core/type_traits.hpp" - -// #include "../signal/delegate.hpp" -#ifndef ENTT_SIGNAL_DELEGATE_HPP -#define ENTT_SIGNAL_DELEGATE_HPP - -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/type_traits.hpp" - -// #include "fwd.hpp" - - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); - -template -constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); - -template -constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); - -template -constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); - -template -constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); - -template -using function_pointer_t = decltype(function_pointer(std::declval()...)); - -template -[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { - return std::index_sequence_for{}; -} - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Basic delegate implementation. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error unless the template parameter is a function type. - */ -template -class delegate; - -/** - * @brief Utility class to use to send around functions and members. - * - * Unmanaged delegate for function pointers and members. Users of this class are - * in charge of disconnecting instances before deleting them. - * - * A delegate can be used as a general purpose invoker without memory overhead - * for free functions possibly with payloads and bound or unbound members. - * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - */ -template -class delegate { - template - [[nodiscard]] auto wrap(std::index_sequence) noexcept { - return [](const void *, Args... args) -> Ret { - [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); - }; - } - - template - [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { - return [](const void *payload, Args... args) -> Ret { - [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); - }; + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; } - template - [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { - return [](const void *payload, Args... args) -> Ret { - [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); - }; + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; } -public: - /*! @brief Function type of the contained target. */ - using function_type = Ret(const void *, Args...); - /*! @brief Function type of the delegate. */ - using type = Ret(Args...); - /*! @brief Return type of the delegate. */ - using result_type = Ret; - - /*! @brief Default constructor. */ - delegate() noexcept - : instance{nullptr}, - fn{nullptr} {} - /** - * @brief Constructs a delegate with a given object or payload, if any. - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload, if any. - * @param value_or_instance Optional valid object that fits the purpose. - */ - template - delegate(connect_arg_t, Type &&...value_or_instance) noexcept { - connect(std::forward(value_or_instance)...); + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; } /** - * @brief Constructs a delegate and connects an user defined function with - * optional payload. - * @param function Function to connect to the delegate. - * @param payload User defined arbitrary data. + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. */ - delegate(function_type *function, const void *payload = nullptr) noexcept { - connect(function, payload); + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; } /** - * @brief Connects a free function or an unbound member to a delegate. - * @tparam Candidate Function or member to connect to the delegate. + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. */ - template - void connect() noexcept { - instance = nullptr; - - if constexpr(std::is_invocable_r_v) { - fn = [](const void *, Args... args) -> Ret { - return Ret(std::invoke(Candidate, std::forward(args)...)); - }; - } else if constexpr(std::is_member_pointer_v) { - fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); - } else { - fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); - } + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); } /** - * @brief Connects a free function with payload or a bound member to a - * delegate. + * @brief Returns the components assigned to the given entity. * - * The delegate isn't responsible for the connected object or the payload. - * Users must always guarantee that the lifetime of the instance overcomes - * the one of the delegate.
- * When used to connect a free function with payload, its signature must be - * such that the instance is the first argument before the ones used to - * define the delegate itself. + * @warning + * Attempting to use an entity that doesn't belong to the group results in + * undefined behavior. * - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid reference that fits the purpose. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. */ - template - void connect(Type &value_or_instance) noexcept { - instance = &value_or_instance; - - if constexpr(std::is_invocable_r_v) { - fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast *>(payload)); - return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); - }; - } else { - fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); - } + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); } /** - * @brief Connects a free function with payload or a bound member to a - * delegate. + * @brief Returns the components assigned to the given entity. * - * @sa connect(Type &) + * @warning + * Attempting to use an entity that doesn't belong to the groups results in + * undefined behavior. * - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid pointer that fits the purpose. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. */ - template - void connect(Type *value_or_instance) noexcept { - instance = value_or_instance; + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools(); - if constexpr(std::is_invocable_r_v) { - fn = [](const void *payload, Args... args) -> Ret { - Type *curr = static_cast(const_cast *>(payload)); - return Ret(std::invoke(Candidate, curr, std::forward(args)...)); - }; + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); } else { - fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); } } /** - * @brief Connects an user defined function with optional payload to a - * delegate. + * @brief Iterates entities and components and applies the given function + * object to them. * - * The delegate isn't responsible for the connected object or the payload. - * Users must always guarantee that the lifetime of an instance overcomes - * the one of the delegate.
- * The payload is returned as the first argument to the target function in - * all cases. + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: * - * @param function Function to connect to the delegate. - * @param payload User defined arbitrary data. - */ - void connect(function_type *function, const void *payload = nullptr) noexcept { - ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); - instance = payload; - fn = function; - } - - /** - * @brief Resets a delegate. + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode * - * After a reset, a delegate cannot be invoked anymore. - */ - void reset() noexcept { - instance = nullptr; - fn = nullptr; - } - - /** - * @brief Returns the instance or the payload linked to a delegate, if any. - * @return An opaque pointer to the underlying data. + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. */ - [[nodiscard]] const void *data() const noexcept { - return instance; + template + void each(Func func) const { + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); + } + } } /** - * @brief Triggers a delegate. + * @brief Returns an iterable object to use to _visit_ a group. * - * The delegate invokes the underlying function and returns the result. + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. * - * @warning - * Attempting to trigger an invalid delegate results in undefined - * behavior. + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. * - * @param args Arguments to use to invoke the underlying function. - * @return The value returned by the underlying function. + * @return An iterable object to use to _visit_ the group. */ - Ret operator()(Args... args) const { - ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); - return fn(instance, std::forward(args)...); + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return {{begin(), cpools}, {end(), cpools}}; } /** - * @brief Checks whether a delegate actually stores a listener. - * @return False if the delegate is empty, true otherwise. + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are either owned types or not but still such that they are + * iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. */ - [[nodiscard]] explicit operator bool() const noexcept { - // no need to also test instance - return !(fn == nullptr); + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); } /** - * @brief Compares the contents of two delegates. - * @param other Delegate with which to compare. - * @return False if the two contents differ, true otherwise. + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. */ - [[nodiscard]] bool operator==(const delegate &other) const noexcept { - return fn == other.fn && instance == other.instance; - } + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + const auto cpools = pools(); -private: - const void *instance; - function_type *fn; -}; + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; -/** - * @brief Compares the contents of two delegates. - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - * @param lhs A valid delegate object. - * @param rhs A valid delegate object. - * @return True if the two contents differ, false otherwise. - */ -template -[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { - return !(lhs == rhs); -} + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } -/** - * @brief Deduction guide. - * @tparam Candidate Function or member to connect to the delegate. - */ -template -delegate(connect_arg_t) -> delegate>>; + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; -/** - * @brief Deduction guide. - * @tparam Candidate Function or member to connect to the delegate. - * @tparam Type Type of class or type of payload. - */ -template -delegate(connect_arg_t, Type &&) -> delegate>>; + std::apply(cb, cpools); + } -/** - * @brief Deduction guide. - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - */ -template -delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; +private: + handler *descriptor; +}; } // namespace entt #endif -// #include "component.hpp" - -// #include "fwd.hpp" - -// #include "group.hpp" -#ifndef ENTT_ENTITY_GROUP_HPP -#define ENTT_ENTITY_GROUP_HPP +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP +#include +#include #include #include #include @@ -20665,16 +22248,10 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate -class extended_group_iterator; +template +[[nodiscard]] auto filter_as_tuple(const std::array &filter) noexcept { + return std::apply([](const auto *...curr) { return std::make_tuple(static_cast(const_cast *>(curr))...); }, filter); +} -template -class extended_group_iterator, get_t> { - template - auto index_to_element(Type &cpool) const { - if constexpr(ignore_as_empty_v) { - return std::make_tuple(); - } else { - return std::forward_as_tuple(cpool.rbegin()[it.index()]); - } +template +[[nodiscard]] auto none_of(const std::array &filter, const typename Type::entity_type entt) noexcept { + return std::apply([entt](const auto *...curr) { return (!(curr && curr->contains(entt)) && ...); }, filter); +} + +template +[[nodiscard]] auto view_pack(const std::tuple value, const std::tuple excl, std::index_sequence) { + const auto pools = std::tuple_cat(value, excl); + basic_view, exclude_t> elem{}; + (((std::get(pools) != nullptr) ? elem.template storage(*std::get(pools)) : void()), ...); + return elem; +} + +template +class view_iterator final { + using iterator_type = typename Type::const_iterator; + + [[nodiscard]] bool valid() const noexcept { + return ((Get != 0u) || (*it != tombstone)) + && std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) + && none_of(filter, *it); } public: + using value_type = typename iterator_type::value_type; + using pointer = typename iterator_type::pointer; + using reference = typename iterator_type::reference; + using difference_type = typename iterator_type::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + last{}, + pools{}, + filter{} {} + + view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl) noexcept + : it{curr}, + last{to}, + pools{value}, + filter{excl} { + while(it != last && !valid()) { + ++it; + } + } + + view_iterator &operator++() noexcept { + while(++it != last && !valid()) {} + return *this; + } + + view_iterator operator++(int) noexcept { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + +private: + iterator_type it; + iterator_type last; + std::array pools; + std::array filter; +}; + +template +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +struct extended_view_iterator final { + using iterator_type = It; using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; - constexpr extended_group_iterator() + constexpr extended_view_iterator() : it{}, pools{} {} - extended_group_iterator(It from, const std::tuple &cpools) + extended_view_iterator(It from, std::tuple value) : it{from}, - pools{cpools} {} + pools{value} {} - extended_group_iterator &operator++() noexcept { + extended_view_iterator &operator++() noexcept { return ++it, *this; } - extended_group_iterator operator++(int) noexcept { - extended_group_iterator orig = *this; + extended_view_iterator operator++(int) noexcept { + extended_view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const noexcept { - return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); } [[nodiscard]] pointer operator->() const noexcept { return operator*(); } + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + template - friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; private: It it; - std::tuple pools; + std::tuple pools; }; template -[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -20757,20 +22415,21 @@ template */ /** - * @brief Group. + * @brief View implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template -class basic_group; +class basic_view; /** - * @brief Non-owning group. + * @brief Multi component view. * - * A non-owning group returns all entities and only the entities that are at - * least in the given storage. Moreover, it's guaranteed that the entity list is - * tightly packed in memory for fast iterations. + * Multi component views iterate over those entities that are at least in the + * given storage. During initialization, a multi component view looks at the + * number of entities available for each component and uses the smallest set in + * order to get a performance boost when iterating. * * @b Important * @@ -20781,19 +22440,61 @@ class basic_group; * or removed from it). * * The entity currently pointed is destroyed. * - * In all other cases, modifying the pools iterated by the group in any way - * invalidates all the iterators and using them results in undefined behavior. + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. * - * @tparam Get Types of storage _observed_ by the group. - * @tparam Exclude Types of storage used to filter the group. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. */ template -class basic_group, get_t, exclude_t> { - using underlying_type = std::common_type_t; - using basic_common_type = std::common_type_t; +class basic_view, exclude_t> { + static constexpr auto offset = sizeof...(Get); + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + friend class basic_view; template - static constexpr std::size_t index_of = type_list_index_v, type_list>; + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + [[nodiscard]] auto opaque_check_set() const noexcept { + std::array other{}; + std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); + return other; + } + + void unchecked_refresh() noexcept { + view = std::get<0>(pools); + std::apply([this](auto *, auto *...other) { ((this->view = other->size() < this->view->size() ? other : this->view), ...); }, pools); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return std::get(pools)->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: std::get(pools)->each()) { + if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || std::get(pools)->contains(entt)) && ...) && internal::none_of(filter, entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + ((std::get(pools) == view ? each(func, seq) : void()), ...); + } public: /*! @brief Underlying entity identifier. */ @@ -20801,143 +22502,153 @@ class basic_group, get_t, exclude_t> { /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = basic_common_type; - /*! @brief Random access iterator type. */ - using iterator = typename base_type::iterator; - /*! @brief Reversed iterator type. */ - using reverse_iterator = typename base_type::reverse_iterator; - /*! @brief Iterable group type. */ - using iterable = iterable_adaptor, get_t>>; + using common_type = base_type; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::view_iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; - /*! @brief Default constructor to use to create empty, invalid groups. */ - basic_group() noexcept - : handler{} {} + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : pools{}, + filter{}, + view{} {} /** - * @brief Constructs a group from a set of storage classes. - * @param ref The actual entities to iterate. - * @param gpool Storage types to iterate _observed_ by the group. + * @brief Constructs a multi-type view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. */ - basic_group(basic_common_type &ref, Get &...gpool) noexcept - : handler{&ref}, - pools{&gpool...} {} + basic_view(Get &...value, Exclude &...excl) noexcept + : pools{&value...}, + filter{&excl...}, + view{} { + unchecked_refresh(); + } /** - * @brief Returns a const reference to the underlying handler. - * @return A const reference to the underlying handler. + * @brief Constructs a multi-type view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. */ - [[nodiscard]] const base_type &handle() const noexcept { - return *handler; - } + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} /** - * @brief Returns the storage for a given component type. - * @tparam Type Type of component of which to return the storage. - * @return The storage for the given component type. + * @brief Forces a view to use a given component to drive iterations + * @tparam Type Type of component to use to drive iterations. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { - return storage>(); + void use() noexcept { + use>(); } /** - * @brief Returns the storage for a given index. - * @tparam Index Index of the storage to return. - * @return The storage for the given index. + * @brief Forces a view to use a given component to drive iterations + * @tparam Index Index of the component to use to drive iterations. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); + void use() noexcept { + if(view) { + view = std::get(pools); + } + } + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + if(view || std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) { + unchecked_refresh(); + } } /** - * @brief Returns the number of entities that are part of the group. - * @return Number of entities that are part of the group. + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. */ - [[nodiscard]] size_type size() const noexcept { - return *this ? handler->size() : size_type{}; + [[nodiscard]] const common_type *handle() const noexcept { + return view; } /** - * @brief Returns the number of elements that a group has currently - * allocated space for. - * @return Capacity of the group. + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. */ - [[nodiscard]] size_type capacity() const noexcept { - return *this ? handler->capacity() : size_type{}; + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); } - /*! @brief Requests the removal of unused capacity. */ - void shrink_to_fit() { - if(*this) { - handler->shrink_to_fit(); + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + if constexpr(Index < offset) { + return std::get(pools); + } else { + return std::get(internal::filter_as_tuple(filter)); } } /** - * @brief Checks whether a group is empty. - * @return True if the group is empty, false otherwise. + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. */ - [[nodiscard]] bool empty() const noexcept { - return !*this || handler->empty(); + template + void storage(Type &elem) noexcept { + storage>(elem); } /** - * @brief Returns an iterator to the first entity of the group. - * - * The returned iterator points to the first entity of the group. If the - * group is empty, the returned iterator will be equal to `end()`. - * - * @return An iterator to the first entity of the group. + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. */ - [[nodiscard]] iterator begin() const noexcept { - return *this ? handler->begin() : iterator{}; + template + void storage(Type &elem) noexcept { + if constexpr(Index < offset) { + std::get(pools) = &elem; + refresh(); + } else { + std::get(filter) = &elem; + } } /** - * @brief Returns an iterator that is past the last entity of the group. - * - * The returned iterator points to the entity following the last entity of - * the group. Attempting to dereference the returned iterator results in - * undefined behavior. - * - * @return An iterator to the entity following the last entity of the - * group. + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. */ - [[nodiscard]] iterator end() const noexcept { - return *this ? handler->end() : iterator{}; + [[nodiscard]] size_type size_hint() const noexcept { + return view ? view->size() : size_type{}; } /** - * @brief Returns an iterator to the first entity of the reversed group. + * @brief Returns an iterator to the first entity of the view. * - * The returned iterator points to the first entity of the reversed group. - * If the group is empty, the returned iterator will be equal to `rend()`. + * If the view is empty, the returned iterator will be equal to `end()`. * - * @return An iterator to the first entity of the reversed group. + * @return An iterator to the first entity of the view. */ - [[nodiscard]] reverse_iterator rbegin() const noexcept { - return *this ? handler->rbegin() : reverse_iterator{}; + [[nodiscard]] iterator begin() const noexcept { + return view ? iterator{view->begin(), view->end(), opaque_check_set(), filter} : iterator{}; } /** - * @brief Returns an iterator that is past the last entity of the reversed - * group. - * - * The returned iterator points to the entity following the last entity of - * the reversed group. Attempting to dereference the returned iterator - * results in undefined behavior. - * - * @return An iterator to the entity following the last entity of the - * reversed group. + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. */ - [[nodiscard]] reverse_iterator rend() const noexcept { - return *this ? handler->rend() : reverse_iterator{}; + [[nodiscard]] iterator end() const noexcept { + return view ? iterator{view->end(), view->end(), opaque_check_set(), filter} : iterator{}; } /** - * @brief Returns the first entity of the group, if any. - * @return The first entity of the group if one exists, the null entity + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { @@ -20946,13 +22657,18 @@ class basic_group, get_t, exclude_t> { } /** - * @brief Returns the last entity of the group, if any. - * @return The last entity of the group if one exists, the null entity + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { - const auto it = rbegin(); - return it != rend() ? *it : null; + if(view) { + auto it = view->rbegin(); + for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} + return it == view->rend() ? null : *it; + } + + return null; } /** @@ -20962,59 +22678,70 @@ class basic_group, get_t, exclude_t> { * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { - const auto it = *this ? handler->find(entt) : iterator{}; - return it != end() && *it == entt ? it : end(); + return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter} : end(); } /** - * @brief Returns the identifier that occupies the given position. - * @param pos Position of the element to return. - * @return The identifier that occupies the given position. + * @brief Returns the components assigned to the given entity. + * @param entt A valid identifier. + * @return The components assigned to the given entity. */ - [[nodiscard]] entity_type operator[](const size_type pos) const { - return begin()[pos]; + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return get(entt); } /** - * @brief Checks if a group is properly initialized. - * @return True if the group is properly initialized, false otherwise. + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return handler != nullptr; + return std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools) + && std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, filter); } /** - * @brief Checks if a group contains an entity. + * @brief Checks if a view contains an entity. * @param entt A valid identifier. - * @return True if the group contains the given entity, false otherwise. + * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { - return *this && handler->contains(entt); + return view && std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && internal::none_of(filter, entt); } /** * @brief Returns the components assigned to the given entity. * - * Prefer this function instead of `registry::get` during iterations. It has - * far better performance than its counterpart. - * * @warning - * Attempting to use an invalid component type results in a compilation - * error. Attempting to use an entity that doesn't belong to the group - * results in undefined behavior. + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior. * - * @tparam Type Types of components to get. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ - template + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * + * @sa get + * + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { + if constexpr(sizeof...(Index) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); - } else if constexpr(sizeof...(Type) == 1) { - return (std::get>(pools)->get(entt), ...); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(pools)->get(entt), ...); } else { - return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); + return std::tuple_cat(std::get(pools)->get_as_tuple(entt)...); } } @@ -21033,139 +22760,54 @@ class basic_group, get_t, exclude_t> { * void(Type &...); * @endcode * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. - * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { - for(const auto entt: *this) { - if constexpr(is_applicable_v{}, std::declval().get({})))>) { - std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); - } else { - std::apply(func, get(entt)); - } - } + view ? pick_and_each(func, std::index_sequence_for{}) : void(); } /** - * @brief Returns an iterable object to use to _visit_ a group. + * @brief Returns an iterable object to use to _visit_ a view. * - * The iterable object returns tuples that contain the current entity and a - * set of references to its non-empty components. The _constness_ of the + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the * components is as requested. * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. - * - * @return An iterable object to use to _visit_ the group. + * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { - return iterable{{begin(), pools}, {end(), pools}}; - } - - /** - * @brief Sort a group according to the given comparison function. - * - * Sort the group so that iterating it with a couple of iterators returns - * entities and components in the expected order. See `begin` and `end` for - * more details. - * - * The comparison function object must return `true` if the first element - * is _less_ than the second one, `false` otherwise. The signature of the - * comparison function should be equivalent to one of the following: - * - * @code{.cpp} - * bool(std::tuple, std::tuple); - * bool(const Type &..., const Type &...); - * bool(const Entity, const Entity); - * @endcode - * - * Where `Type` are such that they are iterated by the group.
- * Moreover, the comparison function object shall induce a - * _strict weak ordering_ on the values. - * - * The sort function object must offer a member function template - * `operator()` that accepts three arguments: - * - * * An iterator to the first element of the range to sort. - * * An iterator past the last element of the range to sort. - * * A comparison function to use to compare the elements. - * - * @tparam Type Optional types of components to compare. - * @tparam Compare Type of comparison function object. - * @tparam Sort Type of sort function object. - * @tparam Args Types of arguments to forward to the sort function object. - * @param compare A valid comparison function object. - * @param algo A valid sort function object. - * @param args Arguments to forward to the sort function object, if any. - */ - template - void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { - if(*this) { - if constexpr(sizeof...(Type) == 0) { - static_assert(std::is_invocable_v, "Invalid comparison function"); - handler->sort(std::move(compare), std::move(algo), std::forward(args)...); - } else { - auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { - if constexpr(sizeof...(Type) == 1) { - return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); - } else { - return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); - } - }; - - handler->sort(std::move(comp), std::move(algo), std::forward(args)...); - } - } + return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; } /** - * @brief Sort the shared pool of entities according to the given component. - * - * Non-owning groups of the same type share with the registry a pool of - * entities with its own order that doesn't depend on the order of any pool - * of components. Users can order the underlying data structure so that it - * respects the order of the pool of the given component. - * - * @note - * The shared pool of entities and thus its order is affected by the changes - * to each and every pool that it tracks. Therefore changes to those pools - * can quickly ruin the order imposed to the pool of entities shared between - * the non-owning groups. - * - * @tparam Type Type of component to use to impose the order. + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. */ - template - void sort() const { - if(*this) { - handler->respect(*std::get>(pools)); - } + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack( + std::tuple_cat(pools, other.pools), + std::tuple_cat(internal::filter_as_tuple(filter), internal::filter_as_tuple(other.filter)), + std::index_sequence_for{}); } private: - base_type *const handler; - const std::tuple pools; + std::tuple pools; + std::array filter; + const common_type *view; }; /** - * @brief Owning group. - * - * Owning groups returns all entities and only the entities that are at - * least in the given storage. Moreover: - * - * * It's guaranteed that the entity list is tightly packed in memory for fast - * iterations. - * * It's guaranteed that all components in the owned storage are tightly packed - * in memory for even faster iterations and to allow direct access. - * * They stay true to the order of the owned storage and all instances have the - * same order in memory. + * @brief Single component view specialization. * - * The more types of storage are owned, the faster it is to iterate a group. + * Single component views are specialized in order to get a boost in terms of + * performance. This kind of views can access the underlying data structure + * directly and avoid superfluous checks. * * @b Important * @@ -21176,156 +22818,171 @@ class basic_group, get_t, exclude_t> { * or removed from it). * * The entity currently pointed is destroyed. * - * In all other cases, modifying the pools iterated by the group in any way - * invalidates all the iterators and using them results in undefined behavior. + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. * - * @tparam Owned Types of storage _owned_ by the group. - * @tparam Get Types of storage _observed_ by the group. - * @tparam Exclude Types of storage used to filter the group. + * @tparam Get Type of storage iterated by the view. */ -template -class basic_group, get_t, exclude_t> { - using underlying_type = std::common_type_t; - using basic_common_type = std::common_type_t; - - template - static constexpr std::size_t index_of = type_list_index_v, type_list>; +template +class basic_view, exclude_t<>, std::void_t>> { + template + friend class basic_view; public: /*! @brief Underlying entity identifier. */ - using entity_type = underlying_type; + using entity_type = typename Get::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = basic_common_type; + using common_type = typename Get::base_type; /*! @brief Random access iterator type. */ - using iterator = typename base_type::iterator; + using iterator = typename common_type::iterator; /*! @brief Reversed iterator type. */ - using reverse_iterator = typename base_type::reverse_iterator; - /*! @brief Iterable group type. */ - using iterable = iterable_adaptor, get_t>>; + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = decltype(std::declval().each()); - /*! @brief Default constructor to use to create empty, invalid groups. */ - basic_group() noexcept - : length{} {} + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : pools{}, + filter{}, + view{} {} /** - * @brief Constructs a group from a set of storage classes. - * @param extent The actual number of entities to iterate. - * @param opool Storage types to iterate _owned_ by the group. - * @param gpool Storage types to iterate _observed_ by the group. + * @brief Constructs a single-type view from a storage class. + * @param value The storage for the type to iterate. */ - basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept - : pools{&opool..., &gpool...}, - length{&extent} {} + basic_view(Get &value) noexcept + : pools{&value}, + filter{}, + view{&value} {} /** - * @brief Returns the storage for a given component type. + * @brief Constructs a single-type view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return view; + } + + /** + * @brief Returns the storage for a given component type, if any. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ - template - [[nodiscard]] decltype(auto) storage() const noexcept { - return storage>(); + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return storage<0>(); } /** - * @brief Returns the storage for a given index. + * @brief Returns the storage for a given index, if any. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); + [[nodiscard]] auto *storage() const noexcept { + return std::get(pools); } /** - * @brief Returns the number of entities that that are part of the group. - * @return Number of entities that that are part of the group. + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + view = std::get(pools) = &elem; + } + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. */ [[nodiscard]] size_type size() const noexcept { - return *this ? *length : size_type{}; + return view ? view->size() : size_type{}; } /** - * @brief Checks whether a group is empty. - * @return True if the group is empty, false otherwise. + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { - return !*this || !*length; + return !view || view->empty(); } /** - * @brief Returns an iterator to the first entity of the group. + * @brief Returns an iterator to the first entity of the view. * - * The returned iterator points to the first entity of the group. If the - * group is empty, the returned iterator will be equal to `end()`. + * If the view is empty, the returned iterator will be equal to `end()`. * - * @return An iterator to the first entity of the group. + * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const noexcept { - return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; + return view ? view->begin() : iterator{}; } /** - * @brief Returns an iterator that is past the last entity of the group. - * - * The returned iterator points to the entity following the last entity of - * the group. Attempting to dereference the returned iterator results in - * undefined behavior. - * - * @return An iterator to the entity following the last entity of the - * group. + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { - return *this ? std::get<0>(pools)->base_type::end() : iterator{}; + return view ? view->end() : iterator{}; } /** - * @brief Returns an iterator to the first entity of the reversed group. + * @brief Returns an iterator to the first entity of the reversed view. * - * The returned iterator points to the first entity of the reversed group. - * If the group is empty, the returned iterator will be equal to `rend()`. + * If the view is empty, the returned iterator will be equal to `rend()`. * - * @return An iterator to the first entity of the reversed group. + * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { - return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; + return view ? view->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed - * group. - * - * The returned iterator points to the entity following the last entity of - * the reversed group. Attempting to dereference the returned iterator - * results in undefined behavior. - * + * view. * @return An iterator to the entity following the last entity of the - * reversed group. + * reversed view. */ [[nodiscard]] reverse_iterator rend() const noexcept { - return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; + return view ? view->rend() : reverse_iterator{}; } /** - * @brief Returns the first entity of the group, if any. - * @return The first entity of the group if one exists, the null entity + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { - const auto it = begin(); - return it != end() ? *it : null; + return (!view || view->empty()) ? null : *view->begin(); } /** - * @brief Returns the last entity of the group, if any. - * @return The last entity of the group if one exists, the null entity + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { - const auto it = rbegin(); - return it != rend() ? *it : null; + return (!view || view->empty()) ? null : *view->rbegin(); } /** @@ -21335,8 +22992,7 @@ class basic_group, get_t, exclude_t> { * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { - const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; - return it != end() && it >= begin() && *it == entt ? it : end(); + return view ? view->find(entt) : iterator{}; } /** @@ -21349,45 +23005,55 @@ class basic_group, get_t, exclude_t> { } /** - * @brief Checks if a group is properly initialized. - * @return True if the group is properly initialized, false otherwise. + * @brief Returns the component assigned to the given entity. + * @param entt A valid identifier. + * @return The component assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return std::get<0>(pools)->get(entt); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return length != nullptr; + return (std::get<0>(pools) != nullptr); } /** - * @brief Checks if a group contains an entity. + * @brief Checks if a view contains an entity. * @param entt A valid identifier. - * @return True if the group contains the given entity, false otherwise. + * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { - return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); + return view && view->contains(entt); } /** - * @brief Returns the components assigned to the given entity. - * - * Prefer this function instead of `registry::get` during iterations. It has - * far better performance than its counterpart. + * @brief Returns the component assigned to the given entity. * * @warning - * Attempting to use an invalid component type results in a compilation - * error. Attempting to use an entity that doesn't belong to the group - * results in undefined behavior. + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior. * - * @tparam Type Types of components to get. + * @tparam Elem Type or index of the component to get. * @param entt A valid identifier. - * @return The components assigned to the entity. + * @return The component assigned to the entity. */ - template + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { - return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); - } else if constexpr(sizeof...(Type) == 1) { - return (std::get>(pools)->get(entt), ...); + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return get<0>(entt); + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Elem) == 0) { + return std::get<0>(pools)->get_as_tuple(entt); } else { - return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); + return std::get(pools)->get(entt); } } @@ -21396,14 +23062,14 @@ class basic_group, get_t, exclude_t> { * object to them. * * The function object is invoked for each entity. It is provided with the - * entity itself and a set of references to non-empty components. The - * _constness_ of the components is as requested.
+ * entity itself and a reference to the component if it's a non-empty one. + * The _constness_ of the component is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} - * void(const entity_type, Type &...); - * void(Type &...); + * void(const entity_type, Type &); + * void(typename Type &); * @endcode * * @note @@ -21415,129 +23081,357 @@ class basic_group, get_t, exclude_t> { */ template void each(Func func) const { - for(auto args: each()) { - if constexpr(is_applicable_v{}, std::declval().get({})))>) { - std::apply(func, args); + if(view) { + if constexpr(is_applicable_v) { + for(const auto pack: each()) { + std::apply(func, pack); + } + } else if constexpr(Get::traits_type::page_size == 0u) { + for(size_type pos{}, last = size(); pos < last; ++pos) { + func(); + } } else { - std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); + for(auto &&component: *std::get<0>(pools)) { + func(component); + } } } } /** - * @brief Returns an iterable object to use to _visit_ a group. - * - * The iterable object returns tuples that contain the current entity and a - * set of references to its non-empty components. The _constness_ of the - * components is as requested. + * @brief Returns an iterable object to use to _visit_ a view. * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. + * The iterable object returns a tuple that contains the current entity and + * a reference to its component if it's a non-empty one. The _constness_ of + * the component is as requested. * - * @return An iterable object to use to _visit_ the group. + * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { - return {{begin(), pools}, {end(), pools}}; + return view ? std::get<0>(pools)->each() : iterable{}; + } + + /** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack( + std::tuple_cat(pools, other.pools), + internal::filter_as_tuple(other.filter), + std::index_sequence_for{}); + } + +private: + std::tuple pools; + std::array filter; + const common_type *view; +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Type &...storage) -> basic_view, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template +basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template +class as_view { + template + auto dispatch(get_t, exclude_t) const { + return reg.template view...>(exclude_t...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template +class as_group { + template + auto dispatch(owned_t, get_t, exclude_t) const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(get_t{}, exclude_t{}); + } else { + return reg.template group...>(get_t...>{}, exclude_t...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Registry Basic registry type. + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default pool as it + * makes assumptions about how the components are laid out. + * + * @tparam Registry Basic registry type. + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +typename Registry::entity_type to_entity(const Registry ®, const Component &instance) { + if(const auto *storage = reg.template storage(); storage) { + constexpr auto page_size = std::remove_const_t>::traits_type::page_size; + const typename Registry::common_type &base = *storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) { + if(const auto dist = (addr - std::addressof(storage->get(*it))); dist >= 0 && dist < static_cast(page_size)) { + return *(it + dist); + } + } + } + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; } /** - * @brief Sort a group according to the given comparison function. - * - * Sort the group so that iterating it with a couple of iterators returns - * entities and components in the expected order. See `begin` and `end` for - * more details. - * - * The comparison function object must return `true` if the first element - * is _less_ than the second one, `false` otherwise. The signature of the - * comparison function should be equivalent to one of the following: - * - * @code{.cpp} - * bool(std::tuple, std::tuple); - * bool(const Type &, const Type &); - * bool(const Entity, const Entity); - * @endcode - * - * Where `Type` are either owned types or not but still such that they are - * iterated by the group.
- * Moreover, the comparison function object shall induce a - * _strict weak ordering_ on the values. - * - * The sort function object must offer a member function template - * `operator()` that accepts three arguments: - * - * * An iterator to the first element of the range to sort. - * * An iterator past the last element of the range to sort. - * * A comparison function to use to compare the elements. - * - * @tparam Type Optional types of components to compare. - * @tparam Compare Type of comparison function object. - * @tparam Sort Type of sort function object. - * @tparam Args Types of arguments to forward to the sort function object. - * @param compare A valid comparison function object. - * @param algo A valid sort function object. - * @param args Arguments to forward to the sort function object, if any. + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. */ - template - void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { - if constexpr(sizeof...(Type) == 0) { - static_assert(std::is_invocable_v, "Invalid comparison function"); - std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); - } else { - auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { - if constexpr(sizeof...(Type) == 1) { - return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); - } else { - return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); - } - }; - - std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); - } + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } - std::apply([this](auto *head, auto *...other) { - for(auto next = *length; next; --next) { - const auto pos = next - 1; - [[maybe_unused]] const auto entt = head->data()[pos]; - (other->swap_elements(other->data()[pos], entt), ...); - } - }, - pools); + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; } private: - const std::tuple pools; - const size_type *const length; + id_type name; }; +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + } // namespace entt #endif -// #include "view.hpp" -#ifndef ENTT_ENTITY_VIEW_HPP -#define ENTT_ENTITY_VIEW_HPP +// #include "entity/mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP -#include -#include -#include #include #include // #include "../config/config.h" -// #include "../core/iterator.hpp" - -// #include "../core/type_traits.hpp" +// #include "../core/any.hpp" -// #include "component.hpp" +// #include "../signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP -// #include "entity.hpp" +#include +#include +#include +#include +#include +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP -// #include "fwd.hpp" +#include +#include +#include +#include +#include +// #include "../config/config.h" -// #include "sparse_set.hpp" +// #include "../core/type_traits.hpp" -// #include "storage.hpp" +// #include "fwd.hpp" namespace entt { @@ -21549,971 +23443,1082 @@ namespace entt { namespace internal { -template -class view_iterator final { - using iterator_type = typename Type::const_iterator; +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); - [[nodiscard]] bool valid() const noexcept { - return ((Get != 0u) || (*it != tombstone)) - && std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) - && std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); - } +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); -public: - using value_type = typename iterator_type::value_type; - using pointer = typename iterator_type::pointer; - using reference = typename iterator_type::reference; - using difference_type = typename iterator_type::difference_type; - using iterator_category = std::forward_iterator_tag; +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); - constexpr view_iterator() noexcept - : it{}, - last{}, - pools{}, - filter{} {} +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); - view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) noexcept - : it{curr}, - last{to}, - pools{all_of}, - filter{none_of} { - while(it != last && !valid()) { - ++it; - } +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; } - view_iterator &operator++() noexcept { - while(++it != last && !valid()) {} - return *this; + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; } - view_iterator operator++(int) noexcept { - view_iterator orig = *this; - return ++(*this), orig; + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; } - [[nodiscard]] pointer operator->() const noexcept { - return &*it; +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); } - [[nodiscard]] reference operator*() const noexcept { - return *operator->(); + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); } - template - friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() noexcept { + instance = nullptr; -private: - iterator_type it; - iterator_type last; - std::array pools; - std::array filter; -}; + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } -template -[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { - return lhs.it == rhs.it; -} + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) noexcept { + instance = &value_or_instance; -template -[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { - return !(lhs == rhs); -} + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } -template -struct extended_view_iterator final { - using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); - using pointer = input_iterator_pointer; - using reference = value_type; - using iterator_category = std::input_iterator_tag; + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) noexcept { + instance = value_or_instance; - constexpr extended_view_iterator() - : it{}, - pools{} {} + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } - extended_view_iterator(It from, std::tuple storage) - : it{from}, - pools{storage} {} + /** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } - extended_view_iterator &operator++() noexcept { - return ++it, *this; + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; } - extended_view_iterator operator++(int) noexcept { - extended_view_iterator orig = *this; - return ++(*this), orig; + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; } - [[nodiscard]] reference operator*() const noexcept { - return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; } - [[nodiscard]] pointer operator->() const noexcept { - return operator*(); + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); } - template - friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } private: - It it; - std::tuple pools; + const void *instance; + function_type *fn; }; -template -[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { - return lhs.it == rhs.it; -} - -template -[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } -} // namespace internal - /** - * Internal details not to be documented. - * @endcond + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. */ +template +delegate(connect_arg_t) -> delegate>>; /** - * @brief View implementation. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. */ -template -class basic_view; +template +delegate(connect_arg_t, Type &&) -> delegate>>; /** - * @brief Multi component view. - * - * Multi component views iterate over those entities that are at least in the - * given storage. During initialization, a multi component view looks at the - * number of entities available for each component and uses the smallest set in - * order to get a performance boost when iterating. - * - * @b Important - * - * Iterators aren't invalidated if: - * - * * New elements are added to the storage. - * * The entity currently pointed is modified (for example, components are added - * or removed from it). - * * The entity currently pointed is destroyed. - * - * In all other cases, modifying the pools iterated by the view in any way - * invalidates all the iterators and using them results in undefined behavior. - * - * @tparam Get Types of storage iterated by the view. - * @tparam Exclude Types of storage used to filter the view. + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. */ -template -class basic_view, exclude_t> { - using underlying_type = std::common_type_t; - using basic_common_type = std::common_type_t; +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; - template - friend class basic_view; +} // namespace entt - template - static constexpr std::size_t index_of = type_list_index_v, type_list>; +#endif - [[nodiscard]] auto opaque_check_set() const noexcept { - std::array other{}; - std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); - return other; - } +// #include "fwd.hpp" - [[nodiscard]] auto filter_as_array() const noexcept { - return std::apply([](const auto *...curr) { return std::array{curr...}; }, filter); - } - template - [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { - if constexpr(Curr == Other) { - return std::forward_as_tuple(std::get(curr)...); - } else { - return storage().get_as_tuple(std::get<0>(curr)); - } - } +namespace entt { - [[nodiscard]] auto reject(const underlying_type entt) const noexcept { - return std::apply([entt](const auto *...curr) { return (curr->contains(entt) || ...); }, filter); - } +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template +class sink; - template - void each(Func &func, std::index_sequence) const { - for(const auto curr: storage().each()) { - if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || storage().contains(entt)) && ...) && !reject(entt)) { - if constexpr(is_applicable_v{}, std::declval().get({})))>) { - std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); - } else { - std::apply(func, std::tuple_cat(dispatch_get(curr)...)); - } - } - } - } +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh; - template - void pick_and_each(Func &func, std::index_sequence seq) const { - ((&storage() == view ? each(func, seq) : void()), ...); - } +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + /*! @brief A sink is allowed to modify a signal. */ + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; public: - /*! @brief Underlying entity identifier. */ - using entity_type = underlying_type; + /*! @brief Allocator type. */ + using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; - /*! @brief Common type among all storage types. */ - using base_type = basic_common_type; - /*! @brief Bidirectional iterator type. */ - using iterator = internal::view_iterator; - /*! @brief Iterable view type. */ - using iterable = iterable_adaptor>; + /*! @brief Sink type. */ + using sink_type = sink>; - /*! @brief Default constructor to use to create empty, invalid views. */ - basic_view() noexcept - : pools{}, - filter{}, - view{} {} + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) + : sigh{allocator_type{}} {} /** - * @brief Constructs a multi-type view from a set of storage classes. - * @param value The storage for the types to iterate. - * @param exclude The storage for the types used to filter the view. + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. */ - basic_view(Get &...value, Exclude &...exclude) noexcept - : pools{&value...}, - filter{&exclude...}, - view{[](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }(&value...)} {} + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{allocator} {} /** - * @brief Constructs a multi-type view from a set of storage classes. - * @param value The storage for the types to iterate. - * @param exclude The storage for the types used to filter the view. + * @brief Copy constructor. + * @param other The instance to copy from. */ - basic_view(std::tuple value, std::tuple exclude = {}) noexcept - : pools{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, value)}, - filter{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, exclude)}, - view{std::apply([](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }, pools)} {} + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) + : calls{other.calls} {} /** - * @brief Creates a new view driven by a given component in its iterations. - * @tparam Type Type of component used to drive the iteration. - * @return A new view driven by the given component in its iterations. + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. */ - template - [[nodiscard]] basic_view use() const noexcept { - return use>(); - } + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{other.calls, allocator} {} /** - * @brief Creates a new view driven by a given component in its iterations. - * @tparam Index Index of the component used to drive the iteration. - * @return A new view driven by the given component in its iterations. + * @brief Move constructor. + * @param other The instance to move from. */ - template - [[nodiscard]] basic_view use() const noexcept { - basic_view other{*this}; - other.view = &storage(); - return other; - } + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) + : calls{std::move(other.calls)} {} /** - * @brief Updates the internal leading view if required. - * @return A newly created and internally optimized view. + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. */ - [[nodiscard]] basic_view refresh() const noexcept { - return std::apply([](auto *...elem) { return basic_view{*elem...}; }, std::tuple_cat(pools, filter)); - } + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{std::move(other.calls), allocator} {} /** - * @brief Returns the leading storage of a view. - * @return The leading storage of the view. + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. */ - [[nodiscard]] const base_type &handle() const noexcept { - return *view; + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { + calls = other.calls; + return *this; } /** - * @brief Returns the storage for a given component type. - * @tparam Comp Type of component of which to return the storage. - * @return The storage for the given component type. + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. */ - template - [[nodiscard]] decltype(auto) storage() const noexcept { - return storage>(); + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { + calls = std::move(other.calls); + return *this; } /** - * @brief Returns the storage for a given index. - * @tparam Index Index of the storage to return. - * @return The storage for the given index. + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. */ - template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { + using std::swap; + swap(calls, other.calls); } /** - * @brief Estimates the number of entities iterated by the view. - * @return Estimated number of entities iterated by the view. + * @brief Returns the associated allocator. + * @return The associated allocator. */ - [[nodiscard]] size_type size_hint() const noexcept { - return view->size(); + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); } /** - * @brief Returns an iterator to the first entity of the view. - * - * The returned iterator points to the first entity of the view. If the view - * is empty, the returned iterator will be equal to `end()`. - * - * @return An iterator to the first entity of the view. + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. */ - [[nodiscard]] iterator begin() const noexcept { - return iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; + [[nodiscard]] size_type size() const noexcept { + return calls.size(); } /** - * @brief Returns an iterator that is past the last entity of the view. - * - * The returned iterator points to the entity following the last entity of - * the view. Attempting to dereference the returned iterator results in - * undefined behavior. - * - * @return An iterator to the entity following the last entity of the view. + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. */ - [[nodiscard]] iterator end() const noexcept { - return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()}; + [[nodiscard]] bool empty() const noexcept { + return calls.empty(); } /** - * @brief Returns the first entity of the view, if any. - * @return The first entity of the view if one exists, the null entity - * otherwise. + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. */ - [[nodiscard]] entity_type front() const noexcept { - const auto it = begin(); - return it != end() ? *it : null; + void publish(Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + calls[pos - 1u](args...); + } } /** - * @brief Returns the last entity of the view, if any. - * @return The last entity of the view if one exists, the null entity - * otherwise. + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. */ - [[nodiscard]] entity_type back() const noexcept { - auto it = view->rbegin(); - for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} - return it == view->rend() ? null : *it; + template + void collect(Func func, Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } } +private: + container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + /*! @brief A sink is allowed to create connection objects. */ + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + /** - * @brief Finds an entity. - * @param entt A valid identifier. - * @return An iterator to the given entity if it's found, past the end - * iterator otherwise. + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] iterator find(const entity_type entt) const noexcept { - return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter_as_array()} : end(); + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(disconnect); } - /** - * @brief Returns the components assigned to the given entity. - * @param entt A valid identifier. - * @return The components assigned to the given entity. - */ - [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { - return get(entt); + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } } +private: + delegate disconnect; + void *signal; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + /** - * @brief Checks if a view is properly initialized. - * @return True if the view is properly initialized, false otherwise. + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. */ - [[nodiscard]] explicit operator bool() const noexcept { - return view != nullptr; - } + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; /** - * @brief Checks if a view contains an entity. - * @param entt A valid identifier. - * @return True if the view contains the given entity, false otherwise. + * @brief Move constructor. + * @param other The scoped connection to move from. */ - [[nodiscard]] bool contains(const entity_type entt) const noexcept { - return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) - && std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); } /** - * @brief Returns the components assigned to the given entity. - * - * @warning - * Attempting to use an entity that doesn't belong to the view results in - * undefined behavior. - * - * @tparam Type Types of components to get. - * @param entt A valid identifier. - * @return The components assigned to the entity. + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { - return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); - } else if constexpr(sizeof...(Type) == 1) { - return (storage>().get(entt), ...); - } else { - return std::tuple_cat(storage>().get_as_tuple(entt)...); - } - } + scoped_connection &operator=(const scoped_connection &) = delete; /** - * @brief Returns the components assigned to the given entity. - * - * @warning - * Attempting to use an entity that doesn't belong to the view results in - * undefined behavior. - * - * @tparam First Index of a component to get. - * @tparam Other Indexes of other components to get. - * @param entt A valid identifier. - * @return The components assigned to the entity. + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Other) == 0) { - return storage().get(entt); - } else { - return std::tuple_cat(storage().get_as_tuple(entt), storage().get_as_tuple(entt)...); - } + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; } /** - * @brief Iterates entities and components and applies the given function - * object to them. - * - * The function object is invoked for each entity. It is provided with the - * entity itself and a set of references to non-empty components. The - * _constness_ of the components is as requested.
- * The signature of the function must be equivalent to one of the following - * forms: - * - * @code{.cpp} - * void(const entity_type, Type &...); - * void(Type &...); - * @endcode - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. */ - template - void each(Func func) const { - pick_and_each(func, std::index_sequence_for{}); + scoped_connection &operator=(connection other) { + conn = std::move(other); + return *this; } /** - * @brief Returns an iterable object to use to _visit_ a view. - * - * The iterable object returns a tuple that contains the current entity and - * a set of references to its non-empty components. The _constness_ of the - * components is as requested. - * - * @return An iterable object to use to _visit_ the view. + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] iterable each() const noexcept { - return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(conn); } - /** - * @brief Combines two views in a _more specific_ one (friend function). - * @tparam OGet Component list of the view to combine with. - * @tparam OExclude Filter list of the view to combine with. - * @param other The view to combine with. - * @return A more specific view. - */ - template - [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { - return std::make_from_tuple, exclude_t>>( - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, filter, other.filter))); + /*! @brief Breaks the connection. */ + void release() { + conn.release(); } private: - std::tuple pools; - std::tuple filter; - const base_type *view; + connection conn; }; /** - * @brief Single component view specialization. - * - * Single component views are specialized in order to get a boost in terms of - * performance. This kind of views can access the underlying data structure - * directly and avoid superfluous checks. - * - * @b Important + * @brief Sink class. * - * Iterators aren't invalidated if: + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. * - * * New elements are added to the storage. - * * The entity currently pointed is modified (for example, components are added - * or removed from it). - * * The entity currently pointed is destroyed. + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. * - * In all other cases, modifying the pool iterated by the view in any way - * invalidates all the iterators and using them results in undefined behavior. + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. * - * @tparam Get Type of storage iterated by the view. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. */ -template -class basic_view, exclude_t<>, std::void_t::in_place_delete>>> { - template - friend class basic_view; +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::difference_type; -public: - /*! @brief Underlying entity identifier. */ - using entity_type = typename Get::entity_type; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Common type among all storage types. */ - using base_type = typename Get::base_type; - /*! @brief Random access iterator type. */ - using iterator = typename base_type::iterator; - /*! @brief Reversed iterator type. */ - using reverse_iterator = typename base_type::reverse_iterator; - /*! @brief Iterable view type. */ - using iterable = decltype(std::declval().each()); + template + static void release(Type value_or_instance, void *signal) { + sink{*static_cast(signal)}.disconnect(value_or_instance); + } - /*! @brief Default constructor to use to create empty, invalid views. */ - basic_view() noexcept - : pools{}, - filter{} {} + template + static void release(void *signal) { + sink{*static_cast(signal)}.disconnect(); + } - /** - * @brief Constructs a single-type view from a storage class. - * @param ref The storage for the type to iterate. - */ - basic_view(Get &ref) noexcept - : pools{&ref}, - filter{} {} + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } +public: /** - * @brief Constructs a single-type view from a storage class. - * @param ref The storage for the type to iterate. + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. */ - basic_view(std::tuple ref, std::tuple<> = {}) noexcept - : pools{&std::get<0>(ref)}, - filter{} {} + sink(sigh &ref) noexcept + : signal{&ref} {} /** - * @brief Returns the leading storage of a view. - * @return The leading storage of the view. + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. */ - [[nodiscard]] const base_type &handle() const noexcept { - return storage(); + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); } /** - * @brief Returns the storage for a given component type. - * @tparam Type Type of component of which to return the storage. - * @return The storage for the given component type. + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. */ - template - [[nodiscard]] decltype(auto) storage() const noexcept { - static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); - return storage<0>(); + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); + + delegate_type call{}; + call.template connect(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance...); + return {std::move(conn), signal}; } /** - * @brief Returns the storage for a given index. - * @tparam Index Index of the storage to return. - * @return The storage for the given index. + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. */ - template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); + template + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); } /** - * @brief Returns the number of entities that have the given component. - * @return Number of entities that have the given component. + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. */ - [[nodiscard]] size_type size() const noexcept { - return handle().size(); + void disconnect(const void *value_or_instance) { + if(value_or_instance) { + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } } - /** - * @brief Checks whether a view is empty. - * @return True if the view is empty, false otherwise. - */ - [[nodiscard]] bool empty() const noexcept { - return handle().empty(); + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type The type of the underlying storage. + */ +template +class sigh_mixin final: public Type { + using underlying_type = Type; + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + basic_registry_type &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return *owner; } - /** - * @brief Returns an iterator to the first entity of the view. - * - * The returned iterator points to the first entity of the view. If the view - * is empty, the returned iterator will be equal to `end()`. - * - * @return An iterator to the first entity of the view. - */ - [[nodiscard]] iterator begin() const noexcept { - return handle().begin(); + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } } - /** - * @brief Returns an iterator that is past the last entity of the view. - * - * The returned iterator points to the entity following the last entity of - * the view. Attempting to dereference the returned iterator results in - * undefined behavior. - * - * @return An iterator to the entity following the last entity of the view. - */ - [[nodiscard]] iterator end() const noexcept { - return handle().end(); - } + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + for(auto pos = underlying_type::each().begin().base().index(); !(pos < 0); --pos) { + if constexpr(underlying_type::traits_type::in_place_delete) { + if(const auto entt = underlying_type::operator[](static_cast(pos)); entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, underlying_type::operator[](static_cast(pos))); + } + } + } - /** - * @brief Returns an iterator to the first entity of the reversed view. - * - * The returned iterator points to the first entity of the reversed view. If - * the view is empty, the returned iterator will be equal to `rend()`. - * - * @return An iterator to the first entity of the reversed view. - */ - [[nodiscard]] reverse_iterator rbegin() const noexcept { - return handle().rbegin(); + underlying_type::pop_all(); } - /** - * @brief Returns an iterator that is past the last entity of the reversed - * view. - * - * The returned iterator points to the entity following the last entity of - * the reversed view. Attempting to dereference the returned iterator - * results in undefined behavior. - * - * @return An iterator to the entity following the last entity of the - * reversed view. - */ - [[nodiscard]] reverse_iterator rend() const noexcept { - return handle().rend(); - } + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); - /** - * @brief Returns the first entity of the view, if any. - * @return The first entity of the view if one exists, the null entity - * otherwise. - */ - [[nodiscard]] entity_type front() const noexcept { - return empty() ? null : *begin(); + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; } +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = basic_registry_type; + + /*! @brief Default constructor. */ + sigh_mixin() + : sigh_mixin{allocator_type{}} {} + /** - * @brief Returns the last entity of the view, if any. - * @return The last entity of the view if one exists, the null entity - * otherwise. + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. */ - [[nodiscard]] entity_type back() const noexcept { - return empty() ? null : *rbegin(); - } + explicit sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} /** - * @brief Finds an entity. - * @param entt A valid identifier. - * @return An iterator to the given entity if it's found, past the end - * iterator otherwise. + * @brief Move constructor. + * @param other The instance to move from. */ - [[nodiscard]] iterator find(const entity_type entt) const noexcept { - return contains(entt) ? handle().find(entt) : end(); - } + sigh_mixin(sigh_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} /** - * @brief Returns the identifier that occupies the given position. - * @param pos Position of the element to return. - * @return The identifier that occupies the given position. + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. */ - [[nodiscard]] entity_type operator[](const size_type pos) const { - return begin()[pos]; - } + sigh_mixin(sigh_mixin &&other, const allocator_type &allocator) noexcept + : underlying_type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} /** - * @brief Returns the component assigned to the given entity. - * @param entt A valid identifier. - * @return The component assigned to the given entity. + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. */ - [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { - return storage().get(entt); + sigh_mixin &operator=(sigh_mixin &&other) noexcept { + underlying_type::operator=(std::move(other)); + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + return *this; } /** - * @brief Checks if a view is properly initialized. - * @return True if the view is properly initialized, false otherwise. + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. */ - [[nodiscard]] explicit operator bool() const noexcept { - return std::get<0>(pools) != nullptr; + void swap(sigh_mixin &other) { + using std::swap; + underlying_type::swap(other); + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); } /** - * @brief Checks if a view contains an entity. - * @param entt A valid identifier. - * @return True if the view contains the given entity, false otherwise. + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. */ - [[nodiscard]] bool contains(const entity_type entt) const noexcept { - return handle().contains(entt); + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; } /** - * @brief Returns the component assigned to the given entity. + * @brief Returns a sink object. * - * @warning - * Attempting to use an entity that doesn't belong to the view results in - * undefined behavior. + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. * - * @tparam Type Type or index of the component to get. - * @param entt A valid identifier. - * @return The component assigned to the entity. + * @sa sink + * + * @return A temporary sink object. */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { - return storage().get_as_tuple(entt); - } else { - static_assert((std::is_same_v, typename Get::value_type> && ...), "Invalid component type"); - return storage().get(entt); - } - } - - /*! @copydoc get */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - return storage().get(entt); + [[nodiscard]] auto on_update() noexcept { + return sink{update}; } /** - * @brief Iterates entities and components and applies the given function - * object to them. - * - * The function object is invoked for each entity. It is provided with the - * entity itself and a reference to the component if it's a non-empty one. - * The _constness_ of the component is as requested.
- * The signature of the function must be equivalent to one of the following - * forms: + * @brief Returns a sink object. * - * @code{.cpp} - * void(const entity_type, Type &); - * void(typename Type &); - * @endcode + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned during iterations. + * @sa sink * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. + * @return A temporary sink object. */ - template - void each(Func func) const { - if constexpr(is_applicable_v) { - for(const auto pack: each()) { - std::apply(func, pack); - } - } else if constexpr(ignore_as_empty_v) { - for(size_type pos{}, last = size(); pos < last; ++pos) { - func(); - } - } else { - for(auto &&component: storage()) { - func(component); - } - } + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; } /** - * @brief Returns an iterable object to use to _visit_ a view. + * @brief Emplace elements into a storage. * - * The iterable object returns a tuple that contains the current entity and - * a reference to its component if it's a non-empty one. The _constness_ of - * the component is as requested. + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. * - * @return An iterable object to use to _visit_ the view. + * @return A return value as returned by the underlying storage. */ - [[nodiscard]] iterable each() const noexcept { - return storage().each(); + auto emplace() { + const auto entt = underlying_type::emplace(); + construction.publish(owner_or_assert(), entt); + return entt; } /** - * @brief Combines two views in a _more specific_ one (friend function). - * @tparam OGet Component list of the view to combine with. - * @tparam OExclude Filter list of the view to combine with. - * @param other The view to combine with. - * @return A more specific view. + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam Args Types of arguments to forward to the underlying storage. + * @param hint A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A return value as returned by the underlying storage. */ - template - [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { - return std::make_from_tuple, exclude_t>>( - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, other.filter))); - } - -private: - std::tuple pools; - std::tuple<> filter; -}; - -/** - * @brief Deduction guide. - * @tparam Type Type of storage classes used to create the view. - * @param storage The storage for the types to iterate. - */ -template -basic_view(Type &...storage) -> basic_view, exclude_t<>>; - -/** - * @brief Deduction guide. - * @tparam Get Types of components iterated by the view. - * @tparam Exclude Types of components used to filter the view. - */ -template -basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; - -} // namespace entt - -#endif - - -namespace entt { - -/** - * @brief Converts a registry to a view. - * @tparam Registry Basic registry type. - */ -template -class as_view { - template - auto dispatch(get_t, exclude_t) const { - return reg.template view...>(exclude_t...>{}); + template + decltype(auto) emplace(const entity_type hint, Args &&...args) { + if constexpr(std::is_same_v) { + const auto entt = underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return entt; + } else { + underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), hint); + return this->get(hint); + } } -public: - /*! @brief Type of registry to convert. */ - using registry_type = Registry; - /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; - /** - * @brief Constructs a converter for a given registry. - * @param source A valid reference to a registry. + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. */ - as_view(registry_type &source) noexcept - : reg{source} {} + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } /** - * @brief Conversion function from a registry to a view. - * @tparam Get Type of storage used to construct the view. - * @tparam Exclude Types of storage used to filter the view. - * @return A newly created view. + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam It Iterator type (as required by the underlying storage type). + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range. + * @param last An iterator past the last element of the range. + * @param args Parameters to use to forward to the underlying storage. */ - template - operator basic_view() const { - return dispatch(Get{}, Exclude{}); - } - -private: - registry_type ® -}; + template + void insert(It first, It last, Args &&...args) { + underlying_type::insert(first, last, std::forward(args)...); -/** - * @brief Converts a registry to a group. - * @tparam Registry Basic registry type. - */ -template -class as_group { - template - auto dispatch(owned_t, get_t, exclude_t) const { - if constexpr(std::is_const_v) { - return reg.template group_if_exists(get_t{}, exclude_t{}); - } else { - return reg.template group...>(get_t...>{}, exclude_t...>{}); + if(auto ® = owner_or_assert(); !construction.empty()) { + for(; first != last; ++first) { + construction.publish(reg, *first); + } } } -public: - /*! @brief Type of registry to convert. */ - using registry_type = Registry; - /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; - - /** - * @brief Constructs a converter for a given registry. - * @param source A valid reference to a registry. - */ - as_group(registry_type &source) noexcept - : reg{source} {} - /** - * @brief Conversion function from a registry to a group. - * @tparam Owned Types of _owned_ by the group. - * @tparam Get Types of storage _observed_ by the group. - * @tparam Exclude Types of storage used to filter the group. - * @return A newly created group. + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. */ - template - operator basic_group() const { - return dispatch(Owned{}, Get{}, Exclude{}); + void bind(any value) noexcept final { + auto *reg = any_cast(&value); + owner = reg ? reg : owner; + underlying_type::bind(std::move(value)); } private: - registry_type ® + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; }; -/** - * @brief Helper to create a listener that directly invokes a member function. - * @tparam Member Member function to invoke on a component of the given type. - * @tparam Registry Basic registry type. - * @param reg A registry that contains the given entity and its components. - * @param entt Entity from which to get the component. - */ -template>> -void invoke(Registry ®, const typename Registry::entity_type entt) { - static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); - delegate func; - func.template connect(reg.template get>(entt)); - func(reg, entt); -} - -/** - * @brief Returns the entity associated with a given component. - * - * @warning - * Currently, this function only works correctly with the default pool as it - * makes assumptions about how the components are laid out. - * - * @tparam Registry Basic registry type. - * @tparam Component Type of component. - * @param reg A registry that contains the given entity and its components. - * @param instance A valid component instance. - * @return The entity associated with the given component. - */ -template -typename Registry::entity_type to_entity(const Registry ®, const Component &instance) { - const auto &storage = reg.template storage(); - const typename Registry::base_type &base = storage; - const auto *addr = std::addressof(instance); - - for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits::page_size) { - if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(component_traits::page_size)) { - return *(it + dist); - } - } - - return null; -} - } // namespace entt #endif @@ -22568,7 +24573,7 @@ struct basic_collector<> { * @return The updated collector. */ template - static constexpr auto group(exclude_t = {}) noexcept { + static constexpr auto group(exclude_t = exclude_t{}) noexcept { return basic_collector, type_list<>, type_list, AllOf...>>{}; } @@ -22603,7 +24608,7 @@ struct basic_collector, type_list, Rule * @return The updated collector. */ template - static constexpr auto group(exclude_t = {}) noexcept { + static constexpr auto group(exclude_t = exclude_t{}) noexcept { return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; } @@ -22624,7 +24629,7 @@ struct basic_collector, type_list, Rule * @return The updated collector. */ template - static constexpr auto where(exclude_t = {}) noexcept { + static constexpr auto where(exclude_t = exclude_t{}) noexcept { using extended_type = matcher, type_list, Rule...>; return basic_collector{}; } @@ -22671,8 +24676,7 @@ inline constexpr basic_collector<> collector{}; * * The entity currently pointed is destroyed. * * In all the other cases, modifying the pools of the given components in any - * way invalidates all the iterators and using them results in undefined - * behavior. + * way invalidates all the iterators. * * @warning * Lifetime of an observer doesn't necessarily have to overcome that of the @@ -22681,10 +24685,12 @@ inline constexpr basic_collector<> collector{}; * pointers. * * @tparam Registry Basic registry type. + * @tparam Mask Mask type. + * @tparam Allocator Type of allocator used to manage memory and elements. */ -template -class basic_observer: private basic_storage { - using base_type = basic_storage; +template +class basic_observer: private basic_storage { + using base_type = basic_storage; template struct matcher_handler; @@ -22718,10 +24724,10 @@ class basic_observer: private basic_storage().disconnect(obs), ...); - (reg.template on_construct().disconnect(obs), ...); - reg.template on_update().disconnect(obs); - reg.template on_destroy().disconnect(obs); + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); + reg.template on_update().disconnect(&obs); + reg.template on_destroy().disconnect(&obs); } }; @@ -22764,12 +24770,12 @@ class basic_observer: private basic_storage().disconnect(obs), ...); - (reg.template on_construct().disconnect(obs), ...); - (reg.template on_construct().disconnect(obs), ...); - (reg.template on_destroy().disconnect(obs), ...); - (reg.template on_destroy().disconnect(obs), ...); - (reg.template on_construct().disconnect(obs), ...); + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); } }; @@ -22792,15 +24798,26 @@ class basic_observer: private basic_storage - basic_observer(registry_type ®, basic_collector) - : basic_observer{} { + basic_observer(registry_type ®, basic_collector, const allocator_type &allocator = allocator_type{}) + : basic_observer{allocator} { connect(reg, std::index_sequence_for{}); } - /*! @brief Default destructor. */ - ~basic_observer() = default; - /** * @brief Default copy assignment operator, deleted on purpose. * @return This observer. @@ -22885,8 +24900,7 @@ class basic_observer: private basic_storage + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -23021,7 +25030,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -23030,14 +25039,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -23052,13 +25061,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -23100,8 +25109,8 @@ struct y_combinator { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -23157,6 +25166,8 @@ struct y_combinator { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -23416,8 +25427,8 @@ struct iterable_adaptor final { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -23473,6 +25484,8 @@ struct iterable_adaptor final { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -23789,7 +25802,7 @@ class adjacency_matrix { [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; - const auto to = vertex * vert + vert; + const auto to = from + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } @@ -23801,7 +25814,7 @@ class adjacency_matrix { [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; - const auto to = vert * (vert - 1u) + vertex; + const auto to = vert * vert + from; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } @@ -23925,8 +25938,133 @@ class adjacency_matrix { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -23982,127 +26120,7 @@ class adjacency_matrix { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif -#ifdef ENTT_NO_ETO -# define ENTT_ETO_TYPE(Type) void -#else -# define ENTT_ETO_TYPE(Type) Type -#endif - -#ifdef ENTT_STANDARD_CPP -# define ENTT_NONSTD false -#else -# define ENTT_NONSTD true -# if defined __clang__ || defined __GNUC__ -# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ -# define ENTT_PRETTY_FUNCTION_PREFIX '=' -# define ENTT_PRETTY_FUNCTION_SUFFIX ']' -# elif defined _MSC_VER -# define ENTT_PRETTY_FUNCTION __FUNCSIG__ -# define ENTT_PRETTY_FUNCTION_PREFIX '<' -# define ENTT_PRETTY_FUNCTION_SUFFIX '>' -# endif -#endif - -#if defined _MSC_VER -# pragma detect_mismatch("entt.version", ENTT_VERSION) -# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) -# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) -# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) -#endif - -#endif - -// #include "../core/compressed_pair.hpp" -#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP -#define ENTT_CORE_COMPRESSED_PAIR_HPP - -#include -#include -#include -#include -// #include "type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - -#include -#include -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - -// #include "version.h" -#ifndef ENTT_CONFIG_VERSION_H -#define ENTT_CONFIG_VERSION_H - -// #include "macro.h" -#ifndef ENTT_CONFIG_MACRO_H -#define ENTT_CONFIG_MACRO_H - -#define ENTT_STR(arg) #arg -#define ENTT_XSTR(arg) ENTT_STR(arg) - -#endif - - -#define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 - -#define ENTT_VERSION \ - ENTT_XSTR(ENTT_VERSION_MAJOR) \ - "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) - -#endif - - -#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_CONSTEXPR -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) -# define ENTT_THROW -# define ENTT_TRY if(true) -# define ENTT_CATCH if(false) -#endif - -#ifdef ENTT_USE_ATOMIC -# include -# define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else -# define ENTT_MAYBE_ATOMIC(Type) Type -#endif - -#ifndef ENTT_ID_TYPE -# include -# define ENTT_ID_TYPE std::uint32_t -#endif - -#ifndef ENTT_SPARSE_PAGE -# define ENTT_SPARSE_PAGE 4096 -#endif - -#ifndef ENTT_PACKED_PAGE -# define ENTT_PACKED_PAGE 1024 -#endif - -#ifdef ENTT_DISABLE_ASSERT -# undef ENTT_ASSERT -# define ENTT_ASSERT(condition, msg) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, msg) assert(condition) -#endif - -#ifdef ENTT_DISABLE_ASSERT -# undef ENTT_ASSERT_CONSTEXPR -# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) -#elif !defined ENTT_ASSERT_CONSTEXPR -# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) -#endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void @@ -24205,7 +26223,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -24447,7 +26464,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -24535,10 +26553,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -24547,6 +26575,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -24598,6 +26678,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -24718,7 +26881,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -24809,6 +26972,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -24905,6 +27072,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -25488,7 +27667,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -25509,7 +27688,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -25676,6 +27855,7 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #include #include +#include #include #include // #include "../config/config.h" @@ -25730,7 +27910,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -25972,7 +28151,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -26060,10 +28240,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -26072,6 +28262,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -26123,6 +28365,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -26243,7 +28568,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -26334,6 +28659,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -26430,6 +28759,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -26571,51 +28912,51 @@ class dense_map_iterator final { return {it->element.first, it->element.second}; } - template - friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -26673,13 +29014,13 @@ class dense_map_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -26709,14 +29050,14 @@ class dense_map { static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { - return fast_mod(sparse.second()(key), bucket_count()); + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template @@ -26907,7 +29248,6 @@ class dense_map { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -26928,11 +29268,6 @@ class dense_map { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -27281,7 +29616,7 @@ class dense_map { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); @@ -27606,51 +29941,51 @@ class dense_set_iterator final { return *operator->(); } - template - friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -27705,13 +30040,13 @@ class dense_set_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -27747,7 +30082,7 @@ class dense_set { template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { - return fast_mod(sparse.second()(value), bucket_count()); + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template @@ -27920,7 +30255,6 @@ class dense_set { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -27941,11 +30275,6 @@ class dense_set { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -28201,7 +30530,7 @@ class dense_set { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); @@ -28418,6 +30747,7 @@ class dense_set { #include #include +#include #include #include // #include "../config/config.h" @@ -28493,7 +30823,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -28735,7 +31064,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -28823,10 +31153,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -28835,6 +31175,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -28886,6 +31278,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -29006,7 +31481,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -29097,6 +31572,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -29193,6 +31672,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -29491,7 +31982,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -29524,7 +32015,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -29533,14 +32024,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -29555,13 +32046,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -29838,7 +32329,7 @@ class adjacency_matrix { [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; - const auto to = vertex * vert + vert; + const auto to = from + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } @@ -29850,7 +32341,7 @@ class adjacency_matrix { [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; - const auto to = vert * (vert - 1u) + vertex; + const auto to = vert * vert + from; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } @@ -29943,6 +32434,7 @@ class basic_flow { using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; void emplace(const id_type res, const bool is_rw) { ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); @@ -29954,6 +32446,76 @@ class basic_flow { deps[res].emplace_back(index.first(), is_rw); } + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + public: /*! @brief Allocator type. */ using allocator_type = Allocator; @@ -29961,6 +32523,8 @@ class basic_flow { using size_type = std::size_t; /*! @brief Iterable task list. */ using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; /*! @brief Default constructor. */ basic_flow() @@ -30035,9 +32599,10 @@ class basic_flow { /*! @brief Clears the flow builder. */ void clear() noexcept { - index.first() = sync_on = {}; + index.first() = {}; vertices.clear(); deps.clear(); + sync_on = {}; } /** @@ -30156,72 +32721,12 @@ class basic_flow { * @brief Generates a task graph for the current content. * @return The adjacency matrix of the task graph. */ - [[nodiscard]] adjacency_matrix graph() const { - const auto length = vertices.size(); - adjacency_matrix matrix{length}; + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; - // creates the adjacency matrix - for(const auto &elem: deps) { - const auto last = elem.second.cend(); - auto it = elem.second.cbegin(); - - while(it != last) { - if(it->second) { - // rw item - if(auto curr = it++; it != last) { - if(it->second) { - matrix.insert(curr->first, it->first); - } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { - for(; it != next; ++it) { - matrix.insert(curr->first, it->first); - matrix.insert(it->first, next->first); - } - } else { - for(; it != next; ++it) { - matrix.insert(curr->first, it->first); - } - } - } - } else { - // ro item (first iteration only) - if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { - for(; it != next; ++it) { - matrix.insert(it->first, next->first); - } - } else { - it = last; - } - } - } - } - - // computes the transitive closure - for(std::size_t vk{}; vk < length; ++vk) { - for(std::size_t vi{}; vi < length; ++vi) { - for(std::size_t vj{}; vj < length; ++vj) { - if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { - matrix.insert(vi, vj); - } - } - } - } - - // applies the transitive reduction - for(std::size_t vert{}; vert < length; ++vert) { - matrix.erase(vert, vert); - } - - for(std::size_t vj{}; vj < length; ++vj) { - for(std::size_t vi{}; vi < length; ++vi) { - if(matrix.contains(vi, vj)) { - for(std::size_t vk{}; vk < length; ++vk) { - if(matrix.contains(vj, vk)) { - matrix.erase(vi, vk); - } - } - } - } - } + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); return matrix; } @@ -30245,14 +32750,13 @@ class basic_flow { #include #include +#include // #include "../core/fwd.hpp" // #include "../core/type_traits.hpp" // #include "../signal/delegate.hpp" -// #include "component.hpp" - // #include "fwd.hpp" // #include "group.hpp" @@ -30277,7 +32781,7 @@ class as_view { /*! @brief Type of registry to convert. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; + using entity_type = typename registry_type::entity_type; /** * @brief Constructs a converter for a given registry. @@ -30320,7 +32824,7 @@ class as_group { /*! @brief Type of registry to convert. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; + using entity_type = typename registry_type::entity_type; /** * @brief Constructs a converter for a given registry. @@ -30375,19 +32879,133 @@ void invoke(Registry ®, const typename Registry::entity_type entt) { */ template typename Registry::entity_type to_entity(const Registry ®, const Component &instance) { - const auto &storage = reg.template storage(); - const typename Registry::base_type &base = storage; - const auto *addr = std::addressof(instance); - - for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits::page_size) { - if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(component_traits::page_size)) { - return *(it + dist); + if(const auto *storage = reg.template storage(); storage) { + constexpr auto page_size = std::remove_const_t>::traits_type::page_size; + const typename Registry::common_type &base = *storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) { + if(const auto dist = (addr - std::addressof(storage->get(*it))); dist >= 0 && dist < static_cast(page_size)) { + return *(it + dist); + } } } return null; } +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + } // namespace entt #endif @@ -30506,7 +33124,7 @@ class basic_organizer final { if constexpr(std::is_same_v) { return reg; } else if constexpr(internal::is_view_v) { - return as_view{reg}; + return static_cast(as_view{reg}); } else { return reg.ctx().template emplace>(); } @@ -30839,8 +33457,8 @@ class basic_organizer final { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -30896,6 +33514,8 @@ class basic_organizer final { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -30940,6 +33560,7 @@ class basic_organizer final { #include #include +#include #include #include // #include "../config/config.h" @@ -30961,8 +33582,8 @@ class basic_organizer final { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -31018,6 +33639,8 @@ class basic_organizer final { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -31119,7 +33742,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -31361,7 +33983,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -31449,10 +34072,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -31461,6 +34094,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -31512,6 +34197,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -31632,7 +34400,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -31723,6 +34491,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -31819,6 +34591,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -32402,7 +35186,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -32423,7 +35207,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -32590,6 +35374,7 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #include #include +#include #include #include // #include "../config/config.h" @@ -32644,7 +35429,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -32886,7 +35670,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -32974,10 +35759,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -32986,6 +35781,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -33037,6 +35884,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -33157,7 +36087,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -33248,6 +36178,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -33344,6 +36278,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -33485,51 +36431,51 @@ class dense_map_iterator final { return {it->element.first, it->element.second}; } - template - friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -33587,13 +36533,13 @@ class dense_map_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -33623,14 +36569,14 @@ class dense_map { static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { - return fast_mod(sparse.second()(key), bucket_count()); + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template @@ -33821,7 +36767,6 @@ class dense_map { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -33842,11 +36787,6 @@ class dense_map { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -34195,7 +37135,7 @@ class dense_map { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); @@ -34420,8 +37360,6 @@ struct uses_allocator, Allocator> // #include "../core/any.hpp" -// #include "../core/compressed_pair.hpp" - // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" @@ -34434,14 +37372,312 @@ struct uses_allocator, Allocator> // #include "../core/utility.hpp" -// #include "component.hpp" - // #include "entity.hpp" // #include "fwd.hpp" // #include "group.hpp" +// #include "mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../signal/sigh.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type The type of the underlying storage. + */ +template +class sigh_mixin final: public Type { + using underlying_type = Type; + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + basic_registry_type &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return *owner; + } + + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + for(auto pos = underlying_type::each().begin().base().index(); !(pos < 0); --pos) { + if constexpr(underlying_type::traits_type::in_place_delete) { + if(const auto entt = underlying_type::operator[](static_cast(pos)); entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, underlying_type::operator[](static_cast(pos))); + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = basic_registry_type; + + /*! @brief Default constructor. */ + sigh_mixin() + : sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh_mixin(sigh_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh_mixin(sigh_mixin &&other, const allocator_type &allocator) noexcept + : underlying_type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + sigh_mixin &operator=(sigh_mixin &&other) noexcept { + underlying_type::operator=(std::move(other)); + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(sigh_mixin &other) { + using std::swap; + underlying_type::swap(other); + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @return A return value as returned by the underlying storage. + */ + auto emplace() { + const auto entt = underlying_type::emplace(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam Args Types of arguments to forward to the underlying storage. + * @param hint A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A return value as returned by the underlying storage. + */ + template + decltype(auto) emplace(const entity_type hint, Args &&...args) { + if constexpr(std::is_same_v) { + const auto entt = underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return entt; + } else { + underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), hint); + return this->get(hint); + } + } + + /** + * @brief Patches the given instance for an entity. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam It Iterator type (as required by the underlying storage type). + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range. + * @param last An iterator past the last element of the range. + * @param args Parameters to use to forward to the underlying storage. + */ + template + void insert(It first, It last, Args &&...args) { + underlying_type::insert(first, last, std::forward(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(; first != last; ++first) { + construction.publish(reg, *first); + } + } + } + + /** + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) noexcept final { + auto *reg = any_cast(&value); + owner = reg ? reg : owner; + underlying_type::bind(std::move(value)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +} // namespace entt + +#endif + // #include "sparse_set.hpp" // #include "storage.hpp" @@ -34530,64 +37766,62 @@ class registry_storage_iterator final { return operator*(); } - template - friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; - template - friend constexpr bool operator==(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + template + friend constexpr bool operator==(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; - template - friend constexpr bool operator<(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + template + friend constexpr bool operator<(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return !(lhs < rhs); } +template class registry_context { - using key_type = id_type; - using mapped_type = basic_any<0u>; - using container_type = dense_map; + using alloc_traits = std::allocator_traits; + using allocator_type = typename alloc_traits::template rebind_alloc>>; public: - template - [[deprecated("Use ::emplace_as instead")]] Type &emplace_hint(const id_type id, Args &&...args) { - return emplace_as(id, std::forward(args)...); - } + explicit registry_context(const allocator_type &allocator) + : ctx{allocator} {} template Type &emplace_as(const id_type id, Args &&...args) { @@ -34615,16 +37849,6 @@ class registry_context { return it != ctx.end() && it->second.type() == type_id() ? (ctx.erase(it), true) : false; } - template - [[deprecated("Use ::get instead")]] [[nodiscard]] const Type &at(const id_type id = type_id().hash()) const { - return get(id); - } - - template - [[deprecated("Use ::get instead")]] [[nodiscard]] Type &at(const id_type id = type_id().hash()) { - return get(id); - } - template [[nodiscard]] const Type &get(const id_type id = type_id().hash()) const { return any_cast(ctx.at(id)); @@ -34654,7 +37878,7 @@ class registry_context { } private: - container_type ctx; + dense_map, identity, std::equal_to, allocator_type> ctx; }; } // namespace internal @@ -34666,138 +37890,93 @@ class registry_context { /** * @brief Fast and reliable entity-component system. - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Entity A valid entity type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_registry { - using alloc_traits = typename std::allocator_traits; - static_assert(std::is_same_v, "Invalid value type"); - using basic_common_type = basic_sparse_set; - using entity_traits = entt_traits; - - template - using storage_for_type = typename storage_for>>::type; - - template - struct group_handler; + using base_type = basic_sparse_set; - template - struct group_handler, get_t, Owned...> { - // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here - static_assert(!std::disjunction_v::in_place_delete>...>, "Groups do not support in-place delete"); - using value_type = std::conditional_t; - value_type current{}; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); - template - group_handler(Args &&...args) - : current{std::forward(args)...} {} + // std::shared_ptr because of its type erased allocator which is useful here + using pool_container_type = dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>>; + using group_container_type = dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>>; - template - void maybe_valid_if(basic_registry &owner, const Entity entt) { - [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); + template + [[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash::value()) { + if constexpr(std::is_same_v) { + return entities; + } else { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + auto &cpool = pools[id]; - const auto is_valid = ((std::is_same_v || std::get &>(cpools).contains(entt)) && ...) - && ((std::is_same_v || owner.assure().contains(entt)) && ...) - && ((std::is_same_v || !owner.assure().contains(entt)) && ...); + if(!cpool) { + using storage_type = storage_for_type; + using alloc_type = typename storage_type::allocator_type; - if constexpr(sizeof...(Owned) == 0) { - if(is_valid && !current.contains(entt)) { - current.emplace(entt); - } - } else { - if(is_valid && !(std::get<0>(cpools).index(entt) < current)) { - const auto pos = current++; - (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); + if constexpr(std::is_same_v && !std::is_constructible_v) { + // std::allocator has no cross constructors (waiting for C++20) + cpool = std::allocate_shared(get_allocator(), alloc_type{}); + } else { + cpool = std::allocate_shared(get_allocator(), get_allocator()); } - } - } - void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { - if constexpr(sizeof...(Owned) == 0) { - current.remove(entt); - } else { - if(const auto cpools = std::forward_as_tuple(owner.assure()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { - const auto pos = --current; - (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); - } + cpool->bind(forward_as_any(*this)); } - } - }; - - struct group_data { - std::size_t size; - std::shared_ptr group; - bool (*owned)(const id_type) noexcept; - bool (*get)(const id_type) noexcept; - bool (*exclude)(const id_type) noexcept; - }; - - template - [[nodiscard]] auto &assure(const id_type id = type_hash::value()) { - static_assert(std::is_same_v>, "Non-decayed types not allowed"); - auto &cpool = pools[id]; - if(!cpool) { - cpool = std::allocate_shared>>(get_allocator(), get_allocator()); - cpool->bind(forward_as_any(*this)); + ENTT_ASSERT(cpool->type() == type_id(), "Unexpected type"); + return static_cast &>(*cpool); } - - ENTT_ASSERT(cpool->type() == type_id(), "Unexpected type"); - return static_cast &>(*cpool); } template - [[nodiscard]] const auto &assure(const id_type id = type_hash::value()) const { - static_assert(std::is_same_v>, "Non-decayed types not allowed"); - - if(const auto it = pools.find(id); it != pools.cend()) { - ENTT_ASSERT(it->second->type() == type_id(), "Unexpected type"); - return static_cast &>(*it->second); - } - - static storage_for_type placeholder{}; - return placeholder; - } - - auto generate_identifier(const std::size_t pos) noexcept { - ENTT_ASSERT(pos < entity_traits::to_entity(null), "No entities available"); - return entity_traits::combine(static_cast(pos), {}); - } + [[nodiscard]] const auto *assure([[maybe_unused]] const id_type id = type_hash::value()) const { + if constexpr(std::is_same_v) { + return &entities; + } else { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); - auto recycle_identifier() noexcept { - ENTT_ASSERT(free_list != null, "No entities available"); - const auto curr = entity_traits::to_entity(free_list); - free_list = entity_traits::combine(entity_traits::to_integral(epool[curr]), tombstone); - return (epool[curr] = entity_traits::combine(curr, entity_traits::to_integral(epool[curr]))); - } + if(const auto it = pools.find(id); it != pools.cend()) { + ENTT_ASSERT(it->second->type() == type_id(), "Unexpected type"); + return static_cast *>(it->second.get()); + } - auto release_entity(const Entity entt, const typename entity_traits::version_type version) { - const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone)); - epool[entity_traits::to_entity(entt)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); - free_list = entity_traits::combine(entity_traits::to_integral(entt), tombstone); - return vers; + return static_cast *>(nullptr); + } } void rebind() { + entities.bind(forward_as_any(*this)); + for(auto &&curr: pools) { curr.second->bind(forward_as_any(*this)); } } public: + /*! @brief Entity traits. */ + using traits_type = typename base_type::traits_type; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = typename traits_type::value_type; /*! @brief Underlying version type. */ - using version_type = typename entity_traits::version_type; + using version_type = typename traits_type::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = basic_common_type; + using common_type = base_type; /*! @brief Context type. */ - using context = internal::registry_context; + using context = internal::registry_context; + + /** + * @copybrief storage_for + * @tparam Type Storage value type, eventually const. + */ + template + using storage_for_type = typename storage_for>>::type; /*! @brief Default constructor. */ basic_registry() @@ -34816,12 +37995,12 @@ class basic_registry { * @param allocator The allocator to use. */ basic_registry(const size_type count, const allocator_type &allocator = allocator_type{}) - : vars{}, - free_list{tombstone}, - epool{allocator}, + : vars{allocator}, pools{allocator}, - groups{allocator} { + groups{allocator}, + entities{allocator} { pools.reserve(count); + rebind(); } /** @@ -34830,10 +38009,9 @@ class basic_registry { */ basic_registry(basic_registry &&other) noexcept : vars{std::move(other.vars)}, - free_list{std::move(other.free_list)}, - epool{std::move(other.epool)}, pools{std::move(other.pools)}, - groups{std::move(other.groups)} { + groups{std::move(other.groups)}, + entities{std::move(other.entities)} { rebind(); } @@ -34844,10 +38022,9 @@ class basic_registry { */ basic_registry &operator=(basic_registry &&other) noexcept { vars = std::move(other.vars); - free_list = std::move(other.free_list); - epool = std::move(other.epool); pools = std::move(other.pools); groups = std::move(other.groups); + entities = std::move(other.entities); rebind(); @@ -34860,11 +38037,11 @@ class basic_registry { */ void swap(basic_registry &other) { using std::swap; + swap(vars, other.vars); - swap(free_list, other.free_list); - swap(epool, other.epool); swap(pools, other.pools); swap(groups, other.groups); + swap(entities, other.entities); rebind(); other.rebind(); @@ -34875,7 +38052,7 @@ class basic_registry { * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { - return epool.get_allocator(); + return pools.get_allocator(); } /** @@ -34900,8 +38077,8 @@ class basic_registry { * @param id Name used to map the storage within the registry. * @return A pointer to the storage if it exists, a null pointer otherwise. */ - [[nodiscard]] base_type *storage(const id_type id) { - return const_cast(std::as_const(*this).storage(id)); + [[nodiscard]] common_type *storage(const id_type id) { + return const_cast(std::as_const(*this).storage(id)); } /** @@ -34909,7 +38086,7 @@ class basic_registry { * @param id Name used to map the storage within the registry. * @return A pointer to the storage if it exists, a null pointer otherwise. */ - [[nodiscard]] const base_type *storage(const id_type id) const { + [[nodiscard]] const common_type *storage(const id_type id) const { const auto it = pools.find(id); return it == pools.cend() ? nullptr : it->second.get(); } @@ -34921,23 +38098,18 @@ class basic_registry { * @return The storage for the given component type. */ template - decltype(auto) storage(const id_type id = type_hash::value()) { + storage_for_type &storage(const id_type id = type_hash::value()) { return assure(id); } /** - * @brief Returns the storage for a given component type. - * - * @warning - * If a storage for the given component doesn't exist yet, a temporary - * placeholder is returned instead. - * + * @brief Returns the storage for a given component type, if any. * @tparam Type Type of component of which to return the storage. * @param id Optional name used to map the storage within the registry. * @return The storage for the given component type. */ template - decltype(auto) storage(const id_type id = type_hash::value()) const { + const storage_for_type *storage(const id_type id = type_hash::value()) const { return assure(id); } @@ -34945,30 +38117,24 @@ class basic_registry { * @brief Returns the number of entities created so far. * @return Number of entities created so far. */ - [[nodiscard]] size_type size() const noexcept { - return epool.size(); + [[deprecated("use .storage().size() instead")]] [[nodiscard]] size_type size() const noexcept { + return entities.size(); } /** * @brief Returns the number of entities still in use. * @return Number of entities still in use. */ - [[nodiscard]] size_type alive() const { - auto sz = epool.size(); - - for(auto curr = free_list; curr != null; --sz) { - curr = epool[entity_traits::to_entity(curr)]; - } - - return sz; + [[deprecated("use .storage().in_use() instead")]] [[nodiscard]] size_type alive() const { + return entities.in_use(); } /** * @brief Increases the capacity (number of entities) of the registry. * @param cap Desired capacity. */ - void reserve(const size_type cap) { - epool.reserve(cap); + [[deprecated("use .storage().reserve(cap) instead")]] void reserve(const size_type cap) { + entities.reserve(cap); } /** @@ -34976,15 +38142,15 @@ class basic_registry { * allocated space for. * @return Capacity of the registry. */ - [[nodiscard]] size_type capacity() const noexcept { - return epool.capacity(); + [[deprecated("use .storage().capacity() instead")]] [[nodiscard]] size_type capacity() const noexcept { + return entities.capacity(); } /** * @brief Checks whether the registry is empty (no entities still in use). * @return True if the registry is empty, false otherwise. */ - [[nodiscard]] bool empty() const { + [[deprecated("use .storage().in_use() instead")]] [[nodiscard]] bool empty() const { return !alive(); } @@ -35000,20 +38166,16 @@ class basic_registry { * * @return A pointer to the array of entities. */ - [[nodiscard]] const entity_type *data() const noexcept { - return epool.data(); + [[deprecated("use .storage().data() instead")]] [[nodiscard]] const entity_type *data() const noexcept { + return entities.data(); } /** - * @brief Returns the head of the list of released entities. - * - * This function is intended for use in conjunction with `assign`.
- * The returned entity has an invalid identifier in all cases. - * - * @return The head of the list of released entities. + * @brief Returns the number of released entities. + * @return The number of released entities. */ - [[nodiscard]] entity_type released() const noexcept { - return free_list; + [[deprecated("use .storage().size() and .storage().in_use() instead")]] [[nodiscard]] size_type released() const noexcept { + return (entities.size() - entities.in_use()); } /** @@ -35022,8 +38184,7 @@ class basic_registry { * @return True if the identifier is valid, false otherwise. */ [[nodiscard]] bool valid(const entity_type entt) const { - const auto pos = size_type(entity_traits::to_entity(entt)); - return (pos < epool.size() && epool[pos] == entt); + return entities.contains(entt); } /** @@ -35033,8 +38194,7 @@ class basic_registry { * version otherwise. */ [[nodiscard]] version_type current(const entity_type entt) const { - const auto pos = size_type(entity_traits::to_entity(entt)); - return entity_traits::to_version(pos < epool.size() ? epool[pos] : tombstone); + return entities.current(entt); } /** @@ -35042,7 +38202,7 @@ class basic_registry { * @return A valid identifier. */ [[nodiscard]] entity_type create() { - return (free_list == null) ? epool.emplace_back(generate_identifier(epool.size())) : recycle_identifier(); + return entities.emplace(); } /** @@ -35055,26 +38215,7 @@ class basic_registry { * @return A valid identifier. */ [[nodiscard]] entity_type create(const entity_type hint) { - const auto length = epool.size(); - - if(hint == null || hint == tombstone) { - return create(); - } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { - epool.resize(size_type(req) + 1u, null); - - for(auto pos = length; pos < req; ++pos) { - release_entity(generate_identifier(pos), {}); - } - - return (epool[req] = hint); - } else if(const auto curr = entity_traits::to_entity(epool[req]); req == curr) { - return create(); - } else { - auto *it = &free_list; - for(; entity_traits::to_entity(*it) != req; it = &epool[entity_traits::to_entity(*it)]) {} - *it = entity_traits::combine(curr, entity_traits::to_integral(*it)); - return (epool[req] = hint); - } + return entities.emplace(hint); } /** @@ -35088,16 +38229,7 @@ class basic_registry { */ template void create(It first, It last) { - for(; free_list != null && first != last; ++first) { - *first = recycle_identifier(); - } - - const auto length = epool.size(); - epool.resize(length + std::distance(first, last), null); - - for(auto pos = length; first != last; ++first, ++pos) { - *first = epool[pos] = generate_identifier(pos); - } + entities.insert(std::move(first), std::move(last)); } /** @@ -35115,13 +38247,13 @@ class basic_registry { * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. - * @param destroyed The head of the list of destroyed entities. + * @param destroyed The number of released entities. */ template - void assign(It first, It last, const entity_type destroyed) { - ENTT_ASSERT(!alive(), "Entities still alive"); - epool.assign(first, last); - free_list = destroyed; + [[deprecated("use .storage().push(first, last) and .storage().in_use(len) instead")]] void assign(It first, It last, const size_type destroyed) { + ENTT_ASSERT(!entities.in_use(), "Non-empty registry"); + entities.push(first, last); + entities.in_use(entities.size() - destroyed); } /** @@ -35129,14 +38261,13 @@ class basic_registry { * * The version is updated and the identifier can be recycled at any time. * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * * @param entt A valid identifier. * @return The version of the recycled entity. */ - version_type release(const entity_type entt) { - return release(entt, static_cast(entity_traits::to_version(entt) + 1u)); + [[deprecated("use .orphan(entt) and .storage().erase(entt) instead")]] version_type release(const entity_type entt) { + ENTT_ASSERT(orphan(entt), "Non-orphan entity"); + entities.erase(entt); + return entities.current(entt); } /** @@ -35145,49 +38276,47 @@ class basic_registry { * The suggested version or the valid version closest to the suggested one * is used instead of the implicitly generated version. * - * @sa release - * * @param entt A valid identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ - version_type release(const entity_type entt, const version_type version) { - ENTT_ASSERT(valid(entt), "Invalid identifier"); - ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return (curr.second->current(entt) == entity_traits::to_version(tombstone)); }), "Non-orphan entity"); - return release_entity(entt, version); + [[deprecated("use .orphan(entt), then .storage().erase(entt)/.bump(next) instead")]] version_type release(const entity_type entt, const version_type version) { + ENTT_ASSERT(orphan(entt), "Non-orphan entity"); + entities.erase(entt); + const auto elem = traits_type::construct(traits_type::to_entity(entt), version); + return entities.bump((elem == tombstone) ? traits_type::next(elem) : elem); } /** * @brief Releases all identifiers in a range. * - * @sa release - * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template - void release(It first, It last) { - for(; first != last; ++first) { - release(*first); - } + [[deprecated("use .orphan(entt) and .storage().erase(first, last) instead")]] void release(It first, It last) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return orphan(entt); }), "Non-orphan entity"); + entities.erase(std::move(first), std::move(last)); } /** * @brief Destroys an entity and releases its identifier. * - * @sa release - * * @warning * Adding or removing components to an entity that is being destroyed can - * result in undefined behavior. Attempting to use an invalid entity results - * in undefined behavior. + * result in undefined behavior. * * @param entt A valid identifier. * @return The version of the recycled entity. */ version_type destroy(const entity_type entt) { - return destroy(entt, static_cast(entity_traits::to_version(entt) + 1u)); + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->remove(entt); + } + + entities.erase(entt); + return entities.current(entt); } /** @@ -35203,11 +38332,9 @@ class basic_registry { * @return The version actually assigned to the entity. */ version_type destroy(const entity_type entt, const version_type version) { - for(size_type pos = pools.size(); pos; --pos) { - pools.begin()[pos - 1u].second->remove(entt); - } - - return release(entt, version); + destroy(entt); + const auto elem = traits_type::construct(traits_type::to_entity(entt), version); + return entities.bump((elem == tombstone) ? traits_type::next(elem) : elem); } /** @@ -35221,9 +38348,14 @@ class basic_registry { */ template void destroy(It first, It last) { - for(; first != last; ++first) { - destroy(*first); + const auto from = entities.each().cbegin().base(); + const auto to = from + entities.pack(first, last); + + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->remove(from, to); } + + entities.erase(from, to); } /** @@ -35259,7 +38391,7 @@ class basic_registry { */ template void insert(It first, It last, const Type &value = {}) { - assure().insert(first, last, value); + assure().insert(std::move(first), std::move(last), value); } /** @@ -35309,13 +38441,8 @@ class basic_registry { * void(Type &); * @endcode * - * @note - * Empty types aren't explicitly instantiated and therefore they are never - * returned. However, this function can be used to trigger an update signal - * for them. - * * @warning - * Attempting to to patch a component of an entity that doesn't own it + * Attempting to patch a component of an entity that doesn't own it * results in undefined behavior. * * @tparam Type Type of component to patch. @@ -35351,7 +38478,6 @@ class basic_registry { /** * @brief Removes the given components from an entity. - * * @tparam Type Type of component to remove. * @tparam Other Other types of components to remove. * @param entt A valid identifier. @@ -35376,17 +38502,27 @@ class basic_registry { */ template size_type remove(It first, It last) { - if constexpr(sizeof...(Other) == 0u) { - return assure().remove(std::move(first), std::move(last)); - } else { - size_type count{}; + size_type count{}; + if constexpr(std::is_same_v) { + common_type *cpools[sizeof...(Other) + 1u]{&assure(), &assure()...}; + + for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) { + if constexpr(sizeof...(Other) != 0u) { + if(cpools[pos]->data() == first.data()) { + std::swap(cpools[pos], cpools[sizeof...(Other)]); + } + } + + count += cpools[pos]->remove(first, last); + } + } else { for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); } - - return count; } + + return count; } /** @@ -35418,8 +38554,18 @@ class basic_registry { */ template void erase(It first, It last) { - if constexpr(sizeof...(Other) == 0u) { - assure().erase(std::move(first), std::move(last)); + if constexpr(std::is_same_v) { + common_type *cpools[sizeof...(Other) + 1u]{&assure(), &assure()...}; + + for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) { + if constexpr(sizeof...(Other) != 0u) { + if(cpools[pos]->data() == first.data()) { + std::swap(cpools[pos], cpools[sizeof...(Other)]); + } + } + + cpools[pos]->erase(first, last); + } } else { for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); @@ -35427,6 +38573,30 @@ class basic_registry { } } + /** + * @brief Erases components satisfying specific criteria from an entity. + * + * The function type is equivalent to: + * + * @code{.cpp} + * void(const id_type, typename basic_registry::base_type &); + * @endcode + * + * Only storage where the entity exists are passed to the function. + * + * @tparam Func Type of the function object to invoke. + * @param entt A valid identifier. + * @param func A valid function object. + */ + template + void erase_if(const entity_type entt, Func func) { + for(auto [id, cpool]: storage()) { + if(cpool.contains(entt) && func(id, std::as_const(cpool))) { + cpool.erase(entt); + } + } + } + /** * @brief Removes all tombstones from a registry or only the pools for the * given components. @@ -35434,7 +38604,7 @@ class basic_registry { */ template void compact() { - if constexpr(sizeof...(Type) == 0) { + if constexpr(sizeof...(Type) == 0u) { for(auto &&curr: pools) { curr.second->compact(); } @@ -35451,7 +38621,12 @@ class basic_registry { */ template [[nodiscard]] bool all_of(const entity_type entt) const { - return (assure>().contains(entt) && ...); + if constexpr(sizeof...(Type) == 1u) { + auto *cpool = assure...>(); + return cpool && cpool->contains(entt); + } else { + return (all_of(entt) && ...); + } } /** @@ -35463,7 +38638,7 @@ class basic_registry { */ template [[nodiscard]] bool any_of(const entity_type entt) const { - return (assure>().contains(entt) || ...); + return (all_of(entt) || ...); } /** @@ -35480,7 +38655,7 @@ class basic_registry { template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { if constexpr(sizeof...(Type) == 1u) { - return (assure>().get(entt), ...); + return (assure>()->get(entt), ...); } else { return std::forward_as_tuple(get(entt)...); } @@ -35490,7 +38665,7 @@ class basic_registry { template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) { if constexpr(sizeof...(Type) == 1u) { - return (const_cast(std::as_const(*this).template get(entt)), ...); + return (static_cast &>(assure>()).get(entt), ...); } else { return std::forward_as_tuple(get(entt)...); } @@ -35532,9 +38707,9 @@ class basic_registry { */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const { - if constexpr(sizeof...(Type) == 1) { - const auto &cpool = assure...>(); - return cpool.contains(entt) ? std::addressof(cpool.get(entt)) : nullptr; + if constexpr(sizeof...(Type) == 1u) { + const auto *cpool = assure...>(); + return (cpool && cpool->contains(entt)) ? std::addressof(cpool->get(entt)) : nullptr; } else { return std::make_tuple(try_get(entt)...); } @@ -35543,8 +38718,9 @@ class basic_registry { /*! @copydoc try_get */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) { - if constexpr(sizeof...(Type) == 1) { - return (const_cast(std::as_const(*this).template try_get(entt)), ...); + if constexpr(sizeof...(Type) == 1u) { + auto &cpool = assure...>(); + return (static_cast(cpool.contains(entt) ? std::addressof(cpool.get(entt)) : nullptr), ...); } else { return std::make_tuple(try_get(entt)...); } @@ -35556,12 +38732,13 @@ class basic_registry { */ template void clear() { - if constexpr(sizeof...(Type) == 0) { - for(auto &&curr: pools) { - curr.second->clear(); + if constexpr(sizeof...(Type) == 0u) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->clear(); } - each([this](const auto entity) { this->release(entity); }); + const auto iterable = entities.each(); + entities.erase(iterable.begin().base(), iterable.end().base()); } else { (assure().clear(), ...); } @@ -35582,17 +38759,9 @@ class basic_registry { * @param func A valid function object. */ template - void each(Func func) const { - if(free_list == null) { - for(auto pos = epool.size(); pos; --pos) { - func(epool[pos - 1]); - } - } else { - for(auto pos = epool.size(); pos; --pos) { - if(const auto entity = epool[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { - func(entity); - } - } + [[deprecated("use .storage().each() instead")]] void each(Func func) const { + for(auto [entt]: entities.each()) { + func(entt); } } @@ -35621,11 +38790,12 @@ class basic_registry { * @sa sink * * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. * @return A temporary sink object. */ template - [[nodiscard]] auto on_construct() { - return assure().on_construct(); + [[nodiscard]] auto on_construct(const id_type id = type_hash::value()) { + return assure(id).on_construct(); } /** @@ -35644,11 +38814,12 @@ class basic_registry { * @sa sink * * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. * @return A temporary sink object. */ template - [[nodiscard]] auto on_update() { - return assure().on_update(); + [[nodiscard]] auto on_update(const id_type id = type_hash::value()) { + return assure(id).on_update(); } /** @@ -35667,21 +38838,16 @@ class basic_registry { * @sa sink * * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. * @return A temporary sink object. */ template - [[nodiscard]] auto on_destroy() { - return assure().on_destroy(); + [[nodiscard]] auto on_destroy(const id_type id = type_hash::value()) { + return assure(id).on_destroy(); } /** * @brief Returns a view for the given components. - * - * Views are created on the fly and share with the registry its internal - * data structures. Feel free to discard them after the use.
- * Creating and destroying a view is an incredibly cheap operation. As a - * rule of thumb, storing a view should never be an option. - * * @tparam Type Type of component used to construct the view. * @tparam Other Other types of components used to construct the view. * @tparam Exclude Types of components used to filter the view. @@ -35689,172 +38855,79 @@ class basic_registry { */ template [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> - view(exclude_t = {}) const { - return {assure>(), assure>()..., assure>()...}; + view(exclude_t = exclude_t{}) const { + const auto cpools = std::make_tuple(assure>(), assure>()..., assure>()...); + basic_view, storage_for_type...>, exclude_t...>> elem{}; + std::apply([&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }, cpools); + return elem; } /*! @copydoc view */ template [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> - view(exclude_t = {}) { + view(exclude_t = exclude_t{}) { return {assure>(), assure>()..., assure>()...}; } /** * @brief Returns a group for the given components. - * - * Groups are created on the fly and share with the registry its internal - * data structures. Feel free to discard them after the use.
- * Creating and destroying a group is an incredibly cheap operation. As a - * rule of thumb, storing a group should never be an option. - * - * Groups support exclusion lists and can own types of components. The more - * types are owned by a group, the faster it is to iterate entities and - * components.
- * However, groups also affect some features of the registry such as the - * creation and destruction of components. - * - * @note - * Pools of components that are owned by a group cannot be sorted anymore. - * The group takes the ownership of the pools and arrange components so as - * to iterate them as fast as possible. - * - * @tparam Owned Type of storage _owned_ by the group. - * @tparam Get Type of storage _observed_ by the group. - * @tparam Exclude Type of storage used to filter the group. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group, if any. + * @tparam Exclude Types of storage used to filter the group, if any. * @return A newly created group. */ template - [[nodiscard]] basic_group...>, get_t...>, exclude_t...>> - group(get_t = {}, exclude_t = {}) { - static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported"); - static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed"); - - using handler_type = group_handler...>, get_t...>, std::remove_const_t...>; - - const auto cpools = std::forward_as_tuple(assure>()..., assure>()...); - constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); - handler_type *handler = nullptr; - - auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { - return gdata.size == size - && (gdata.owned(type_hash>::value()) && ...) - && (gdata.get(type_hash>::value()) && ...) - && (gdata.exclude(type_hash>::value()) && ...); - }); + basic_group...>, get_t...>, exclude_t...>> + group(get_t = get_t{}, exclude_t = exclude_t{}) { + using handler_type = typename basic_group...>, get_t...>, exclude_t...>>::handler; - if(it != groups.cend()) { - handler = static_cast(it->group.get()); - } else { - group_data candidate = { - size, - std::apply([this](auto &&...args) { return std::allocate_shared(get_allocator(), std::forward(args)...); }, entt::uses_allocator_construction_args(get_allocator())), - []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash>::value()) || ...); }, - []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash>::value()) || ...); }, - []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash>::value()) || ...); }, - }; - - handler = static_cast(candidate.group.get()); - - const void *maybe_valid_if = nullptr; - const void *discard_if = nullptr; - - if constexpr(sizeof...(Owned) == 0) { - groups.push_back(std::move(candidate)); - } else { - [[maybe_unused]] auto has_conflict = [size](const auto &gdata) { - const auto overlapping = (0u + ... + gdata.owned(type_hash>::value())); - const auto sz = overlapping + (0u + ... + gdata.get(type_hash>::value())) + (0u + ... + gdata.exclude(type_hash>::value())); - return !overlapping || ((sz == size) || (sz == gdata.size)); - }; - - ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups"); - - const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { - return !(0u + ... + gdata.owned(type_hash>::value())) || (size > gdata.size); - }); - - const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) { - return (0u + ... + gdata.owned(type_hash>::value())); - }); - - maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); - discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); - groups.insert(next, std::move(candidate)); - } - - (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); - (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); - (on_destroy>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); + if(auto it = groups.find(type_hash::value()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; + } - (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); - (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); - (on_construct>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + std::shared_ptr handler{}; - if constexpr(sizeof...(Owned) == 0) { - for(const auto entity: view(exclude)) { - handler->current.emplace(entity); - } - } else { - // we cannot iterate backwards because we want to leave behind valid entities in case of owned types - for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) { - handler->template maybe_valid_if...>>>(*this, *first); - } - } + if constexpr(sizeof...(Owned) == 0u) { + handler = std::allocate_shared(get_allocator(), get_allocator(), assure>()..., assure>()...); + } else { + handler = std::allocate_shared(get_allocator(), assure>()..., assure>()..., assure>()...); + [[maybe_unused]] const id_type elem[]{type_hash>::value()..., type_hash>::value()..., type_hash>::value()...}; + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&elem](const auto &data) { return data.second->owned(elem, sizeof...(Owned)) == 0u; }), "Conflicting groups"); } - return {handler->current, std::get> &>(cpools)..., std::get> &>(cpools)...}; + groups.emplace(type_hash::value(), handler); + return {*handler}; } /*! @copydoc group */ template - [[nodiscard]] basic_group...>, get_t...>, exclude_t...>> - group_if_exists(get_t = {}, exclude_t = {}) const { - auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) { - return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude)) - && (gdata.owned(type_hash>::value()) && ...) - && (gdata.get(type_hash>::value()) && ...) - && (gdata.exclude(type_hash>::value()) && ...); - }); + basic_group...>, get_t...>, exclude_t...>> + group_if_exists(get_t = get_t{}, exclude_t = exclude_t{}) const { + using handler_type = typename basic_group...>, get_t...>, exclude_t...>>::handler; - if(it == groups.cend()) { - return {}; - } else { - using handler_type = group_handler...>, get_t...>, std::remove_const_t...>; - return {static_cast(it->group.get())->current, assure>()..., assure>()...}; + if(auto it = groups.find(type_hash::value()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; } + + return {}; } /** * @brief Checks whether the given components belong to any group. - * @tparam Component Types of components in which one is interested. + * @tparam Type Type of component in which one is interested. + * @tparam Other Other types of components in which one is interested. * @return True if the pools of the given components are _free_, false * otherwise. */ - template + template [[nodiscard]] bool owned() const { - return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash>::value()) || ...); }); - } - - /** - * @brief Checks whether a group can be sorted. - * @tparam Owned Type of storage _owned_ by the group. - * @tparam Get Type of storage _observed_ by the group. - * @tparam Exclude Type of storage used to filter the group. - * @return True if the group can be sorted, false otherwise. - */ - template - [[nodiscard]] bool sortable(const basic_group, get_t, exclude_t> &) noexcept { - constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); - auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash::value())) && (size < gdata.size); }; - return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend(); + const id_type elem[]{type_hash>::value(), type_hash>::value()...}; + return std::any_of(groups.cbegin(), groups.cend(), [&elem](auto &&data) { return data.second->owned(elem, 1u + sizeof...(Other)); }); } /** * @brief Sorts the elements of a given component. * - * The order remains valid until a component of the given type is assigned - * to or removed from an entity.
* The comparison function object returns `true` if the first element is * _less_ than the second one, `false` otherwise. Its signature is also * equivalent to one of the following: @@ -35901,15 +38974,9 @@ class basic_registry { /** * @brief Sorts two pools of components in the same way. * - * Being `To` and `From` the two sets, after invoking this function an - * iterator for `To` returns elements according to the following rules: - * - * * All entities in `To` that are also in `From` are returned first - * according to the order they have in `From`. - * * All entities in `To` that are not in `From` are returned in no - * particular order after all the other entities. - * - * Any subsequent change to `From` won't affect the order in `To`. + * Entities and components in `To` which are part of both storage are sorted + * internally with the order they have in `From`. The others follow in no + * particular order. * * @warning * Pools of components owned by a group cannot be sorted. @@ -35920,7 +38987,7 @@ class basic_registry { template void sort() { ENTT_ASSERT(!owned(), "Cannot sort owned storage"); - assure().respect(assure()); + assure().sort_as(assure()); } /** @@ -35938,11 +39005,9 @@ class basic_registry { private: context vars; - entity_type free_list; - std::vector epool; - // std::shared_ptr because of its type erased allocator which is useful here - dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>> pools; - std::vector> groups; + pool_container_type pools; + group_container_type groups; + storage_for_type entities; }; } // namespace entt @@ -36058,38 +39123,22 @@ class runtime_view_iterator final { /** * @brief Generic runtime view. * - * Runtime views iterate over those entities that have at least all the given - * components in their bags. During initialization, a runtime view looks at the - * number of entities available for each component and picks up a reference to - * the smallest set of candidate entities in order to get a performance boost - * when iterate.
- * Order of elements during iterations are highly dependent on the order of the - * underlying data structures. See sparse_set and its specializations for more - * details. + * Runtime views iterate over those entities that are at least in the given + * storage. During initialization, a runtime view looks at the number of + * entities available for each component and uses the smallest set in order to + * get a performance boost when iterating. * * @b Important * * Iterators aren't invalidated if: * - * * New instances of the given components are created and assigned to entities. - * * The entity currently pointed is modified (as an example, if one of the - * given components is removed from the entity to which the iterator points). + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). * * The entity currently pointed is destroyed. * - * In all the other cases, modifying the pools of the given components in any - * way invalidates all the iterators and using them results in undefined - * behavior. - * - * @note - * Views share references to the underlying data structures of the registry that - * generated them. Therefore any change to the entities and to the components - * made by means of the registry are immediately reflected by the views, unless - * a pool was missing when the view was built (in this case, the view won't - * have a valid reference and won't be updated accordingly). - * - * @warning - * Lifetime of a view must not overcome that of the registry that generated it. - * In any other case, attempting to use a view results in undefined behavior. + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. * * @tparam Type Common base type. * @tparam Allocator Type of allocator used to manage memory and elements. @@ -36108,9 +39157,9 @@ class basic_runtime_view { /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = Type; + using common_type = Type; /*! @brief Bidirectional iterator type. */ - using iterator = internal::runtime_view_iterator; + using iterator = internal::runtime_view_iterator; /*! @brief Default constructor to use to create empty, invalid views. */ basic_runtime_view() noexcept @@ -36189,7 +39238,7 @@ class basic_runtime_view { * @param base An opaque reference to a storage object. * @return This runtime view. */ - basic_runtime_view &iterate(base_type &base) { + basic_runtime_view &iterate(common_type &base) { if(pools.empty() || !(base.size() < pools[0u]->size())) { pools.push_back(&base); } else { @@ -36204,7 +39253,7 @@ class basic_runtime_view { * @param base An opaque reference to a storage object. * @return This runtime view. */ - basic_runtime_view &exclude(base_type &base) { + basic_runtime_view &exclude(common_type &base) { filter.push_back(&base); return *this; } @@ -36221,9 +39270,7 @@ class basic_runtime_view { * @brief Returns an iterator to the first entity that has the given * components. * - * The returned iterator points to the first entity that has the given - * components. If the view is empty, the returned iterator will be equal to - * `end()`. + * If the view is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity that has the given components. */ @@ -36234,11 +39281,6 @@ class basic_runtime_view { /** * @brief Returns an iterator that is past the last entity that has the * given components. - * - * The returned iterator points to the entity following the last entity that - * has the given components. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the entity following the last entity that has the * given components. */ @@ -36261,8 +39303,7 @@ class basic_runtime_view { * @brief Iterates entities and applies the given function object to them. * * The function object is invoked for each entity. It is provided only with - * the entity itself. To get the components, users can use the registry with - * which the view was built.
+ * the entity itself.
* The signature of the function should be equivalent to the following: * * @code{.cpp} @@ -36292,7 +39333,6 @@ class basic_runtime_view { #ifndef ENTT_ENTITY_SNAPSHOT_HPP #define ENTT_ENTITY_SNAPSHOT_HPP -#include #include #include #include @@ -36305,8 +39345,6 @@ class basic_runtime_view { // #include "../core/type_traits.hpp" -// #include "component.hpp" - // #include "entity.hpp" // #include "fwd.hpp" @@ -36316,6 +39354,31 @@ class basic_runtime_view { namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +void orphans(Registry ®istry) { + auto view = registry.template view(); + + for(auto entt: view) { + if(registry.orphan(entt)) { + view.storage()->erase(entt); + } + } +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + /** * @brief Utility class to create snapshots from a registry. * @@ -36328,34 +39391,8 @@ namespace entt { */ template class basic_snapshot { - using entity_traits = entt_traits; - - template - void get(Archive &archive, std::size_t sz, It first, It last) const { - const auto view = reg->template view(); - archive(typename entity_traits::entity_type(sz)); - - while(first != last) { - const auto entt = *(first++); - - if(reg->template all_of(entt)) { - std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt))); - } - } - } - - template - void component(Archive &archive, It first, It last, std::index_sequence) const { - std::array size{}; - auto begin = first; - - while(begin != last) { - const auto entt = *(begin++); - ((reg->template all_of(entt) ? ++size[Index] : 0u), ...); - } - - (get(archive, size[Index], first, last), ...); - } + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; public: /*! Basic registry type. */ @@ -36377,58 +39414,96 @@ class basic_snapshot { basic_snapshot &operator=(basic_snapshot &&) noexcept = default; /** - * @brief Puts aside all the entities from the underlying registry. - * - * Entities are serialized along with their versions. Destroyed entities are - * taken in consideration as well by this function. - * + * @brief Serializes all elements of a type with associated identifiers. + * @tparam Type Type of elements to serialize. * @tparam Archive Type of output archive. * @param archive A valid reference to an output archive. + * @param id Optional name used to map the storage within the registry. * @return An object of this type to continue creating the snapshot. */ - template - const basic_snapshot &entities(Archive &archive) const { - const auto sz = reg->size(); + template + const basic_snapshot &get(Archive &archive, const id_type id = type_hash::value()) const { + if(const auto *storage = reg->template storage(id); storage) { + archive(static_cast(storage->size())); - archive(typename entity_traits::entity_type(sz + 1u)); - archive(reg->released()); + if constexpr(std::is_same_v) { + archive(static_cast(storage->in_use())); - for(auto first = reg->data(), last = first + sz; first != last; ++first) { - archive(*first); + for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) { + archive(*first); + } + } else { + for(auto elem: storage->reach()) { + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, elem); + } + } + } else { + archive(typename traits_type::entity_type{}); } return *this; } /** - * @brief Puts aside the given components. - * - * Each instance is serialized together with the entity to which it belongs. - * Entities are serialized along with their versions. - * - * @tparam Component Types of components to serialize. + * @brief Serializes all elements of a type with associated identifiers for + * the entities in a range. + * @tparam Type Type of elements to serialize. * @tparam Archive Type of output archive. + * @tparam It Type of input iterator. * @param archive A valid reference to an output archive. + * @param first An iterator to the first element of the range to serialize. + * @param last An iterator past the last element of the range to serialize. + * @param id Optional name used to map the storage within the registry. * @return An object of this type to continue creating the snapshot. */ - template - const basic_snapshot &component(Archive &archive) const { - if constexpr(sizeof...(Component) == 1u) { - const auto view = reg->template view(); - (component(archive, view.rbegin(), view.rend()), ...); - return *this; + template + const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash::value()) const { + static_assert(!std::is_same_v, "Entity types not supported"); + + if(const auto *storage = reg->template storage(id); storage && !storage->empty()) { + archive(static_cast(std::distance(first, last))); + + for(; first != last; ++first) { + if(const auto entt = *first; storage->contains(entt)) { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, storage->get_as_tuple(entt)); + } else { + archive(static_cast(null)); + } + } } else { - (component(archive), ...); - return *this; + archive(typename traits_type::entity_type{}); } + + return *this; } /** - * @brief Puts aside the given components for the entities in a range. - * - * Each instance is serialized together with the entity to which it belongs. - * Entities are serialized along with their versions. - * + * @brief Serializes all identifiers, including those to be recycled. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + [[deprecated("use .get(archive) instead")]] const basic_snapshot &entities(Archive &archive) const { + return get(archive); + } + + /** + * @brief Serializes all elements of a type with associated identifiers. + * @tparam Component Types of components to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @return An object of this type to continue creating the snapshot. + */ + template + [[deprecated("use .get(archive) instead")]] const basic_snapshot &component(Archive &archive) const { + return (get(archive), ...); + } + + /** + * @brief Serializes all elements of a type with associated identifiers for + * the entities in a range. * @tparam Component Types of components to serialize. * @tparam Archive Type of output archive. * @tparam It Type of input iterator. @@ -36438,9 +39513,8 @@ class basic_snapshot { * @return An object of this type to continue creating the snapshot. */ template - const basic_snapshot &component(Archive &archive, It first, It last) const { - component(archive, first, last, std::index_sequence_for{}); - return *this; + [[deprecated("use .get(archive, first, last) instead")]] const basic_snapshot &component(Archive &archive, It first, It last) const { + return (get(archive, first, last), ...); } private: @@ -36459,33 +39533,8 @@ class basic_snapshot { */ template class basic_snapshot_loader { - using entity_traits = entt_traits; - - template - void assign(Archive &archive) const { - typename entity_traits::entity_type length{}; - entity_type entt; - - archive(length); - - if constexpr(ignore_as_empty_v) { - while(length--) { - archive(entt); - const auto entity = reg->valid(entt) ? entt : reg->create(entt); - ENTT_ASSERT(entity == entt, "Entity not available for use"); - reg->template emplace(entt); - } - } else { - Component instance; - - while(length--) { - archive(entt, instance); - const auto entity = reg->valid(entt) ? entt : reg->create(entt); - ENTT_ASSERT(entity == entt, "Entity not available for use"); - reg->template emplace(entt, std::move(instance)); - } - } - } + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; public: /*! Basic registry type. */ @@ -36510,48 +39559,80 @@ class basic_snapshot_loader { basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; /** - * @brief Restores entities that were in use during serialization. - * - * This function restores the entities that were in use during serialization - * and gives them the versions they originally had. - * + * @brief Restores all elements of a type with associated identifiers. + * @tparam Type Type of elements to restore. * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. * @return A valid loader to continue restoring data. */ - template - const basic_snapshot_loader &entities(Archive &archive) const { - typename entity_traits::entity_type length{}; + template + basic_snapshot_loader &get([[maybe_unused]] Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; archive(length); - std::vector all(length); - for(std::size_t pos{}; pos < length; ++pos) { - archive(all[pos]); - } + if constexpr(std::is_same_v) { + typename traits_type::entity_type in_use{}; + + storage.reserve(length); + archive(in_use); - reg->assign(++all.cbegin(), all.cend(), all[0u]); + for(entity_type entity = null; length; --length) { + archive(entity); + storage.emplace(entity); + } + + storage.in_use(in_use); + } else { + auto &other = reg->template storage(); + entity_type entt{null}; + + while(length--) { + if(archive(entt); entt != null) { + const auto entity = other.contains(entt) ? entt : other.emplace(entt); + ENTT_ASSERT(entity == entt, "Entity not available for use"); + + if constexpr(Registry::template storage_for_type::traits_type::page_size == 0u) { + storage.emplace(entity); + } else { + Type elem{}; + archive(elem); + storage.emplace(entity, std::move(elem)); + } + } + } + } return *this; } /** - * @brief Restores components and assigns them to the right entities. + * @brief Restores all identifiers, including those to be recycled. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A valid loader to continue restoring data. + */ + template + [[deprecated("use .get(archive) instead")]] basic_snapshot_loader &entities(Archive &archive) { + return get(archive); + } + + /** + * @brief Restores all elements of a type with associated identifiers. * * The template parameter list must be exactly the same used during - * serialization. In the event that the entity to which the component is - * assigned doesn't exist yet, the loader will take care to create it with - * the version it originally had. + * serialization. * - * @tparam Component Types of components to restore. + * @tparam Component Type of component to restore. * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. * @return A valid loader to continue restoring data. */ template - const basic_snapshot_loader &component(Archive &archive) const { - (assign(archive), ...); - return *this; + [[deprecated("use .get(archive) instead")]] basic_snapshot_loader &component(Archive &archive) { + return (get(archive), ...); } /** @@ -36560,17 +39641,12 @@ class basic_snapshot_loader { * In case all the entities were serialized but only part of the components * was saved, it could happen that some of the entities have no components * once restored.
- * This functions helps to identify and destroy those entities. + * This function helps to identify and destroy those entities. * * @return A valid loader to continue restoring data. */ - const basic_snapshot_loader &orphans() const { - reg->each([this](const auto entt) { - if(reg->orphan(entt)) { - reg->release(entt); - } - }); - + basic_snapshot_loader &orphans() { + internal::orphans(*reg); return *this; } @@ -36588,7 +39664,7 @@ class basic_snapshot_loader { * Identifiers that entities originally had are not transferred to the target. * Instead, the loader maps remote identifiers to local ones while restoring a * snapshot.
- * An example of use is the implementation of a client-server applications with + * An example of use is the implementation of a client-server application with * the requirement of transferring somehow parts of the representation side to * side. * @@ -36596,29 +39672,16 @@ class basic_snapshot_loader { */ template class basic_continuous_loader { - using entity_traits = entt_traits; - - void destroy(typename Registry::entity_type entt) { - if(const auto it = remloc.find(entt); it == remloc.cend()) { - const auto local = reg->create(); - remloc.emplace(entt, std::make_pair(local, true)); - reg->destroy(local); - } - } + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; void restore(typename Registry::entity_type entt) { - const auto it = remloc.find(entt); - - if(it == remloc.cend()) { - const auto local = reg->create(); - remloc.emplace(entt, std::make_pair(local, true)); - } else { - if(!reg->valid(remloc[entt].first)) { - remloc[entt].first = reg->create(); + if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) { + if(!reg->valid(remloc[entity].second)) { + remloc[entity].second = reg->create(); } - - // set the dirty flag - remloc[entt].second = true; + } else { + remloc.insert_or_assign(entity, std::make_pair(entt, reg->create())); } } @@ -36667,42 +39730,6 @@ class basic_continuous_loader { } } - template - void remove_if_exists() { - for(auto &&ref: remloc) { - const auto local = ref.second.first; - - if(reg->valid(local)) { - reg->template remove(local); - } - } - } - - template - void assign(Archive &archive, [[maybe_unused]] Member Other::*...member) { - typename entity_traits::entity_type length{}; - entity_type entt; - - archive(length); - - if constexpr(ignore_as_empty_v) { - while(length--) { - archive(entt); - restore(entt); - reg->template emplace_or_replace(map(entt)); - } - } else { - Component instance; - - while(length--) { - archive(entt, instance); - (update(instance, member), ...); - restore(entt); - reg->template emplace_or_replace(map(entt), std::move(instance)); - } - } - } - public: /*! Basic registry type. */ using registry_type = Registry; @@ -36714,7 +39741,8 @@ class basic_continuous_loader { * @param source A valid reference to a registry. */ basic_continuous_loader(registry_type &source) noexcept - : reg{&source} {} + : remloc{source.get_allocator()}, + reg{&source} {} /*! @brief Default move constructor. */ basic_continuous_loader(basic_continuous_loader &&) = default; @@ -36723,31 +39751,66 @@ class basic_continuous_loader { basic_continuous_loader &operator=(basic_continuous_loader &&) = default; /** - * @brief Restores entities that were in use during serialization. + * @brief Restores all elements of a type with associated identifiers. * - * This function restores the entities that were in use during serialization - * and creates local counterparts for them if required. + * It creates local counterparts for remote elements as needed.
+ * Members are either data members of type entity_type or containers of + * entities. In both cases, a loader visits them and replaces entities with + * their local counterpart. * + * @tparam Type Type of elements to restore. * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. - * @return A non-const reference to this loader. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. */ - template - basic_continuous_loader &entities(Archive &archive) { - typename entity_traits::entity_type length{}; - entity_type entt{}; + template + basic_continuous_loader &get([[maybe_unused]] Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; + entity_type entt{null}; archive(length); - // discards the head of the list of destroyed entities - archive(entt); - for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) { - archive(entt); + if constexpr(std::is_same_v) { + typename traits_type::entity_type in_use{}; - if(const auto entity = entity_traits::to_entity(entt); entity == pos) { + storage.reserve(length); + archive(in_use); + + for(std::size_t pos{}; pos < in_use; ++pos) { + archive(entt); restore(entt); - } else { - destroy(entt); + } + + for(std::size_t pos = in_use; pos < length; ++pos) { + archive(entt); + + if(const auto entity = to_entity(entt); remloc.contains(entity)) { + if(reg->valid(remloc[entity].second)) { + reg->destroy(remloc[entity].second); + } + + remloc.erase(entity); + } + } + } else { + for(auto &&ref: remloc) { + storage.remove(ref.second.second); + } + + while(length--) { + if(archive(entt); entt != null) { + restore(entt); + + if constexpr(Registry::template storage_for_type::traits_type::page_size == 0u) { + storage.emplace(map(entt)); + } else { + Type elem{}; + archive(elem); + storage.emplace(map(entt), std::move(elem)); + } + } } } @@ -36755,58 +39818,75 @@ class basic_continuous_loader { } /** - * @brief Restores components and assigns them to the right entities. + * @brief Restores all identifiers, including those to be recycled. * - * The template parameter list must be exactly the same used during - * serialization. In the event that the entity to which the component is - * assigned doesn't exist yet, the loader will take care to create a local - * counterpart for it.
- * Members can be either data members of type entity_type or containers of - * entities. In both cases, the loader will visit them and update the - * entities by replacing each one with its local counterpart. + * It creates local counterparts for remote elements as needed. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A non-const reference to this loader. + */ + template + [[deprecated("use .get(archive) instead")]] basic_continuous_loader &entities(Archive &archive) { + return get(archive); + } + + /** + * @brief Serializes all elements of a type with associated identifiers. + * + * It creates local counterparts for remote elements as needed.
+ * Members are either data members of type entity_type or containers of + * entities. In both cases, a loader visits them and replaces entities with + * their local counterpart. * * @tparam Component Type of component to restore. * @tparam Archive Type of input archive. - * @tparam Other Types of components to update with local counterparts. * @tparam Member Types of members to update with their local counterparts. * @param archive A valid reference to an input archive. * @param member Members to update with their local counterparts. * @return A non-const reference to this loader. */ - template - basic_continuous_loader &component(Archive &archive, Member Other::*...member) { - (remove_if_exists(), ...); - (assign(archive, member...), ...); + template + [[deprecated("use .component(archive, members...) instead")]] basic_continuous_loader &component(Archive &archive, Member Clazz::*...member) { + ([&](auto &storage) { + for(auto &&ref: remloc) { + storage.remove(ref.second.second); + } + + typename traits_type::entity_type length{}; + entity_type entt{null}; + + archive(length); + + while(length--) { + if(archive(entt); entt != null) { + restore(entt); + + if constexpr(std::remove_reference_t::traits_type::page_size == 0u) { + storage.emplace(map(entt)); + } else { + typename std::remove_reference_t::value_type elem{}; + archive(elem); + (update(elem, member), ...); + storage.emplace(map(entt), std::move(elem)); + } + } + } + }(reg->template storage()), + ...); + return *this; } /** - * @brief Helps to purge entities that no longer have a conterpart. + * @brief Helps to purge entities that no longer have a counterpart. * * Users should invoke this member function after restoring each snapshot, * unless they know exactly what they are doing. * * @return A non-const reference to this loader. */ - basic_continuous_loader &shrink() { - auto it = remloc.begin(); - - while(it != remloc.cend()) { - const auto local = it->second.first; - bool &dirty = it->second.second; - - if(dirty) { - dirty = false; - ++it; - } else { - if(reg->valid(local)) { - reg->destroy(local); - } - - it = remloc.erase(it); - } - } - + [[deprecated("use .get(archive) instead")]] basic_continuous_loader &shrink() { return *this; } @@ -36816,17 +39896,12 @@ class basic_continuous_loader { * In case all the entities were serialized but only part of the components * was saved, it could happen that some of the entities have no components * once restored.
- * This functions helps to identify and destroy those entities. + * This function helps to identify and destroy those entities. * * @return A non-const reference to this loader. */ basic_continuous_loader &orphans() { - reg->each([this](const auto entt) { - if(reg->orphan(entt)) { - reg->release(entt); - } - }); - + internal::orphans(*reg); return *this; } @@ -36836,7 +39911,8 @@ class basic_continuous_loader { * @return True if `entity` is managed by the loader, false otherwise. */ [[nodiscard]] bool contains(entity_type entt) const noexcept { - return (remloc.find(entt) != remloc.cend()); + const auto it = remloc.find(to_entity(entt)); + return it != remloc.cend() && it->second.first == entt; } /** @@ -36845,18 +39921,15 @@ class basic_continuous_loader { * @return The local identifier if any, the null entity otherwise. */ [[nodiscard]] entity_type map(entity_type entt) const noexcept { - const auto it = remloc.find(entt); - entity_type other = null; - - if(it != remloc.cend()) { - other = it->second.first; + if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) { + return it->second.second; } - return other; + return null; } private: - dense_map> remloc; + dense_map> remloc; registry_type *reg; }; @@ -36962,6 +40035,10 @@ struct sparse_set_iterator final { return *operator->(); } + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } @@ -36971,38 +40048,38 @@ struct sparse_set_iterator final { difference_type offset; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } -template -[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } -template -[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { - return lhs.index() < rhs.index(); +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -37013,14 +40090,6 @@ template * @endcond */ -/*! @brief Sparse set deletion policy. */ -enum class deletion_policy : std::uint8_t { - /*! @brief Swap-and-pop deletion policy. */ - swap_and_pop = 0u, - /*! @brief In-place deletion policy. */ - in_place = 1u -}; - /** * @brief Basic sparse set implementation. * @@ -37028,10 +40097,6 @@ enum class deletion_policy : std::uint8_t { * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a * _packed_ one; one used for direct access through contiguous memory, the other * one used to get the data through an extra level of indirection.
- * This is largely used by the registry to offer users the fastest access ever - * to the components. Views and groups in general are almost entirely designed - * around sparse sets. - * * This type of data structure is widely documented in the literature and on the * web. This is nothing more than a customized implementation suitable for the * purpose of the framework. @@ -37041,7 +40106,7 @@ enum class deletion_policy : std::uint8_t { * no guarantees that entities are returned in the insertion order when iterate * a sparse set. Do not make assumption on the order in any case. * - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Entity A valid entity type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template @@ -37050,23 +40115,26 @@ class basic_sparse_set { static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector; - using entity_traits = entt_traits; [[nodiscard]] auto sparse_ptr(const Entity entt) const { - const auto pos = static_cast(entity_traits::to_entity(entt)); - const auto page = pos / entity_traits::page_size; - return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr; + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; } [[nodiscard]] auto &sparse_ref(const Entity entt) const { ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); - const auto pos = static_cast(entity_traits::to_entity(entt)); - return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)]; + const auto pos = static_cast(traits_type::to_entity(entt)); + return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - index(entt)); } [[nodiscard]] auto &assure_at_least(const Entity entt) { - const auto pos = static_cast(entity_traits::to_entity(entt)); - const auto page = pos / entity_traits::page_size; + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; if(!(page < sparse.size())) { sparse.resize(page + 1u, nullptr); @@ -37074,11 +40142,11 @@ class basic_sparse_set { if(!sparse[page]) { auto page_allocator{packed.get_allocator()}; - sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size); - std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null); + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, null); } - auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; + auto &elem = sparse[page][fast_mod(pos, traits_type::page_size)]; ENTT_ASSERT(elem == null, "Slot not available"); return elem; } @@ -37088,8 +40156,8 @@ class basic_sparse_set { for(auto &&page: sparse) { if(page != nullptr) { - std::destroy(page, page + entity_traits::page_size); - alloc_traits::deallocate(page_allocator, page, entity_traits::page_size); + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); page = nullptr; } } @@ -37100,13 +40168,28 @@ class basic_sparse_set { return nullptr; } - virtual void swap_at(const std::size_t, const std::size_t) {} - virtual void move_element(const std::size_t, const std::size_t) {} + virtual void swap_or_move(const std::size_t, const std::size_t) {} protected: /*! @brief Random access iterator type. */ using basic_iterator = internal::sparse_set_iterator; + /** + * @brief Swaps two items at specific locations. + * @param lhs A position to move from. + * @param rhs The other position to move from. + */ + void swap_at(const std::size_t lhs, const std::size_t rhs) { + const auto entity = static_cast(lhs); + const auto other = static_cast(rhs); + + sparse_ref(packed[lhs]) = traits_type::combine(other, traits_type::to_integral(packed[lhs])); + sparse_ref(packed[rhs]) = traits_type::combine(entity, traits_type::to_integral(packed[rhs])); + + using std::swap; + swap(packed[lhs], packed[rhs]); + } + /** * @brief Erases an entity from a sparse set. * @param it An iterator to the element to pop. @@ -37114,8 +40197,8 @@ class basic_sparse_set { void swap_and_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched"); auto &self = sparse_ref(*it); - const auto entt = entity_traits::to_entity(self); - sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back())); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); packed[static_cast(entt)] = packed.back(); // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((packed.back() = null, true), ""); @@ -37130,8 +40213,8 @@ class basic_sparse_set { */ void in_place_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched"); - const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null)); - packed[static_cast(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved)); + const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null)); + packed[static_cast(entt)] = std::exchange(free_list, traits_type::combine(entt, tombstone)); } protected: @@ -37152,6 +40235,23 @@ class basic_sparse_set { } } + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + if(const auto prev = std::exchange(free_list, tombstone); prev == null) { + for(auto first = begin(); !(first.index() < 0); ++first) { + sparse_ref(*first) = null; + } + } else { + for(auto first = begin(); !(first.index() < 0); ++first) { + if(*first != tombstone) { + sparse_ref(*first) = null; + } + } + } + + packed.clear(); + } + /** * @brief Assigns an entity to a sparse set. * @param entt A valid identifier. @@ -37163,25 +40263,27 @@ class basic_sparse_set { if(auto &elem = assure_at_least(entt); free_list == null || force_back) { packed.push_back(entt); - elem = entity_traits::combine(static_cast(packed.size() - 1u), entity_traits::to_integral(entt)); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); return begin(); } else { - const auto pos = static_cast(entity_traits::to_entity(free_list)); - elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt)); + const auto pos = static_cast(traits_type::to_entity(free_list)); + elem = traits_type::combine(traits_type::to_integral(free_list), traits_type::to_integral(entt)); free_list = std::exchange(packed[pos], entt); return --(end() - pos); } } public: - /*! @brief Allocator type. */ - using allocator_type = Allocator; + /*! @brief Entity traits. */ + using traits_type = entt_traits; /*! @brief Underlying entity identifier. */ - using entity_type = typename entity_traits::value_type; + using entity_type = typename traits_type::value_type; /*! @brief Underlying version type. */ - using version_type = typename entity_traits::version_type; + using version_type = typename traits_type::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; /*! @brief Pointer type to contained entities. */ using pointer = typename packed_container_type::const_pointer; /*! @brief Random access iterator type. */ @@ -37191,7 +40293,7 @@ class basic_sparse_set { /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ - using const_reverse_iterator = reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; /*! @brief Default constructor. */ basic_sparse_set() @@ -37215,14 +40317,14 @@ class basic_sparse_set { /** * @brief Constructs an empty container with the given value type, policy * and allocator. - * @param value Returned value type, if any. + * @param elem Returned value type, if any. * @param pol Type of deletion policy. * @param allocator The allocator to use (possibly default-constructed). */ - explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) : sparse{allocator}, packed{allocator}, - info{&value}, + info{&elem}, free_list{tombstone}, mode{pol} {} @@ -37339,7 +40441,7 @@ class basic_sparse_set { * @return Extent of the sparse set. */ [[nodiscard]] size_type extent() const noexcept { - return sparse.size() * entity_traits::page_size; + return sparse.size() * traits_type::page_size; } /** @@ -37364,6 +40466,14 @@ class basic_sparse_set { return packed.empty(); } + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (free_list == null); + } + /** * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. @@ -37375,8 +40485,7 @@ class basic_sparse_set { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first entity of the internal packed - * array. If the sparse set is empty, the returned iterator will be equal to + * If the sparse set is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity of the sparse set. @@ -37393,11 +40502,6 @@ class basic_sparse_set { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last entity in - * a sparse set. Attempting to dereference the returned iterator results in - * undefined behavior. - * * @return An iterator to the element following the last entity of a sparse * set. */ @@ -37413,9 +40517,8 @@ class basic_sparse_set { /** * @brief Returns a reverse iterator to the beginning. * - * The returned iterator points to the first entity of the reversed internal - * packed array. If the sparse set is empty, the returned iterator will be - * equal to `rend()`. + * If the sparse set is empty, the returned iterator will be equal to + * `rend()`. * * @return An iterator to the first entity of the reversed internal packed * array. @@ -37431,11 +40534,6 @@ class basic_sparse_set { /** * @brief Returns a reverse iterator to the end. - * - * The returned iterator points to the element following the last entity in - * the reversed sparse set. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last entity of the * reversed sparse set. */ @@ -37455,7 +40553,7 @@ class basic_sparse_set { * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { - return contains(entt) ? --(end() - index(entt)) : end(); + return contains(entt) ? to_iterator(entt) : end(); } /** @@ -37465,9 +40563,9 @@ class basic_sparse_set { */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); - constexpr auto cap = entity_traits::to_entity(null); + constexpr auto cap = traits_type::to_entity(null); // testing versions permits to avoid accessing the packed array - return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap); + return elem && (((~cap & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); } /** @@ -37478,8 +40576,8 @@ class basic_sparse_set { */ [[nodiscard]] version_type current(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); - constexpr auto fallback = entity_traits::to_version(tombstone); - return elem ? entity_traits::to_version(*elem) : fallback; + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; } /** @@ -37494,7 +40592,7 @@ class basic_sparse_set { */ [[nodiscard]] size_type index(const entity_type entt) const noexcept { ENTT_ASSERT(contains(entt), "Set does not contain entity"); - return static_cast(entity_traits::to_entity(sparse_ref(entt))); + return static_cast(traits_type::to_entity(sparse_ref(entt))); } /** @@ -37526,13 +40624,13 @@ class basic_sparse_set { * @param entt A valid identifier. * @return An opaque pointer to the element assigned to the entity, if any. */ - [[nodiscard]] const void *get(const entity_type entt) const noexcept { + [[nodiscard]] const void *value(const entity_type entt) const noexcept { return get_at(index(entt)); } - /*! @copydoc get */ - [[nodiscard]] void *get(const entity_type entt) noexcept { - return const_cast(std::as_const(*this).get(entt)); + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(entt)); } /** @@ -37543,28 +40641,12 @@ class basic_sparse_set { * results in undefined behavior. * * @param entt A valid identifier. - * @param value Optional opaque value to forward to mixins, if any. + * @param elem Optional opaque element to forward to mixins, if any. * @return Iterator pointing to the emplaced element in case of success, the * `end()` iterator otherwise. */ - iterator emplace(const entity_type entt, const void *value = nullptr) { - return try_emplace(entt, false, value); - } - - /** - * @brief Bump the version number of an entity. - * - * @warning - * Attempting to bump the version of an entity that doesn't belong to the - * sparse set results in undefined behavior. - * - * @param entt A valid identifier. - */ - void bump(const entity_type entt) { - auto &entity = sparse_ref(entt); - ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); - entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt)); - packed[static_cast(entity_traits::to_entity(entity))] = entt; + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, false, elem); } /** @@ -37581,7 +40663,7 @@ class basic_sparse_set { * success, the `end()` iterator otherwise. */ template - iterator insert(It first, It last) { + iterator push(It first, It last) { for(auto it = first; it != last; ++it) { try_emplace(*it, true); } @@ -37589,6 +40671,24 @@ class basic_sparse_set { return first == last ? end() : find(*first); } + /** + * @brief Bump the version number of an entity. + * + * @warning + * Attempting to bump the version of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &entity = sparse_ref(entt); + ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); + entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt)); + packed[static_cast(traits_type::to_entity(entity))] = entt; + return traits_type::to_version(entt); + } + /** * @brief Erases an entity from a sparse set. * @@ -37599,7 +40699,7 @@ class basic_sparse_set { * @param entt A valid identifier. */ void erase(const entity_type entt) { - const auto it = --(end() - index(entt)); + const auto it = to_iterator(entt); pop(it, it + 1u); } @@ -37643,29 +40743,45 @@ class basic_sparse_set { size_type remove(It first, It last) { size_type count{}; - for(; first != last; ++first) { - count += remove(*first); + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += std::distance(it, first); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } } return count; } - /*! @brief Removes all tombstones from the packed array of a sparse set. */ + /*! @brief Removes all tombstones from a sparse set. */ void compact() { size_type from = packed.size(); for(; from && packed[from - 1u] == tombstone; --from) {} - for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) { - if(const size_type to = entity_traits::to_entity(*it); to < from) { + for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[traits_type::to_entity(*it)])) { + if(const size_type to = traits_type::to_entity(*it); to < from) { --from; - move_element(from, to); + swap_or_move(from, to); - using std::swap; - swap(packed[from], packed[to]); + packed[to] = std::exchange(packed[from], tombstone); + const auto entity = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); - const auto entity = static_cast(to); - sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to])); - *it = entity_traits::combine(static_cast(from), entity_traits::reserved); + *it = traits_type::combine(static_cast(from), tombstone); for(; from && packed[from - 1u] == tombstone; --from) {} } } @@ -37688,21 +40804,12 @@ class basic_sparse_set { * @param rhs A valid identifier. */ void swap_elements(const entity_type lhs, const entity_type rhs) { - ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities"); - - auto &entt = sparse_ref(lhs); - auto &other = sparse_ref(rhs); + const auto from = index(lhs); + const auto to = index(rhs); - const auto from = entity_traits::to_entity(entt); - const auto to = entity_traits::to_entity(other); - - // basic no-leak guarantee (with invalid state) if swapping throws - swap_at(static_cast(from), static_cast(to)); - entt = entity_traits::combine(to, entity_traits::to_integral(packed[from])); - other = entity_traits::combine(from, entity_traits::to_integral(packed[to])); - - using std::swap; - swap(packed[from], packed[to]); + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, to); } /** @@ -37750,9 +40857,9 @@ class basic_sparse_set { const auto idx = index(packed[next]); const auto entt = packed[curr]; - swap_at(next, idx); - const auto entity = static_cast(curr); - sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr])); + swap_or_move(next, idx); + const auto entity = static_cast(curr); + sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr])); curr = std::exchange(next, idx); } } @@ -37780,48 +40887,35 @@ class basic_sparse_set { * @brief Sort entities according to their order in another sparse set. * * Entities that are part of both the sparse sets are ordered internally - * according to the order they have in `other`. All the other entities goes - * to the end of the list and there are no guarantees on their order.
- * In other terms, this function can be used to impose the same order on two - * sets by using one of them as a master and the other one as a slave. - * - * Iterating the sparse set with a couple of iterators returns elements in - * the expected order after a call to `respect`. See `begin` and `end` for - * more details. + * according to the order they have in `other`.
+ * All the other entities goes to the end of the list and there are no + * guarantees on their order. * * @param other The sparse sets that imposes the order of the entities. */ - void respect(const basic_sparse_set &other) { + void sort_as(const basic_sparse_set &other) { compact(); const auto to = other.end(); auto from = other.begin(); - for(size_type pos = packed.size() - 1; pos && from != to; ++from) { - if(contains(*from)) { - if(*from != packed[pos]) { + for(auto it = begin(); it.index() && from != to; ++from) { + if(const auto curr = *from; contains(curr)) { + if(const auto entt = *it; entt != curr) { // basic no-leak guarantee (with invalid state) if swapping throws - swap_elements(packed[pos], *from); + swap_elements(entt, curr); } - --pos; + ++it; } } } /*! @brief Clears a sparse set. */ void clear() { - if(const auto last = end(); free_list == null) { - pop(begin(), last); - } else { - for(auto &&entity: *this) { - // tombstone filter on itself - if(const auto it = find(entity); it != last) { - pop(it, it + 1u); - } - } - } - + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); free_list = tombstone; packed.clear(); } @@ -37862,8 +40956,6 @@ class basic_sparse_set { #include // #include "../config/config.h" -// #include "../core/compressed_pair.hpp" - // #include "../core/iterator.hpp" // #include "../core/memory.hpp" @@ -37878,8 +40970,6 @@ class basic_sparse_set { // #include "sparse_set.hpp" -// #include "storage_mixin.hpp" - namespace entt { @@ -37890,13 +40980,12 @@ namespace entt { namespace internal { -template +template class storage_iterator final { - friend storage_iterator; + friend storage_iterator; using container_type = std::remove_const_t; using alloc_traits = std::allocator_traits; - using comp_traits = component_traits>; using iterator_traits = std::iterator_traits, @@ -37913,12 +41002,12 @@ class storage_iterator final { constexpr storage_iterator() noexcept = default; constexpr storage_iterator(Container *ref, const difference_type idx) noexcept - : packed{ref}, + : payload{ref}, offset{idx} {} template, typename = std::enable_if_t> - constexpr storage_iterator(const storage_iterator> &other) noexcept - : storage_iterator{other.packed, other.offset} {} + constexpr storage_iterator(const storage_iterator, Size> &other) noexcept + : storage_iterator{other.payload, other.offset} {} constexpr storage_iterator &operator++() noexcept { return --offset, *this; @@ -37958,12 +41047,12 @@ class storage_iterator final { [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { const auto pos = index() - value; - return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; + return (*payload)[pos / Size][fast_mod(pos, Size)]; } [[nodiscard]] constexpr pointer operator->() const noexcept { const auto pos = index(); - return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); + return (*payload)[pos / Size] + fast_mod(pos, Size); } [[nodiscard]] constexpr reference operator*() const noexcept { @@ -37975,42 +41064,42 @@ class storage_iterator final { } private: - Container *packed; + Container *payload; difference_type offset; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } -template -[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } -template -[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { - return lhs.index() < rhs.index(); +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -38020,10 +41109,11 @@ class extended_storage_iterator final { friend class extended_storage_iterator; public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); using pointer = input_iterator_pointer; using reference = value_type; - using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr extended_storage_iterator() @@ -38053,20 +41143,24 @@ class extended_storage_iterator final { return {*std::get(it), *std::get(it)...}; } - template - friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } + + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; private: std::tuple it; }; -template -[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return std::get<0>(lhs.it) == std::get<0>(rhs.it); } -template -[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -38089,43 +41183,43 @@ template * normally available for non-empty types will not be available for empty ones. * * @tparam Type Type of objects assigned to the entities. - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Entity A valid entity type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_storage: public basic_sparse_set::template rebind_alloc> { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); - using underlying_type = basic_sparse_set>; using container_type = std::vector>; - using comp_traits = component_traits; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); [[nodiscard]] auto &element_at(const std::size_t pos) const { - return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; } auto assure_at_least(const std::size_t pos) { - auto &&container = packed.first(); - const auto idx = pos / comp_traits::page_size; + const auto idx = pos / traits_type::page_size; - if(!(idx < container.size())) { - auto curr = container.size(); - container.resize(idx + 1u, nullptr); + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); ENTT_TRY { - for(const auto last = container.size(); curr < last; ++curr) { - container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size); + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); } } ENTT_CATCH { - container.resize(curr); + payload.resize(curr); ENTT_THROW; } } - return container[idx] + fast_mod(pos, comp_traits::page_size); + return payload[idx] + fast_mod(pos, traits_type::page_size); } template @@ -38134,7 +41228,7 @@ class basic_storage: public basic_sparse_set(it.index())); - entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward(args)...); + entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward(args)...); } ENTT_CATCH { base_type::pop(it, it + 1u); @@ -38145,25 +41239,24 @@ class basic_storage: public basic_sparse_set(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } + } + /** * @brief Assigns an entity to a storage. * @param entt A valid identifier. @@ -38226,7 +41333,7 @@ class basic_storage: public basic_sparse_set) { return emplace_element(entt, force_back, *static_cast(value)); @@ -38245,22 +41352,24 @@ class basic_storage: public basic_sparse_set; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; /*! @brief Pointer type to contained elements. */ using pointer = typename container_type::pointer; /*! @brief Constant pointer type to contained elements. */ using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; /*! @brief Random access iterator type. */ - using iterator = internal::storage_iterator; + using iterator = internal::storage_iterator; /*! @brief Constant random access iterator type. */ - using const_iterator = internal::storage_iterator; + using const_iterator = internal::storage_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ @@ -38269,6 +41378,10 @@ class basic_storage: public basic_sparse_set>; /*! @brief Constant extended iterable storage proxy. */ using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; /*! @brief Default constructor. */ basic_storage() @@ -38279,8 +41392,8 @@ class basic_storage: public basic_sparse_set(), deletion_policy{comp_traits::in_place_delete}, allocator}, - packed{container_type{allocator}, allocator} {} + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator}, + payload{allocator} {} /** * @brief Move constructor. @@ -38288,7 +41401,7 @@ class basic_storage: public basic_sparse_set(base_type::size()); - return const_iterator{&packed.first(), pos}; + return const_iterator{&payload, pos}; } /*! @copydoc cbegin */ @@ -38404,21 +41514,16 @@ class basic_storage: public basic_sparse_set(base_type::size()); - return iterator{&packed.first(), pos}; + return iterator{&payload, pos}; } /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { - return const_iterator{&packed.first(), {}}; + return const_iterator{&payload, {}}; } /*! @copydoc cend */ @@ -38428,15 +41533,13 @@ class basic_storage: public basic_sparse_set value_type &emplace(const entity_type entt, Args &&...args) { - if constexpr(std::is_aggregate_v) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { const auto it = emplace_element(entt, false, Type{std::forward(args)...}); return element_at(static_cast(it.index())); } else { @@ -38561,12 +41659,15 @@ class basic_storage: public basic_sparse_set - void insert(It first, It last, const value_type &value = {}) { + iterator insert(It first, It last, const value_type &value = {}) { for(; first != last; ++first) { emplace_element(*first, true, value); } + + return begin(); } /** @@ -38580,12 +41681,15 @@ class basic_storage: public basic_sparse_set::value_type, value_type>>> - void insert(EIt first, EIt last, CIt from) { + iterator insert(EIt first, EIt last, CIt from) { for(; first != last; ++first, ++from) { emplace_element(*first, true, *from); } + + return begin(); } /** @@ -38605,34 +41709,54 @@ class basic_storage: public basic_sparse_set packed; + container_type payload; }; /*! @copydoc basic_storage */ template -class basic_storage>> +class basic_storage::page_size == 0u>> : public basic_sparse_set::template rebind_alloc> { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); - using underlying_type = basic_sparse_set>; - using comp_traits = component_traits; public: /*! @brief Base type. */ - using base_type = underlying_type; - /*! @brief Allocator type. */ - using allocator_type = Allocator; + using base_type = basic_sparse_set>; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; /*! @brief Extended iterable storage proxy. */ using iterable = iterable_adaptor>; /*! @brief Constant extended iterable storage proxy. */ using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; /*! @brief Default constructor. */ basic_storage() @@ -38643,7 +41767,7 @@ class basic_storage(), deletion_policy{comp_traits::in_place_delete}, allocator} {} + : base_type{type_id(), deletion_policy{traits_type::in_place_delete}, allocator} {} /** * @brief Move constructor. @@ -38758,286 +41882,325 @@ class basic_storage -struct storage_type { - /*! @brief Type-to-storage conversion result. */ - using type = sigh_storage_mixin>; -}; -/** - * @brief Helper type. - * @tparam Args Arguments to forward. - */ -template -using storage_type_t = typename storage_type::type; + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}}; + } -/** - * Type-to-storage conversion utility that preserves constness. - * @tparam Type Storage value type, eventually const. - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Allocator Type of allocator used to manage memory and elements. - */ -template -struct storage_for { - /*! @brief Type-to-storage conversion result. */ - using type = constness_as_t, Entity, Allocator>, Type>; + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}}; + } }; /** - * @brief Helper type. - * @tparam Args Arguments to forward. + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. */ -template -using storage_for_t = typename storage_for::type; - -} // namespace entt - -#endif - -// #include "entity/storage_mixin.hpp" -#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP -#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP - -#include -// #include "../config/config.h" - -// #include "../core/any.hpp" - -// #include "../signal/sigh.hpp" - -// #include "fwd.hpp" - - -namespace entt { +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + using local_traits_type = entt_traits; -/** - * @brief Mixin type used to add signal support to storage types. - * - * The function type of a listener is equivalent to: - * - * @code{.cpp} - * void(basic_registry &, entity_type); - * @endcode - * - * This applies to all signals made available. - * - * @tparam Type The type of the underlying storage. - */ -template -class sigh_storage_mixin final: public Type { - using basic_registry_type = basic_registry; - using sigh_type = sigh; - using basic_iterator = typename Type::basic_iterator; + auto entity_at(const std::size_t pos) const noexcept { + ENTT_ASSERT(pos < local_traits_type::to_entity(null), "Invalid element"); + return local_traits_type::combine(static_cast(pos), {}); + } - void pop(basic_iterator first, basic_iterator last) override { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); +private: + void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) override { + ENTT_ASSERT(((lhs < length) + (rhs < length)) != 1u, "Cross swapping is not supported"); + } +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { for(; first != last; ++first) { - const auto entt = *first; - destruction.publish(*owner, entt); - const auto it = Type::find(entt); - Type::pop(it, it + 1u); + if(const auto pos = base_type::index(*first); pos < length) { + base_type::bump(local_traits_type::next(*first)); + + if(pos != --length) { + base_type::swap_at(pos, length); + } + } } } - basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::try_emplace(entt, force_back, value); - construction.publish(*owner, entt); - return Type::find(entt); + /*! @brief Erases all entities of a sparse set. */ + void pop_all() override { + length = 0u; + base_type::pop_all(); + } + + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(emplace(hint)); } public: - /*! @brief Allocator type. */ - using allocator_type = typename Type::allocator_type; + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Entity; + /*! @brief Component traits. */ + using traits_type = component_traits; /*! @brief Underlying entity identifier. */ - using entity_type = typename Type::entity_type; - /*! @brief Expected registry type. */ - using registry_type = basic_registry_type; + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; /*! @brief Default constructor. */ - sigh_storage_mixin() - : sigh_storage_mixin{allocator_type{}} {} + basic_storage() + : basic_storage{allocator_type{}} { + } /** - * @brief Constructs an empty storage with a given allocator. + * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ - explicit sigh_storage_mixin(const allocator_type &allocator) - : Type{allocator}, - owner{}, - construction{allocator}, - destruction{allocator}, - update{allocator} {} + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), deletion_policy::swap_and_pop, allocator}, + length{} {} /** * @brief Move constructor. * @param other The instance to move from. */ - sigh_storage_mixin(sigh_storage_mixin &&other) noexcept - : Type{std::move(other)}, - owner{other.owner}, - construction{std::move(other.construction)}, - destruction{std::move(other.destruction)}, - update{std::move(other.update)} {} + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + length{std::exchange(other.length, size_type{})} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ - sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept - : Type{std::move(other), allocator}, - owner{other.owner}, - construction{std::move(other.construction), allocator}, - destruction{std::move(other.destruction), allocator}, - update{std::move(other.update), allocator} {} + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator}, + length{std::exchange(other.length, size_type{})} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This storage. */ - sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept { - Type::operator=(std::move(other)); - owner = other.owner; - construction = std::move(other.construction); - destruction = std::move(other.destruction); - update = std::move(other.update); + basic_storage &operator=(basic_storage &&other) noexcept { + base_type::operator=(std::move(other)); + length = std::exchange(other.length, size_type{}); return *this; } /** - * @brief Exchanges the contents with those of a given storage. - * @param other Storage to exchange the content with. + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. */ - void swap(sigh_storage_mixin &other) { - using std::swap; - Type::swap(other); - swap(owner, other.owner); - swap(construction, other.construction); - swap(destruction, other.destruction); - swap(update, other.update); + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one"); } /** - * @brief Returns a sink object. - * - * The sink returned by this function can be used to receive notifications - * whenever a new instance is created and assigned to an entity.
- * Listeners are invoked after the object has been assigned to the entity. + * @brief Returns an empty tuple. * - * @sa sink + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. * - * @return A temporary sink object. + * @param entt A valid identifier. + * @return Returns an empty tuple. */ - [[nodiscard]] auto on_construct() noexcept { - return sink{construction}; + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one"); + return std::tuple{}; } /** - * @brief Returns a sink object. - * - * The sink returned by this function can be used to receive notifications - * whenever an instance is explicitly updated.
- * Listeners are invoked after the object has been updated. - * - * @sa sink - * - * @return A temporary sink object. + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. */ - [[nodiscard]] auto on_update() noexcept { - return sink{update}; + void swap(basic_storage &other) { + using std::swap; + base_type::swap(other); + swap(length, other.length); } /** - * @brief Returns a sink object. - * - * The sink returned by this function can be used to receive notifications - * whenever an instance is removed from an entity and thus destroyed.
- * Listeners are invoked before the object has been removed from the entity. - * - * @sa sink - * - * @return A temporary sink object. + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. */ - [[nodiscard]] auto on_destroy() noexcept { - return sink{destruction}; + entity_type emplace() { + if(length == base_type::size()) { + return *base_type::try_emplace(entity_at(length++), true); + } + + return base_type::operator[](length++); } /** - * @brief Assigns entities to a storage. - * @tparam Args Types of arguments to use to construct the object. - * @param entt A valid identifier. - * @param args Parameters to use to initialize the object. - * @return A reference to the newly created object. + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. */ - template - decltype(auto) emplace(const entity_type entt, Args &&...args) { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::emplace(entt, std::forward(args)...); - construction.publish(*owner, entt); - return this->get(entt); + entity_type emplace(const entity_type hint) { + if(hint == null || hint == tombstone) { + return emplace(); + } else if(const auto curr = local_traits_type::construct(local_traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) { + const auto pos = static_cast(local_traits_type::to_entity(hint)); + + while(!(pos < base_type::size())) { + base_type::try_emplace(entity_at(base_type::size()), true); + } + + base_type::swap_at(pos, length++); + } else if(const auto idx = base_type::index(curr); idx < length) { + return emplace(); + } else { + base_type::swap_at(idx, length++); + } + + base_type::bump(hint); + + return hint; } /** - * @brief Patches the given instance for an entity. + * @brief Updates a given identifier. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. - * @return A reference to the patched instance. */ template - decltype(auto) patch(const entity_type entt, Func &&...func) { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::patch(entt, std::forward(func)...); - update.publish(*owner, entt); - return this->get(entt); + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + (std::forward(func)(), ...); } /** - * @brief Assigns entities to a storage. - * @tparam It Type of input iterator. - * @tparam Args Types of arguments to use to construct the objects assigned - * to the entities. - * @param first An iterator to the first element of the range of entities. - * @param last An iterator past the last element of the range of entities. - * @param args Parameters to use to initialize the objects assigned to the - * entities. + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. */ - template - void insert(It first, It last, Args &&...args) { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - Type::insert(first, last, std::forward(args)...); + template + void insert(It first, It last) { + for(const auto sz = base_type::size(); first != last && length != sz; ++first, ++length) { + *first = base_type::operator[](length); + } - for(auto it = construction.empty() ? last : first; it != last; ++it) { - construction.publish(*owner, *it); + for(; first != last; ++first) { + *first = *base_type::try_emplace(entity_at(length++), true); } } /** - * @brief Forwards variables to derived classes, if any. - * @param value A variable wrapped in an opaque container. + * @brief Makes all elements in a range contiguous. + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to pack. + * @param last An iterator past the last element of the range to pack. + * @return The number of elements within the newly created range. */ - void bind(any value) noexcept final { - auto *reg = any_cast(&value); - owner = reg ? reg : owner; - Type::bind(std::move(value)); + template + size_type pack(It first, It last) { + size_type len = length; + + for(; first != last; ++first, --len) { + const auto pos = base_type::index(*first); + ENTT_ASSERT(pos < length, "Invalid element"); + base_type::swap_at(pos, static_cast(len - 1u)); + } + + return (length - len); + } + + /** + * @brief Returns the number of elements considered still in use. + * @return The number of elements considered still in use. + */ + [[nodiscard]] size_type in_use() const noexcept { + return length; + } + + /** + * @brief Sets the number of elements considered still in use. + * @param len The number of elements considered still in use. + */ + void in_use(const size_type len) noexcept { + ENTT_ASSERT(!(len > base_type::size()), "Invalid length"); + length = len; + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::end() - length}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cend() - length}, internal::extended_storage_iterator{base_type::cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rbegin() + length}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crbegin() + length}}; } private: - basic_registry_type *owner; - sigh_type construction; - sigh_type destruction; - sigh_type update; + size_type length; }; } // namespace entt @@ -39059,16 +42222,10 @@ class sigh_storage_mixin final: public Type { // #include "../core/type_traits.hpp" -// #include "component.hpp" - // #include "entity.hpp" // #include "fwd.hpp" -// #include "sparse_set.hpp" - -// #include "storage.hpp" - namespace entt { @@ -39079,6 +42236,24 @@ namespace entt { namespace internal { +template +[[nodiscard]] auto filter_as_tuple(const std::array &filter) noexcept { + return std::apply([](const auto *...curr) { return std::make_tuple(static_cast(const_cast *>(curr))...); }, filter); +} + +template +[[nodiscard]] auto none_of(const std::array &filter, const typename Type::entity_type entt) noexcept { + return std::apply([entt](const auto *...curr) { return (!(curr && curr->contains(entt)) && ...); }, filter); +} + +template +[[nodiscard]] auto view_pack(const std::tuple value, const std::tuple excl, std::index_sequence) { + const auto pools = std::tuple_cat(value, excl); + basic_view, exclude_t> elem{}; + (((std::get(pools) != nullptr) ? elem.template storage(*std::get(pools)) : void()), ...); + return elem; +} + template class view_iterator final { using iterator_type = typename Type::const_iterator; @@ -39086,7 +42261,7 @@ class view_iterator final { [[nodiscard]] bool valid() const noexcept { return ((Get != 0u) || (*it != tombstone)) && std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) - && std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); + && none_of(filter, *it); } public: @@ -39102,11 +42277,11 @@ class view_iterator final { pools{}, filter{} {} - view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) noexcept + view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl) noexcept : it{curr}, last{to}, - pools{all_of}, - filter{none_of} { + pools{value}, + filter{excl} { while(it != last && !valid()) { ++it; } @@ -39152,6 +42327,7 @@ template template struct extended_view_iterator final { + using iterator_type = It; using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; @@ -39162,9 +42338,9 @@ struct extended_view_iterator final { : it{}, pools{} {} - extended_view_iterator(It from, std::tuple storage) + extended_view_iterator(It from, std::tuple value) : it{from}, - pools{storage} {} + pools{value} {} extended_view_iterator &operator++() noexcept { return ++it, *this; @@ -39183,6 +42359,10 @@ struct extended_view_iterator final { return operator*(); } + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + template friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; @@ -39234,31 +42414,33 @@ class basic_view; * or removed from it). * * The entity currently pointed is destroyed. * - * In all other cases, modifying the pools iterated by the view in any way - * invalidates all the iterators and using them results in undefined behavior. + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. * * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. */ template class basic_view, exclude_t> { - using underlying_type = std::common_type_t; - using basic_common_type = std::common_type_t; + static constexpr auto offset = sizeof...(Get); + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; template friend class basic_view; template - static constexpr std::size_t index_of = type_list_index_v, type_list>; + static constexpr std::size_t index_of = type_list_index_v, type_list>; [[nodiscard]] auto opaque_check_set() const noexcept { - std::array other{}; + std::array other{}; std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); return other; } - [[nodiscard]] auto filter_as_array() const noexcept { - return std::apply([](const auto *...curr) { return std::array{curr...}; }, filter); + void unchecked_refresh() noexcept { + view = std::get<0>(pools); + std::apply([this](auto *, auto *...other) { ((this->view = other->size() < this->view->size() ? other : this->view), ...); }, pools); } template @@ -39266,18 +42448,14 @@ class basic_view, exclude_t> { if constexpr(Curr == Other) { return std::forward_as_tuple(std::get(curr)...); } else { - return storage().get_as_tuple(std::get<0>(curr)); + return std::get(pools)->get_as_tuple(std::get<0>(curr)); } } - [[nodiscard]] auto reject(const underlying_type entt) const noexcept { - return std::apply([entt](const auto *...curr) { return (curr->contains(entt) || ...); }, filter); - } - template void each(Func &func, std::index_sequence) const { - for(const auto curr: storage().each()) { - if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || storage().contains(entt)) && ...) && !reject(entt)) { + for(const auto curr: std::get(pools)->each()) { + if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || std::get(pools)->contains(entt)) && ...) && internal::none_of(filter, entt)) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); } else { @@ -39289,7 +42467,7 @@ class basic_view, exclude_t> { template void pick_and_each(Func &func, std::index_sequence seq) const { - ((&storage() == view ? each(func, seq) : void()), ...); + ((std::get(pools) == view ? each(func, seq) : void()), ...); } public: @@ -39298,9 +42476,9 @@ class basic_view, exclude_t> { /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = basic_common_type; + using common_type = base_type; /*! @brief Bidirectional iterator type. */ - using iterator = internal::view_iterator; + using iterator = internal::view_iterator; /*! @brief Iterable view type. */ using iterable = iterable_adaptor>; @@ -39313,79 +42491,106 @@ class basic_view, exclude_t> { /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. - * @param exclude The storage for the types used to filter the view. + * @param excl The storage for the types used to filter the view. */ - basic_view(Get &...value, Exclude &...exclude) noexcept + basic_view(Get &...value, Exclude &...excl) noexcept : pools{&value...}, - filter{&exclude...}, - view{[](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }(&value...)} {} + filter{&excl...}, + view{} { + unchecked_refresh(); + } /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. - * @param exclude The storage for the types used to filter the view. + * @param excl The storage for the types used to filter the view. */ - basic_view(std::tuple value, std::tuple exclude = {}) noexcept - : pools{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, value)}, - filter{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, exclude)}, - view{std::apply([](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }, pools)} {} + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} /** - * @brief Creates a new view driven by a given component in its iterations. - * @tparam Type Type of component used to drive the iteration. - * @return A new view driven by the given component in its iterations. + * @brief Forces a view to use a given component to drive iterations + * @tparam Type Type of component to use to drive iterations. */ template - [[nodiscard]] basic_view use() const noexcept { - return use>(); + void use() noexcept { + use>(); } /** - * @brief Creates a new view driven by a given component in its iterations. - * @tparam Index Index of the component used to drive the iteration. - * @return A new view driven by the given component in its iterations. + * @brief Forces a view to use a given component to drive iterations + * @tparam Index Index of the component to use to drive iterations. */ template - [[nodiscard]] basic_view use() const noexcept { - basic_view other{*this}; - other.view = &storage(); - return other; + void use() noexcept { + if(view) { + view = std::get(pools); + } } - /** - * @brief Updates the internal leading view if required. - * @return A newly created and internally optimized view. - */ - [[nodiscard]] basic_view refresh() const noexcept { - return std::apply([](auto *...elem) { return basic_view{*elem...}; }, std::tuple_cat(pools, filter)); + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + if(view || std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) { + unchecked_refresh(); + } } /** - * @brief Returns the leading storage of a view. + * @brief Returns the leading storage of a view, if any. * @return The leading storage of the view. */ - [[nodiscard]] const base_type &handle() const noexcept { - return *view; + [[nodiscard]] const common_type *handle() const noexcept { + return view; } /** - * @brief Returns the storage for a given component type. - * @tparam Comp Type of component of which to return the storage. + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { + [[nodiscard]] auto *storage() const noexcept { return storage>(); } /** - * @brief Returns the storage for a given index. + * @brief Returns the storage for a given index, if any. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); + [[nodiscard]] auto *storage() const noexcept { + if constexpr(Index < offset) { + return std::get(pools); + } else { + return std::get(internal::filter_as_tuple(filter)); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + if constexpr(Index < offset) { + std::get(pools) = &elem; + refresh(); + } else { + std::get(filter) = &elem; + } } /** @@ -39393,32 +42598,26 @@ class basic_view, exclude_t> { * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const noexcept { - return view->size(); + return view ? view->size() : size_type{}; } /** * @brief Returns an iterator to the first entity of the view. * - * The returned iterator points to the first entity of the view. If the view - * is empty, the returned iterator will be equal to `end()`. + * If the view is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const noexcept { - return iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; + return view ? iterator{view->begin(), view->end(), opaque_check_set(), filter} : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the view. - * - * The returned iterator points to the entity following the last entity of - * the view. Attempting to dereference the returned iterator results in - * undefined behavior. - * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { - return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()}; + return view ? iterator{view->end(), view->end(), opaque_check_set(), filter} : iterator{}; } /** @@ -39437,9 +42636,13 @@ class basic_view, exclude_t> { * otherwise. */ [[nodiscard]] entity_type back() const noexcept { - auto it = view->rbegin(); - for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} - return it == view->rend() ? null : *it; + if(view) { + auto it = view->rbegin(); + for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} + return it == view->rend() ? null : *it; + } + + return null; } /** @@ -39449,7 +42652,7 @@ class basic_view, exclude_t> { * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { - return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter_as_array()} : end(); + return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter} : end(); } /** @@ -39462,11 +42665,12 @@ class basic_view, exclude_t> { } /** - * @brief Checks if a view is properly initialized. - * @return True if the view is properly initialized, false otherwise. + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return view != nullptr; + return std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools) + && std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, filter); } /** @@ -39475,8 +42679,7 @@ class basic_view, exclude_t> { * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { - return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) - && std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); + return view && std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && internal::none_of(filter, entt); } /** @@ -39486,39 +42689,33 @@ class basic_view, exclude_t> { * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * - * @tparam Type Types of components to get. + * @tparam Type Type of the component to get. + * @tparam Other Other types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ - template + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { - return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); - } else if constexpr(sizeof...(Type) == 1) { - return (storage>().get(entt), ...); - } else { - return std::tuple_cat(storage>().get_as_tuple(entt)...); - } + return get, index_of...>(entt); } /** * @brief Returns the components assigned to the given entity. * - * @warning - * Attempting to use an entity that doesn't belong to the view results in - * undefined behavior. + * @sa get * - * @tparam First Index of a component to get. - * @tparam Other Indexes of other components to get. + * @tparam Index Indexes of the components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ - template + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Other) == 0) { - return storage().get(entt); + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(pools)->get(entt), ...); } else { - return std::tuple_cat(storage().get_as_tuple(entt), storage().get_as_tuple(entt)...); + return std::tuple_cat(std::get(pools)->get_as_tuple(entt)...); } } @@ -39542,7 +42739,7 @@ class basic_view, exclude_t> { */ template void each(Func func) const { - pick_and_each(func, std::index_sequence_for{}); + view ? pick_and_each(func, std::index_sequence_for{}) : void(); } /** @@ -39567,14 +42764,16 @@ class basic_view, exclude_t> { */ template [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { - return std::make_from_tuple, exclude_t>>( - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, filter, other.filter))); + return internal::view_pack( + std::tuple_cat(pools, other.pools), + std::tuple_cat(internal::filter_as_tuple(filter), internal::filter_as_tuple(other.filter)), + std::index_sequence_for{}); } private: std::tuple pools; - std::tuple filter; - const base_type *view; + std::array filter; + const common_type *view; }; /** @@ -39593,13 +42792,13 @@ class basic_view, exclude_t> { * or removed from it). * * The entity currently pointed is destroyed. * - * In all other cases, modifying the pool iterated by the view in any way - * invalidates all the iterators and using them results in undefined behavior. + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. * * @tparam Get Type of storage iterated by the view. */ template -class basic_view, exclude_t<>, std::void_t::in_place_delete>>> { +class basic_view, exclude_t<>, std::void_t>> { template friend class basic_view; @@ -39609,62 +42808,81 @@ class basic_view, exclude_t<>, std::void_t().each()); /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : pools{}, - filter{} {} + filter{}, + view{} {} /** * @brief Constructs a single-type view from a storage class. - * @param ref The storage for the type to iterate. + * @param value The storage for the type to iterate. */ - basic_view(Get &ref) noexcept - : pools{&ref}, - filter{} {} + basic_view(Get &value) noexcept + : pools{&value}, + filter{}, + view{&value} {} /** * @brief Constructs a single-type view from a storage class. - * @param ref The storage for the type to iterate. + * @param value The storage for the type to iterate. */ - basic_view(std::tuple ref, std::tuple<> = {}) noexcept - : pools{&std::get<0>(ref)}, - filter{} {} + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} /** - * @brief Returns the leading storage of a view. + * @brief Returns the leading storage of a view, if any. * @return The leading storage of the view. */ - [[nodiscard]] const base_type &handle() const noexcept { - return storage(); + [[nodiscard]] const common_type *handle() const noexcept { + return view; } /** - * @brief Returns the storage for a given component type. + * @brief Returns the storage for a given component type, if any. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { + [[nodiscard]] auto *storage() const noexcept { static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); return storage<0>(); } /** - * @brief Returns the storage for a given index. + * @brief Returns the storage for a given index, if any. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template - [[nodiscard]] decltype(auto) storage() const noexcept { - return *std::get(pools); + [[nodiscard]] auto *storage() const noexcept { + return std::get(pools); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + view = std::get(pools) = &elem; } /** @@ -39672,7 +42890,7 @@ class basic_view, exclude_t<>, std::void_tsize() : size_type{}; } /** @@ -39680,59 +42898,47 @@ class basic_view, exclude_t<>, std::void_tempty(); } /** * @brief Returns an iterator to the first entity of the view. * - * The returned iterator points to the first entity of the view. If the view - * is empty, the returned iterator will be equal to `end()`. + * If the view is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const noexcept { - return handle().begin(); + return view ? view->begin() : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the view. - * - * The returned iterator points to the entity following the last entity of - * the view. Attempting to dereference the returned iterator results in - * undefined behavior. - * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { - return handle().end(); + return view ? view->end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed view. * - * The returned iterator points to the first entity of the reversed view. If - * the view is empty, the returned iterator will be equal to `rend()`. + * If the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { - return handle().rbegin(); + return view ? view->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * view. - * - * The returned iterator points to the entity following the last entity of - * the reversed view. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const noexcept { - return handle().rend(); + return view ? view->rend() : reverse_iterator{}; } /** @@ -39741,7 +42947,7 @@ class basic_view, exclude_t<>, std::void_tempty()) ? null : *view->begin(); } /** @@ -39750,7 +42956,7 @@ class basic_view, exclude_t<>, std::void_tempty()) ? null : *view->rbegin(); } /** @@ -39760,7 +42966,7 @@ class basic_view, exclude_t<>, std::void_tfind(entt) : iterator{}; } /** @@ -39778,15 +42984,15 @@ class basic_view, exclude_t<>, std::void_t(pools)->get(entt); } /** - * @brief Checks if a view is properly initialized. - * @return True if the view is properly initialized, false otherwise. + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { - return std::get<0>(pools) != nullptr; + return (std::get<0>(pools) != nullptr); } /** @@ -39795,7 +43001,7 @@ class basic_view, exclude_t<>, std::void_tcontains(entt); } /** @@ -39805,24 +43011,24 @@ class basic_view, exclude_t<>, std::void_t + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - if constexpr(sizeof...(Type) == 0) { - return storage().get_as_tuple(entt); - } else { - static_assert((std::is_same_v, typename Get::value_type> && ...), "Invalid component type"); - return storage().get(entt); - } + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return get<0>(entt); } /*! @copydoc get */ - template + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - return storage().get(entt); + if constexpr(sizeof...(Elem) == 0) { + return std::get<0>(pools)->get_as_tuple(entt); + } else { + return std::get(pools)->get(entt); + } } /** @@ -39849,17 +43055,19 @@ class basic_view, exclude_t<>, std::void_t void each(Func func) const { - if constexpr(is_applicable_v) { - for(const auto pack: each()) { - std::apply(func, pack); - } - } else if constexpr(ignore_as_empty_v) { - for(size_type pos{}, last = size(); pos < last; ++pos) { - func(); - } - } else { - for(auto &&component: storage()) { - func(component); + if(view) { + if constexpr(is_applicable_v) { + for(const auto pack: each()) { + std::apply(func, pack); + } + } else if constexpr(Get::traits_type::page_size == 0u) { + for(size_type pos{}, last = size(); pos < last; ++pos) { + func(); + } + } else { + for(auto &&component: *std::get<0>(pools)) { + func(component); + } } } } @@ -39874,7 +43082,7 @@ class basic_view, exclude_t<>, std::void_t(pools)->each() : iterable{}; } /** @@ -39886,13 +43094,16 @@ class basic_view, exclude_t<>, std::void_t [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { - return std::make_from_tuple, exclude_t>>( - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, other.filter))); + return internal::view_pack( + std::tuple_cat(pools, other.pools), + internal::filter_as_tuple(other.filter), + std::index_sequence_for{}); } private: std::tuple pools; - std::tuple<> filter; + std::array filter; + const common_type *view; }; /** @@ -39944,8 +43155,8 @@ basic_view(std::tuple, std::tuple = {}) -> basic_view, std::tuple = {}) -> basic_view out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; - const auto to = vertex * vert + vert; + const auto to = from + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } @@ -40645,7 +43860,7 @@ class adjacency_matrix { [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; - const auto to = vert * (vert - 1u) + vertex; + const auto to = vert * vert + from; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } @@ -40830,8 +44045,8 @@ void dot(std::ostream &out, const Graph &graph) { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -40887,6 +44102,8 @@ void dot(std::ostream &out, const Graph &graph) { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -40931,6 +44148,7 @@ void dot(std::ostream &out, const Graph &graph) { #include #include +#include #include #include // #include "../config/config.h" @@ -40952,8 +44170,8 @@ void dot(std::ostream &out, const Graph &graph) { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -41009,6 +44227,8 @@ void dot(std::ostream &out, const Graph &graph) { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -41110,7 +44330,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -41352,7 +44571,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -41440,10 +44660,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -41452,6 +44682,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -41503,6 +44785,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -41623,7 +44988,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -41714,6 +45079,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -41810,6 +45179,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -42393,7 +45774,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -42414,7 +45795,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -42581,6 +45962,7 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #include #include +#include #include #include // #include "../config/config.h" @@ -42635,7 +46017,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -42877,7 +46258,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -42965,10 +46347,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -42977,6 +46369,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -43028,6 +46472,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -43148,7 +46675,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -43239,6 +46766,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -43335,6 +46866,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -43476,51 +47019,51 @@ class dense_map_iterator final { return {it->element.first, it->element.second}; } - template - friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -43578,13 +47121,13 @@ class dense_map_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -43614,14 +47157,14 @@ class dense_map { static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { - return fast_mod(sparse.second()(key), bucket_count()); + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template @@ -43812,7 +47355,6 @@ class dense_map { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -43833,11 +47375,6 @@ class dense_map { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -44186,7 +47723,7 @@ class dense_map { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); @@ -44511,51 +48048,51 @@ class dense_set_iterator final { return *operator->(); } - template - friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -44610,13 +48147,13 @@ class dense_set_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -44652,7 +48189,7 @@ class dense_set { template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { - return fast_mod(sparse.second()(value), bucket_count()); + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template @@ -44825,7 +48362,6 @@ class dense_set { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -44846,11 +48382,6 @@ class dense_set { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -45106,7 +48637,7 @@ class dense_set { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); @@ -45323,6 +48854,7 @@ class dense_set { #include #include +#include #include #include // #include "../config/config.h" @@ -45398,7 +48930,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -45640,7 +49171,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -45728,10 +49260,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -45740,6 +49282,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -45791,6 +49385,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -45911,7 +49588,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -46002,6 +49679,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -46098,6 +49779,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -46396,7 +50089,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -46429,7 +50122,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -46438,14 +50131,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -46460,13 +50153,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -46743,7 +50436,7 @@ class adjacency_matrix { [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; - const auto to = vertex * vert + vert; + const auto to = from + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } @@ -46755,7 +50448,7 @@ class adjacency_matrix { [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; - const auto to = vert * (vert - 1u) + vertex; + const auto to = vert * vert + from; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } @@ -46848,6 +50541,7 @@ class basic_flow { using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; void emplace(const id_type res, const bool is_rw) { ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); @@ -46859,6 +50553,76 @@ class basic_flow { deps[res].emplace_back(index.first(), is_rw); } + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + public: /*! @brief Allocator type. */ using allocator_type = Allocator; @@ -46866,6 +50630,8 @@ class basic_flow { using size_type = std::size_t; /*! @brief Iterable task list. */ using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; /*! @brief Default constructor. */ basic_flow() @@ -46940,9 +50706,10 @@ class basic_flow { /*! @brief Clears the flow builder. */ void clear() noexcept { - index.first() = sync_on = {}; + index.first() = {}; vertices.clear(); deps.clear(); + sync_on = {}; } /** @@ -47061,72 +50828,12 @@ class basic_flow { * @brief Generates a task graph for the current content. * @return The adjacency matrix of the task graph. */ - [[nodiscard]] adjacency_matrix graph() const { - const auto length = vertices.size(); - adjacency_matrix matrix{length}; - - // creates the adjacency matrix - for(const auto &elem: deps) { - const auto last = elem.second.cend(); - auto it = elem.second.cbegin(); + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; - while(it != last) { - if(it->second) { - // rw item - if(auto curr = it++; it != last) { - if(it->second) { - matrix.insert(curr->first, it->first); - } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { - for(; it != next; ++it) { - matrix.insert(curr->first, it->first); - matrix.insert(it->first, next->first); - } - } else { - for(; it != next; ++it) { - matrix.insert(curr->first, it->first); - } - } - } - } else { - // ro item (first iteration only) - if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { - for(; it != next; ++it) { - matrix.insert(it->first, next->first); - } - } else { - it = last; - } - } - } - } - - // computes the transitive closure - for(std::size_t vk{}; vk < length; ++vk) { - for(std::size_t vi{}; vi < length; ++vi) { - for(std::size_t vj{}; vj < length; ++vj) { - if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { - matrix.insert(vi, vj); - } - } - } - } - - // applies the transitive reduction - for(std::size_t vert{}; vert < length; ++vert) { - matrix.erase(vert, vert); - } - - for(std::size_t vj{}; vj < length; ++vj) { - for(std::size_t vi{}; vi < length; ++vi) { - if(matrix.contains(vi, vj)) { - for(std::size_t vk{}; vk < length; ++vk) { - if(matrix.contains(vj, vk)) { - matrix.erase(vi, vk); - } - } - } - } - } + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); return matrix; } @@ -47167,8 +50874,8 @@ class basic_flow { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -47224,6 +50931,8 @@ class basic_flow { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -47320,40 +51029,40 @@ class locator final { * cases, they are discarded. * * @tparam Args Types of arguments to use to construct the fallback service. - * @tparam Impl Fallback service type. + * @tparam Type Fallback service type. * @param args Parameters to use to construct the fallback service. * @return A reference to a valid service. */ - template + template [[nodiscard]] static Service &value_or(Args &&...args) { - return service ? *service : emplace(std::forward(args)...); + return service ? *service : emplace(std::forward(args)...); } /** * @brief Sets or replaces a service. - * @tparam Impl Service type. + * @tparam Type Service type. * @tparam Args Types of arguments to use to construct the service. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ - template + template static Service &emplace(Args &&...args) { - service = std::make_shared(std::forward(args)...); + service = std::make_shared(std::forward(args)...); return *service; } /** * @brief Sets or replaces a service using a given allocator. - * @tparam Impl Service type. + * @tparam Type Service type. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the service. * @param alloc The allocator to use. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ - template - static Service &allocate_emplace(Allocator alloc, Args &&...args) { - service = std::allocate_shared(alloc, std::forward(args)...); + template + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); return *service; } @@ -47375,6 +51084,18 @@ class locator final { service = other.value; } + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + private: // std::shared_ptr because of its type erased allocator which is useful here inline static std::shared_ptr service{}; @@ -47468,8 +51189,8 @@ struct adl_meta_pointer_like { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -47525,6 +51246,8 @@ struct adl_meta_pointer_like { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -47569,6 +51292,7 @@ struct adl_meta_pointer_like { #include #include +#include #include #include // #include "../config/config.h" @@ -47590,8 +51314,8 @@ struct adl_meta_pointer_like { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -47647,6 +51371,8 @@ struct adl_meta_pointer_like { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -47748,7 +51474,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -47990,7 +51715,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -48078,10 +51804,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -48090,6 +51826,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -48141,6 +51929,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -48261,7 +52132,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -48352,6 +52223,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -48448,6 +52323,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -49031,7 +52918,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -49052,7 +52939,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -49219,6 +53106,7 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #include #include +#include #include #include // #include "../config/config.h" @@ -49273,7 +53161,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -49515,7 +53402,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -49603,10 +53491,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -49615,6 +53513,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -49666,6 +53616,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -49786,7 +53819,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -49877,6 +53910,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -49973,6 +54010,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -50114,51 +54163,51 @@ class dense_map_iterator final { return {it->element.first, it->element.second}; } - template - friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -50216,13 +54265,13 @@ class dense_map_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -50252,14 +54301,14 @@ class dense_map { static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { - return fast_mod(sparse.second()(key), bucket_count()); + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template @@ -50450,7 +54499,6 @@ class dense_map { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -50471,11 +54519,6 @@ class dense_map { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -50824,7 +54867,7 @@ class dense_map { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); @@ -51149,51 +55192,51 @@ class dense_set_iterator final { return *operator->(); } - template - friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -51248,13 +55291,13 @@ class dense_set_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -51290,7 +55333,7 @@ class dense_set { template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { - return fast_mod(sparse.second()(value), bucket_count()); + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template @@ -51463,7 +55506,6 @@ class dense_set { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -51484,11 +55526,6 @@ class dense_set { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -51744,7 +55781,7 @@ class dense_set { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); @@ -51977,8 +56014,8 @@ class dense_set { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -52034,6 +56071,8 @@ class dense_set { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -52100,7 +56139,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -52133,7 +56172,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -52142,14 +56181,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -52164,13 +56203,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -52200,8 +56239,8 @@ struct meta_type_node; struct meta_context { dense_map value{}; - static inline meta_context &from(meta_ctx &ctx); - static inline const meta_context &from(const meta_ctx &ctx); + [[nodiscard]] static inline meta_context &from(meta_ctx &ctx); + [[nodiscard]] static inline const meta_context &from(const meta_ctx &ctx); }; } // namespace internal @@ -52228,11 +56267,11 @@ class meta_ctx: private internal::meta_context { * Internal details not to be documented. */ -inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { return ctx; } -inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { return ctx; } @@ -52273,8 +56312,8 @@ inline const internal::meta_context &internal::meta_context::from(const meta_ctx #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -52330,6 +56369,8 @@ inline const internal::meta_context &internal::meta_context::from(const meta_ctx # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -52390,7 +56431,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -52423,7 +56464,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -52432,14 +56473,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -52454,13 +56495,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -52610,7 +56651,7 @@ struct basic_hashed_string { template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; - using hs_traits = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -52622,10 +56663,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { - base_type base{str, 0u, hs_traits::offset}; + base_type base{str, 0u, traits_type::offset}; for(; str[base.length]; ++base.length) { - base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; } return base; @@ -52633,10 +56674,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { - base_type base{str, len, hs_traits::offset}; + base_type base{str, len, traits_type::offset}; for(size_type pos{}; pos < len; ++pos) { - base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; } return base; @@ -53147,6 +57188,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -53201,7 +57243,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -53443,7 +57484,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -53531,10 +57573,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -53543,6 +57595,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -53594,6 +57698,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -53714,7 +57901,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -53805,6 +57992,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -53901,6 +58092,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -53952,7 +58155,7 @@ class basic_any { }; template - static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { @@ -54021,17 +58224,17 @@ class basic_any { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { - static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); @@ -54321,7 +58524,7 @@ class basic_any { * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) noexcept { +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -54329,7 +58532,7 @@ Type any_cast(const basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) noexcept { +[[nodiscard]] Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); @@ -54338,7 +58541,7 @@ Type any_cast(basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) noexcept { +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); @@ -54354,14 +58557,14 @@ Type any_cast(basic_any &&data) noexcept { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) noexcept { +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template -Type *any_cast(basic_any *data) noexcept { +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); @@ -54381,7 +58584,7 @@ Type *any_cast(basic_any *data) noexcept { * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> -basic_any make_any(Args &&...args) { +[[nodiscard]] basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } @@ -54394,7 +58597,7 @@ basic_any make_any(Args &&...args) { * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> -basic_any forward_as_any(Type &&value) { +[[nodiscard]] basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } @@ -54889,6 +59092,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -54943,7 +59147,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -55185,7 +59388,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -55273,10 +59477,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -55285,6 +59499,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -55336,6 +59602,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -55456,7 +59805,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -55547,6 +59896,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -55643,6 +59996,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "../core/utility.hpp" @@ -55672,8 +60037,8 @@ using nth_argument_t = typename nth_argument::type; #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -55729,6 +60094,8 @@ using nth_argument_t = typename nth_argument::type; # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -55825,40 +60192,40 @@ class locator final { * cases, they are discarded. * * @tparam Args Types of arguments to use to construct the fallback service. - * @tparam Impl Fallback service type. + * @tparam Type Fallback service type. * @param args Parameters to use to construct the fallback service. * @return A reference to a valid service. */ - template + template [[nodiscard]] static Service &value_or(Args &&...args) { - return service ? *service : emplace(std::forward(args)...); + return service ? *service : emplace(std::forward(args)...); } /** * @brief Sets or replaces a service. - * @tparam Impl Service type. + * @tparam Type Service type. * @tparam Args Types of arguments to use to construct the service. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ - template + template static Service &emplace(Args &&...args) { - service = std::make_shared(std::forward(args)...); + service = std::make_shared(std::forward(args)...); return *service; } /** * @brief Sets or replaces a service using a given allocator. - * @tparam Impl Service type. + * @tparam Type Service type. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the service. * @param alloc The allocator to use. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ - template - static Service &allocate_emplace(Allocator alloc, Args &&...args) { - service = std::allocate_shared(alloc, std::forward(args)...); + template + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); return *service; } @@ -55880,6 +60247,18 @@ class locator final { service = other.value; } + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + private: // std::shared_ptr because of its type erased allocator which is useful here inline static std::shared_ptr service{}; @@ -56140,7 +60519,6 @@ struct meta_associative_container_traits; /** * @brief Provides the member constant `value` to true if a given type is a * pointer-like type from the point of view of the meta system, false otherwise. - * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like: std::false_type {}; @@ -56285,7 +60663,7 @@ meta_type_node resolve(const meta_context &) noexcept; template [[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { - std::size_t pos{}; + [[maybe_unused]] std::size_t pos{}; meta_type_node (*value)(const meta_context &) noexcept = nullptr; ((value = (pos++ == index ? &resolve>> : value)), ...); ENTT_ASSERT(value != nullptr, "Out of bounds"); @@ -56415,19 +60793,19 @@ struct meta_range_iterator final { using reference = value_type; using iterator_category = std::input_iterator_tag; - meta_range_iterator() noexcept + constexpr meta_range_iterator() noexcept : it{}, ctx{} {} - meta_range_iterator(const meta_ctx &area, const It iter) noexcept + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept : it{iter}, ctx{&area} {} - meta_range_iterator &operator++() noexcept { + constexpr meta_range_iterator &operator++() noexcept { return ++it, *this; } - meta_range_iterator operator++(int) noexcept { + constexpr meta_range_iterator operator++(int) noexcept { meta_range_iterator orig = *this; return ++(*this), orig; } @@ -57134,7 +61512,7 @@ class meta_any { * @return A properly initialized and not necessarily owning wrapper. */ template -meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { return meta_any{ctx, std::in_place_type, std::forward(value)}; } @@ -57145,7 +61523,7 @@ meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { * @return A properly initialized and not necessarily owning wrapper. */ template -meta_any forward_as_meta(Type &&value) { +[[nodiscard]] meta_any forward_as_meta(Type &&value) { return forward_as_meta(locator::value_or(), std::forward(value)); } @@ -57243,6 +61621,16 @@ struct meta_handle { return static_cast(any); } + /*! @copydoc meta_any::operator== */ + [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { + return (any == other.any); + } + + /*! @copydoc meta_any::operator!= */ + [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { + return !(*this == other); + } + /** * @brief Access operator for accessing the contained opaque object. * @return A wrapper that shares a reference to an unmanaged object. @@ -57277,13 +61665,21 @@ struct meta_prop { ctx{&area} {} /** - * @brief Returns the stored value by copy. + * @brief Returns the stored value by const reference. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; } + /** + * @brief Returns the stored value by reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. @@ -57292,11 +61688,30 @@ struct meta_prop { return (node != nullptr); } + /** + * @brief Checks if two objects refer to the same type. + * @param other The object with which to compare. + * @return True if the objects refer to the same type, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_prop &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + private: const internal::meta_prop_node *node; const meta_ctx *ctx; }; +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { + return !(lhs == rhs); +} + /*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Unsigned integer type. */ @@ -57397,11 +61812,26 @@ struct meta_data { return (node != nullptr); } + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_data &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + private: const internal::meta_data_node *node; const meta_ctx *ctx; }; +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + /*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Unsigned integer type. */ @@ -57483,8 +61913,12 @@ struct meta_func { */ template meta_any invoke(meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; - return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); + if constexpr(sizeof...(Args) == 0u) { + return invoke(std::move(instance), static_cast(nullptr), size_type{}); + } else { + meta_any arguments[sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } } /*! @copydoc meta_data::prop */ @@ -57518,11 +61952,26 @@ struct meta_func { return (node != nullptr); } + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + private: const internal::meta_func_node *node; const meta_ctx *ctx; }; +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + /*! @brief Opaque wrapper for types. */ class meta_type { template @@ -57860,8 +62309,12 @@ class meta_type { */ template [[nodiscard]] meta_any construct(Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; - return construct(arguments, sizeof...(Args)); + if constexpr(sizeof...(Args) == 0u) { + return construct(static_cast(nullptr), size_type{}); + } else { + meta_any arguments[sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return construct(arguments, sizeof...(Args)); + } } /** @@ -57869,12 +62322,12 @@ class meta_type { * @param element A valid pointer to an element of the underlying type. * @return A wrapper that references the given instance. */ - meta_any from_void(void *element) const { + [[nodiscard]] meta_any from_void(void *element) const { return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; } /*! @copydoc from_void */ - meta_any from_void(const void *element) const { + [[nodiscard]] meta_any from_void(const void *element) const { return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; } @@ -57894,7 +62347,7 @@ class meta_type { meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { if(node.details) { if(auto it = node.details->func.find(id); it != node.details->func.cend()) { - if(const auto *candidate = lookup(args, sz, (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); } } @@ -57920,8 +62373,12 @@ class meta_type { */ template meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; - return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); + if constexpr(sizeof...(Args) == 0u) { + return invoke(id, std::move(instance), static_cast(nullptr), size_type{}); + } else { + meta_any arguments[sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } } /** @@ -57987,11 +62444,7 @@ class meta_type { return !(ctx == nullptr); } - /** - * @brief Checks if two objects refer to the same type. - * @param other The object with which to compare. - * @return True if the objects refer to the same type, false otherwise. - */ + /*! @copydoc meta_prop::operator== */ [[nodiscard]] bool operator==(const meta_type &other) const noexcept { return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); } @@ -58143,7 +62596,7 @@ class meta_sequence_container::meta_iterator final { explicit meta_iterator(const meta_ctx &area, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, - handle{std::move(iter)} {} + handle{iter} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, 1, nullptr); @@ -58237,7 +62690,7 @@ class meta_associative_container::meta_iterator final { meta_iterator(const meta_ctx &area, std::integral_constant, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, - handle{std::move(iter)} {} + handle{iter} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, nullptr); @@ -58347,7 +62800,7 @@ inline meta_sequence_container::iterator meta_sequence_container::insert(iterato * @return A possibly invalid iterator following the last removed element. */ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { - return insert(std::move(it), {}); + return insert(it, {}); } /** @@ -58728,8 +63181,8 @@ struct meta_type_node; struct meta_context { dense_map value{}; - static inline meta_context &from(meta_ctx &ctx); - static inline const meta_context &from(const meta_ctx &ctx); + [[nodiscard]] static inline meta_context &from(meta_ctx &ctx); + [[nodiscard]] static inline const meta_context &from(const meta_ctx &ctx); }; } // namespace internal @@ -58756,11 +63209,11 @@ class meta_ctx: private internal::meta_context { * Internal details not to be documented. */ -inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { return ctx; } -inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { return ctx; } @@ -58870,11 +63323,7 @@ struct as_void_t final { */ template struct is_meta_policy - : std::disjunction< - std::is_same, - std::is_same, - std::is_same, - std::is_same> {}; + : std::bool_constant || std::is_same_v || std::is_same_v || std::is_same_v> {}; /** * @brief Helper variable template. @@ -59099,9 +63548,12 @@ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, - std::conditional_t>, Type>, type_list, type_list>, - !std::is_base_of_v>, Type>, - std::is_base_of_v>, Type> && std::is_const_v>> {}; + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || std::is_base_of_v>, Type>)> {}; /** * @brief Meta function descriptor. @@ -59169,7 +63621,7 @@ using meta_function_helper_t = typename meta_function_helper::t * @return A meta any containing the returned value, if any. */ template -std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type}; } else if constexpr(std::is_same_v) { @@ -59190,7 +63642,7 @@ std::enable_if_t, meta_any> meta_dispatch(const meta_ct * @return A meta any containing the returned value, if any. */ template -std::enable_if_t, meta_any> meta_dispatch(Type &&value) { +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { return meta_dispatch(locator::value_or(), std::forward(value)); } @@ -59555,28 +64007,12 @@ namespace entt { namespace internal { -inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) { +[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) { auto &&context = internal::meta_context::from(ctx); ENTT_ASSERT(context.value.contains(info.hash()), "Type not available"); return context.value[info.hash()]; } -inline meta_base_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_base_node node) { - return parent.details->base.insert_or_assign(id, std::move(node)).first->second; -} - -inline meta_conv_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_conv_node node) { - return parent.details->conv.insert_or_assign(id, std::move(node)).first->second; -} - -inline meta_ctor_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_ctor_node node) { - return parent.details->ctor.insert_or_assign(id, std::move(node)).first->second; -} - -inline meta_dtor_node &meta_extend(internal::meta_type_node &parent, meta_dtor_node node) { - return (parent.dtor = std::move(node)); -} - inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) { return parent.details->data.insert_or_assign(id, std::move(node)).first->second; } @@ -59598,10 +64034,6 @@ inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_ty return parent.details->func.insert_or_assign(id, std::move(node)).first->second; } -inline meta_prop_node &meta_extend(dense_map &prop, const id_type id, meta_prop_node node) { - return (prop[id] = std::move(node)); -} - } // namespace internal /** @@ -59682,16 +64114,8 @@ class meta_factory { template auto base() noexcept { static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); - - internal::meta_extend( - internal::owner(*ctx, *info), - type_id().hash(), - internal::meta_base_node{ - &internal::resolve, - +[](const void *instance) noexcept { - return static_cast(static_cast(static_cast(instance))); - }}); - + auto *const op = +[](const void *instance) noexcept { return static_cast(static_cast(static_cast(instance))); }; + internal::owner(*ctx, *info).details->base.insert_or_assign(type_id().hash(), internal::meta_base_node{&internal::resolve, op}); bucket = nullptr; return *this; } @@ -59711,15 +64135,8 @@ class meta_factory { template auto conv() noexcept { using conv_type = std::remove_cv_t>>; - - internal::meta_extend( - internal::owner(*ctx, *info), - type_id().hash(), - internal::meta_conv_node{ - +[](const meta_ctx &area, const void *instance) { - return forward_as_meta(area, std::invoke(Candidate, *static_cast(instance))); - }}); - + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast(instance))); }; + internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id().hash(), internal::meta_conv_node{op}); bucket = nullptr; return *this; } @@ -59736,15 +64153,8 @@ class meta_factory { template auto conv() noexcept { using conv_type = std::remove_cv_t>; - - internal::meta_extend( - internal::owner(*ctx, *info), - type_id().hash(), - internal::meta_conv_node{ - +[](const meta_ctx &area, const void *instance) { - return forward_as_meta(area, static_cast(*static_cast(instance))); - }}); - + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast(*static_cast(instance))); }; + internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id().hash(), internal::meta_conv_node{op}); bucket = nullptr; return *this; } @@ -59767,15 +64177,7 @@ class meta_factory { using descriptor = meta_function_helper_t; static_assert(Policy::template value, "Invalid return type for the given policy"); static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); - - internal::meta_extend( - internal::owner(*ctx, *info), - type_id().hash(), - internal::meta_ctor_node{ - descriptor::args_type::size, - &meta_arg, - &meta_construct}); - + internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg, &meta_construct}); bucket = nullptr; return *this; } @@ -59795,14 +64197,7 @@ class meta_factory { // default constructor is already implicitly generated, no need for redundancy if constexpr(sizeof...(Args) != 0u) { using descriptor = meta_function_helper_t; - - internal::meta_extend( - internal::owner(*ctx, *info), - type_id().hash(), - internal::meta_ctor_node{ - descriptor::args_type::size, - &meta_arg, - &meta_construct}); + internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg, &meta_construct}); } bucket = nullptr; @@ -59830,12 +64225,8 @@ class meta_factory { template auto dtor() noexcept { static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); - - internal::meta_extend( - internal::owner(*ctx, *info), - internal::meta_dtor_node{ - +[](void *instance) { std::invoke(Func, *static_cast(instance)); }}); - + auto *const op = +[](void *instance) { std::invoke(Func, *static_cast(instance)); }; + internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op}; bucket = nullptr; return *this; } @@ -59856,32 +64247,39 @@ class meta_factory { template auto data(const id_type id) noexcept { if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_reference_t>; + using data_type = std::invoke_result_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_data_node{ /* this is never static */ - std::is_const_v ? internal::meta_traits::is_const : internal::meta_traits::is_none, + std::is_const_v> ? internal::meta_traits::is_const : internal::meta_traits::is_none, 1u, - &internal::resolve>, - &meta_arg>>, + &internal::resolve>>, + &meta_arg>>>, &meta_setter, &meta_getter}); bucket = &elem.prop; } else { - using data_type = std::remove_reference_t>; + using data_type = std::remove_pointer_t; + + if constexpr(std::is_pointer_v) { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } else { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_data_node{ - ((std::is_same_v> || std::is_const_v) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, + ((std::is_same_v>> || std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, 1u, - &internal::resolve>, - &meta_arg>>, + &internal::resolve>>, + &meta_arg>>>, &meta_setter, &meta_getter}); @@ -60021,18 +64419,11 @@ class meta_factory { ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties"); if constexpr(sizeof...(Value) == 0u) { - internal::meta_extend( - *bucket, - id, - internal::meta_prop_node{ - &internal::resolve}); + (*bucket)[id] = internal::meta_prop_node{&internal::resolve}; } else { - internal::meta_extend( - *bucket, - id, - internal::meta_prop_node{ - &internal::resolve>..., - std::make_shared>(std::forward(value))...}); + (*bucket)[id] = internal::meta_prop_node{ + &internal::resolve>..., + std::make_shared>(std::forward(value))...}; } return *this; @@ -60798,7 +65189,7 @@ class meta_any { * @return A properly initialized and not necessarily owning wrapper. */ template -meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { return meta_any{ctx, std::in_place_type, std::forward(value)}; } @@ -60809,7 +65200,7 @@ meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { * @return A properly initialized and not necessarily owning wrapper. */ template -meta_any forward_as_meta(Type &&value) { +[[nodiscard]] meta_any forward_as_meta(Type &&value) { return forward_as_meta(locator::value_or(), std::forward(value)); } @@ -60907,6 +65298,16 @@ struct meta_handle { return static_cast(any); } + /*! @copydoc meta_any::operator== */ + [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { + return (any == other.any); + } + + /*! @copydoc meta_any::operator!= */ + [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { + return !(*this == other); + } + /** * @brief Access operator for accessing the contained opaque object. * @return A wrapper that shares a reference to an unmanaged object. @@ -60941,13 +65342,21 @@ struct meta_prop { ctx{&area} {} /** - * @brief Returns the stored value by copy. + * @brief Returns the stored value by const reference. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; } + /** + * @brief Returns the stored value by reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. @@ -60956,11 +65365,30 @@ struct meta_prop { return (node != nullptr); } + /** + * @brief Checks if two objects refer to the same type. + * @param other The object with which to compare. + * @return True if the objects refer to the same type, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_prop &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + private: const internal::meta_prop_node *node; const meta_ctx *ctx; }; +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { + return !(lhs == rhs); +} + /*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Unsigned integer type. */ @@ -61061,11 +65489,26 @@ struct meta_data { return (node != nullptr); } + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_data &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + private: const internal::meta_data_node *node; const meta_ctx *ctx; }; +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + /*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Unsigned integer type. */ @@ -61147,8 +65590,12 @@ struct meta_func { */ template meta_any invoke(meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; - return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); + if constexpr(sizeof...(Args) == 0u) { + return invoke(std::move(instance), static_cast(nullptr), size_type{}); + } else { + meta_any arguments[sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } } /*! @copydoc meta_data::prop */ @@ -61182,11 +65629,26 @@ struct meta_func { return (node != nullptr); } + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + private: const internal::meta_func_node *node; const meta_ctx *ctx; }; +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + /*! @brief Opaque wrapper for types. */ class meta_type { template @@ -61524,8 +65986,12 @@ class meta_type { */ template [[nodiscard]] meta_any construct(Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; - return construct(arguments, sizeof...(Args)); + if constexpr(sizeof...(Args) == 0u) { + return construct(static_cast(nullptr), size_type{}); + } else { + meta_any arguments[sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return construct(arguments, sizeof...(Args)); + } } /** @@ -61533,12 +65999,12 @@ class meta_type { * @param element A valid pointer to an element of the underlying type. * @return A wrapper that references the given instance. */ - meta_any from_void(void *element) const { + [[nodiscard]] meta_any from_void(void *element) const { return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; } /*! @copydoc from_void */ - meta_any from_void(const void *element) const { + [[nodiscard]] meta_any from_void(const void *element) const { return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; } @@ -61558,7 +66024,7 @@ class meta_type { meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { if(node.details) { if(auto it = node.details->func.find(id); it != node.details->func.cend()) { - if(const auto *candidate = lookup(args, sz, (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); } } @@ -61584,8 +66050,12 @@ class meta_type { */ template meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; - return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); + if constexpr(sizeof...(Args) == 0u) { + return invoke(id, std::move(instance), static_cast(nullptr), size_type{}); + } else { + meta_any arguments[sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } } /** @@ -61651,11 +66121,7 @@ class meta_type { return !(ctx == nullptr); } - /** - * @brief Checks if two objects refer to the same type. - * @param other The object with which to compare. - * @return True if the objects refer to the same type, false otherwise. - */ + /*! @copydoc meta_prop::operator== */ [[nodiscard]] bool operator==(const meta_type &other) const noexcept { return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); } @@ -61807,7 +66273,7 @@ class meta_sequence_container::meta_iterator final { explicit meta_iterator(const meta_ctx &area, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, - handle{std::move(iter)} {} + handle{iter} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, 1, nullptr); @@ -61901,7 +66367,7 @@ class meta_associative_container::meta_iterator final { meta_iterator(const meta_ctx &area, std::integral_constant, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, - handle{std::move(iter)} {} + handle{iter} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, nullptr); @@ -62011,7 +66477,7 @@ inline meta_sequence_container::iterator meta_sequence_container::insert(iterato * @return A possibly invalid iterator following the last removed element. */ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { - return insert(std::move(it), {}); + return insert(it, {}); } /** @@ -62282,7 +66748,7 @@ meta_type_node resolve(const meta_context &) noexcept; template [[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { - std::size_t pos{}; + [[maybe_unused]] std::size_t pos{}; meta_type_node (*value)(const meta_context &) noexcept = nullptr; ((value = (pos++ == index ? &resolve>> : value)), ...); ENTT_ASSERT(value != nullptr, "Out of bounds"); @@ -62503,11 +66969,7 @@ struct as_void_t final { */ template struct is_meta_policy - : std::disjunction< - std::is_same, - std::is_same, - std::is_same, - std::is_same> {}; + : std::bool_constant || std::is_same_v || std::is_same_v || std::is_same_v> {}; /** * @brief Helper variable template. @@ -62551,19 +67013,19 @@ struct meta_range_iterator final { using reference = value_type; using iterator_category = std::input_iterator_tag; - meta_range_iterator() noexcept + constexpr meta_range_iterator() noexcept : it{}, ctx{} {} - meta_range_iterator(const meta_ctx &area, const It iter) noexcept + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept : it{iter}, ctx{&area} {} - meta_range_iterator &operator++() noexcept { + constexpr meta_range_iterator &operator++() noexcept { return ++it, *this; } - meta_range_iterator operator++(int) noexcept { + constexpr meta_range_iterator operator++(int) noexcept { meta_range_iterator orig = *this; return ++(*this), orig; } @@ -62848,7 +67310,6 @@ struct meta_associative_container_traits; /** * @brief Provides the member constant `value` to true if a given type is a * pointer-like type from the point of view of the meta system, false otherwise. - * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like: std::false_type {}; @@ -62972,9 +67433,12 @@ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, - std::conditional_t>, Type>, type_list, type_list>, - !std::is_base_of_v>, Type>, - std::is_base_of_v>, Type> && std::is_const_v>> {}; + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || std::is_base_of_v>, Type>)> {}; /** * @brief Meta function descriptor. @@ -63042,7 +67506,7 @@ using meta_function_helper_t = typename meta_function_helper::t * @return A meta any containing the returned value, if any. */ template -std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type}; } else if constexpr(std::is_same_v) { @@ -63063,7 +67527,7 @@ std::enable_if_t, meta_any> meta_dispatch(const meta_ct * @return A meta any containing the returned value, if any. */ template -std::enable_if_t, meta_any> meta_dispatch(Type &&value) { +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { return meta_dispatch(locator::value_or(), std::forward(value)); } @@ -63523,8 +67987,8 @@ using invoke_result_t = typename std::invoke_result::type; #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -63580,6 +68044,8 @@ using invoke_result_t = typename std::invoke_result::type; # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -63630,7 +68096,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -63663,7 +68129,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -63672,14 +68138,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -63694,13 +68160,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -63850,7 +68316,7 @@ struct basic_hashed_string { template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; - using hs_traits = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -63862,10 +68328,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { - base_type base{str, 0u, hs_traits::offset}; + base_type base{str, 0u, traits_type::offset}; for(; str[base.length]; ++base.length) { - base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; } return base; @@ -63873,10 +68339,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { - base_type base{str, len, hs_traits::offset}; + base_type base{str, len, traits_type::offset}; for(size_type pos{}; pos < len; ++pos) { - base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; } return base; @@ -64387,6 +68853,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -64441,7 +68908,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -64683,7 +69149,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -64771,10 +69238,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -64783,6 +69260,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -64834,6 +69363,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -64954,7 +69566,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -65045,6 +69657,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -65141,6 +69757,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -65192,7 +69820,7 @@ class basic_any { }; template - static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { @@ -65261,17 +69889,17 @@ class basic_any { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { - static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); @@ -65561,7 +70189,7 @@ class basic_any { * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) noexcept { +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -65569,7 +70197,7 @@ Type any_cast(const basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) noexcept { +[[nodiscard]] Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); @@ -65578,7 +70206,7 @@ Type any_cast(basic_any &data) noexcept { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) noexcept { +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); @@ -65594,14 +70222,14 @@ Type any_cast(basic_any &&data) noexcept { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) noexcept { +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template -Type *any_cast(basic_any *data) noexcept { +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); @@ -65621,7 +70249,7 @@ Type *any_cast(basic_any *data) noexcept { * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> -basic_any make_any(Args &&...args) { +[[nodiscard]] basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } @@ -65634,7 +70262,7 @@ basic_any make_any(Args &&...args) { * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> -basic_any forward_as_any(Type &&value) { +[[nodiscard]] basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } @@ -65928,6 +70556,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -65982,7 +70611,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -66224,7 +70852,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -66312,10 +70941,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -66324,6 +70963,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -66375,6 +71066,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -66495,7 +71269,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -66586,6 +71360,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -66682,6 +71460,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -66715,7 +71505,7 @@ struct poly_inspector { * @brief Generic conversion operator (definition only). * @tparam Type Type to which conversion is requested. */ - template + template operator Type &&() const; /** @@ -67351,6 +72141,27 @@ struct process_adaptor: process, Delta>, private Fu #include #include #include +// #include "fwd.hpp" +#ifndef ENTT_PROCESS_FWD_HPP +#define ENTT_PROCESS_FWD_HPP + +#include + +namespace entt { + +template +class process; + +template +class basic_scheduler; + +/*! @brief Alias declaration for the most common use case. */ +using scheduler = basic_scheduler<>; + +} // namespace entt + +#endif + // #include "process.hpp" #ifndef ENTT_PROCESS_PROCESS_HPP #define ENTT_PROCESS_PROCESS_HPP @@ -67714,11 +72525,11 @@ namespace entt { * @tparam Delta Type to use to provide elapsed time. */ template -class scheduler { +class basic_scheduler { struct process_handler { using instance_type = std::unique_ptr; - using update_fn_type = bool(scheduler &, std::size_t, Delta, void *); - using abort_fn_type = void(scheduler &, std::size_t, bool); + using update_fn_type = bool(basic_scheduler &, std::size_t, Delta, void *); + using abort_fn_type = void(basic_scheduler &, std::size_t, bool); using next_type = std::unique_ptr; instance_type instance; @@ -67734,8 +72545,8 @@ class scheduler { template continuation then(Args &&...args) { static_assert(std::is_base_of_v, Proc>, "Invalid process type"); - auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; - handler->next.reset(new process_handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}); + auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &basic_scheduler::deleter}; + handler->next.reset(new process_handler{std::move(proc), &basic_scheduler::update, &basic_scheduler::abort, nullptr}); handler = handler->next.get(); return *this; } @@ -67750,7 +72561,7 @@ class scheduler { }; template - [[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) { + [[nodiscard]] static bool update(basic_scheduler &owner, std::size_t pos, const Delta delta, void *data) { auto *process = static_cast(owner.handlers[pos].instance.get()); process->tick(delta, data); @@ -67770,7 +72581,7 @@ class scheduler { } template - static void abort(scheduler &owner, std::size_t pos, const bool immediately) { + static void abort(basic_scheduler &owner, std::size_t pos, const bool immediately) { static_cast(owner.handlers[pos].instance.get())->abort(immediately); } @@ -67780,18 +72591,20 @@ class scheduler { } public: + /*! @brief Unsigned integer type. */ + using delta_type = Delta; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Default constructor. */ - scheduler() + basic_scheduler() : handlers{} {} /*! @brief Default move constructor. */ - scheduler(scheduler &&) = default; + basic_scheduler(basic_scheduler &&) = default; /*! @brief Default move assignment operator. @return This scheduler. */ - scheduler &operator=(scheduler &&) = default; + basic_scheduler &operator=(basic_scheduler &&) = default; /** * @brief Number of processes currently scheduled. @@ -67847,8 +72660,8 @@ class scheduler { template auto attach(Args &&...args) { static_assert(std::is_base_of_v, Proc>, "Invalid process type"); - auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; - auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}); + auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &basic_scheduler::deleter}; + auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &basic_scheduler::update, &basic_scheduler::abort, nullptr}); // forces the process to exit the uninitialized state ref.update(*this, handlers.size() - 1u, {}, nullptr); return continuation{&handlers.back()}; @@ -67922,7 +72735,7 @@ class scheduler { * @param delta Elapsed time. * @param data Optional data. */ - void update(const Delta delta, void *data = nullptr) { + void update(const delta_type delta, void *data = nullptr) { for(auto pos = handlers.size(); pos; --pos) { const auto curr = pos - 1u; @@ -68002,8 +72815,8 @@ class scheduler { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -68059,6 +72872,8 @@ class scheduler { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -68103,6 +72918,7 @@ class scheduler { #include #include +#include #include #include // #include "../config/config.h" @@ -68124,8 +72940,8 @@ class scheduler { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -68181,6 +72997,8 @@ class scheduler { # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -68282,7 +73100,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -68524,7 +73341,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -68612,10 +73430,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -68624,6 +73452,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -68675,6 +73555,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -68795,7 +73758,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -68886,6 +73849,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -68982,6 +73949,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -69565,7 +74544,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -69586,7 +74565,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -69753,6 +74732,7 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #include #include +#include #include #include // #include "../config/config.h" @@ -69807,7 +74787,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -70049,7 +75028,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -70137,10 +75117,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -70149,6 +75139,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -70200,6 +75242,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -70320,7 +75445,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -70411,6 +75536,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -70507,6 +75636,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -70648,51 +75789,51 @@ class dense_map_iterator final { return {it->element.first, it->element.second}; } - template - friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -70750,13 +75891,13 @@ class dense_map_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -70786,14 +75927,14 @@ class dense_map { static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { - return fast_mod(sparse.second()(key), bucket_count()); + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template @@ -70984,7 +76125,6 @@ class dense_map { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -71005,11 +76145,6 @@ class dense_map { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -71358,7 +76493,7 @@ class dense_map { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); @@ -71593,6 +76728,7 @@ struct uses_allocator, Allocator> #include #include +#include #include #include // #include "../config/config.h" @@ -71614,8 +76750,8 @@ struct uses_allocator, Allocator> #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -71671,6 +76807,8 @@ struct uses_allocator, Allocator> # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -71772,7 +76910,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -72014,7 +77151,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -72102,10 +77240,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -72114,6 +77262,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -72165,6 +77365,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -72285,7 +77568,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -72376,6 +77659,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -72472,6 +77759,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -72988,7 +78287,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -73021,7 +78320,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -73030,14 +78329,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -73052,13 +78351,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -73295,81 +78594,81 @@ class resource { /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if both handles refer to the same resource, false otherwise. */ -template -[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) == std::addressof(*rhs)); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. - * @return False if both handles refer to the same registry, true otherwise. + * @return False if both handles refer to the same resource, true otherwise. */ -template -[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than the second, false otherwise. */ -template -[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) < std::addressof(*rhs)); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than the second, false otherwise. */ -template -[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { - return (std::addressof(*lhs) > std::addressof(*rhs)); +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than or equal to the second, false * otherwise. */ -template -[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { return !(lhs > rhs); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than or equal to the second, * false otherwise. */ -template -[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { return !(lhs < rhs); } @@ -73456,51 +78755,51 @@ class resource_cache_iterator final { return operator*(); } - template - friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; - template - friend constexpr bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + template + friend constexpr bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; - template - friend constexpr bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + template + friend constexpr bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -73519,7 +78818,7 @@ template */ template class resource_cache { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using container_allocator = typename alloc_traits::template rebind_alloc>; using container_type = dense_map, container_allocator>; @@ -73602,8 +78901,7 @@ class resource_cache { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the cache. If the - * cache is empty, the returned iterator will be equal to `end()`. + * If the cache is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal cache. */ @@ -73623,11 +78921,6 @@ class resource_cache { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the cache. Attempting to dereference the returned iterator results in - * undefined behavior. - * * @return An iterator to the element following the last instance of the * internal cache. */ @@ -73986,81 +79279,81 @@ class resource { /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if both handles refer to the same resource, false otherwise. */ -template -[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) == std::addressof(*rhs)); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. - * @return False if both handles refer to the same registry, true otherwise. + * @return False if both handles refer to the same resource, true otherwise. */ -template -[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than the second, false otherwise. */ -template -[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) < std::addressof(*rhs)); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than the second, false otherwise. */ -template -[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { - return (std::addressof(*lhs) > std::addressof(*rhs)); +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than or equal to the second, false * otherwise. */ -template -[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { return !(lhs > rhs); } /** * @brief Compares two handles. - * @tparam Res Type of resource managed by the first handle. - * @tparam Other Type of resource managed by the second handle. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than or equal to the second, * false otherwise. */ -template -[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { return !(lhs < rhs); } @@ -74096,8 +79389,8 @@ template #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -74153,6 +79446,8 @@ template # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -74189,6 +79484,7 @@ template #include #include +#include #include #include // #include "../config/config.h" @@ -74210,8 +79506,8 @@ template #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 11 -#define ENTT_VERSION_PATCH 0 +#define ENTT_VERSION_MINOR 12 +#define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ @@ -74267,6 +79563,8 @@ template # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else @@ -74368,7 +79666,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -74610,7 +79907,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -74698,10 +79996,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -74710,6 +80018,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -74761,6 +80121,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -74881,7 +80324,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -74972,6 +80415,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -75068,6 +80515,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -75185,7 +80644,13 @@ class delegate { [[nodiscard]] auto wrap(std::index_sequence) noexcept { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } }; } @@ -75194,7 +80659,13 @@ class delegate { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } }; } @@ -75203,7 +80674,13 @@ class delegate { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } }; } @@ -75342,6 +80819,14 @@ class delegate { fn = nullptr; } + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. @@ -75473,8 +80958,8 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate delegate delegate #include +#include #include #include // #include "../config/config.h" @@ -75595,8 +81083,8 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate delegate::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -75995,7 +81484,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -76083,10 +81573,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -76095,6 +81595,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -76146,6 +81698,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -76266,7 +81901,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -76357,6 +81992,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -76453,6 +82092,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -77036,7 +82687,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). - * @tparam Args Types of arguments to use to construct the object. + * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct allocation_deleter: private Allocator { @@ -77057,7 +82708,7 @@ struct allocation_deleter: private Allocator { * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } @@ -77224,6 +82875,7 @@ constexpr Type *uninitialized_construct_using_allocator(Type *value, const Alloc #include #include +#include #include #include // #include "../config/config.h" @@ -77278,7 +82930,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -77520,7 +83171,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -77608,10 +83260,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -77620,6 +83282,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -77671,6 +83385,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -77791,7 +83588,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -77882,6 +83679,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -77978,6 +83779,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif // #include "fwd.hpp" @@ -78119,51 +83932,51 @@ class dense_map_iterator final { return {it->element.first, it->element.second}; } - template - friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; - template - friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; -template -[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } -template -[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } -template -[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } -template -[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } -template -[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } -template -[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -78221,13 +84034,13 @@ class dense_map_local_iterator final { std::size_t offset; }; -template -[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } -template -[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -78257,14 +84070,14 @@ class dense_map { static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; - using alloc_traits = typename std::allocator_traits; + using alloc_traits = std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { - return fast_mod(sparse.second()(key), bucket_count()); + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template @@ -78455,7 +84268,6 @@ class dense_map { /** * @brief Returns an iterator to the beginning. * - * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. @@ -78476,11 +84288,6 @@ class dense_map { /** * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the internal array. Attempting to dereference the returned iterator - * results in undefined behavior. - * * @return An iterator to the element following the last instance of the * internal array. */ @@ -78829,7 +84636,7 @@ class dense_map { } /*! @copydoc equal_range */ - template + template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); @@ -79064,6 +84871,7 @@ struct uses_allocator, Allocator> #include #include +#include #include #include // #include "../config/config.h" @@ -79118,7 +84926,6 @@ using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. - * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; @@ -79360,7 +85167,8 @@ struct type_list_contains; * @tparam Other Type to look for. */ template -struct type_list_contains, Other>: std::disjunction...> {}; +struct type_list_contains, Other> + : std::bool_constant<(std::is_same_v || ...)> {}; /** * @brief Helper variable template. @@ -79448,10 +85256,20 @@ struct value_list_element> */ template struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); /*! @brief Searched value. */ static constexpr auto value = Value; }; +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + /** * @brief Helper type. * @tparam Index Index of the value to return. @@ -79460,6 +85278,58 @@ struct value_list_element<0u, value_list> { template inline constexpr auto value_list_element_v = value_list_element::value; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::value; + /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. @@ -79511,6 +85381,89 @@ struct value_list_cat> { template using value_list_cat_t = typename value_list_cat::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; @@ -79631,7 +85584,7 @@ inline constexpr bool is_iterator_v = is_iterator::value; */ template struct is_ebco_eligible - : std::conjunction, std::negation>> {}; + : std::bool_constant && !std::is_final_v> {}; /** * @brief Helper variable template. @@ -79722,6 +85675,10 @@ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: std::false_type {}; + /** * @brief Helper variable template. * @tparam Type The type to test. @@ -79818,6 +85775,18 @@ using nth_argument_t = typename nth_argument::type; } // namespace entt +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + #endif @@ -80229,7 +86198,7 @@ struct basic_hashed_string { template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; - using hs_traits = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -80241,10 +86210,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { - base_type base{str, 0u, hs_traits::offset}; + base_type base{str, 0u, traits_type::offset}; for(; str[base.length]; ++base.length) { - base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; } return base; @@ -80252,10 +86221,10 @@ class basic_hashed_string: internal::basic_hashed_string { // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { - base_type base{str, len, hs_traits::offset}; + base_type base{str, len, traits_type::offset}; for(size_type pos{}; pos < len; ++pos) { - base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; } return base; @@ -80780,7 +86749,7 @@ struct identity { * @param value The actual argument. * @return The submitted value as-is. */ - template + template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } @@ -80813,7 +86782,7 @@ template * @brief Helper type for visitors. * @tparam Func Types of function objects. */ -template +template struct overloaded: Func... { using Func::operator()...; }; @@ -80822,14 +86791,14 @@ struct overloaded: Func... { * @brief Deduction guide. * @tparam Func Types of function objects. */ -template +template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ -template +template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. @@ -80844,13 +86813,13 @@ struct y_combinator { * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ - template + template constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ - template + template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -80869,8 +86838,8 @@ struct y_combinator { #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP -#include -#include +#include +#include #include #include #include @@ -80956,7 +86925,13 @@ class delegate { [[nodiscard]] auto wrap(std::index_sequence) noexcept { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); - return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } }; } @@ -80965,7 +86940,13 @@ class delegate { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } }; } @@ -80974,7 +86955,13 @@ class delegate { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); - return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } }; } @@ -81113,6 +87100,14 @@ class delegate { fn = nullptr; } + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. @@ -81251,7 +87246,8 @@ class sigh { friend class sink>; using alloc_traits = std::allocator_traits; - using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using delegate_type = delegate; + using container_type = std::vector>; public: /*! @brief Allocator type. */ @@ -81363,8 +87359,8 @@ class sigh { * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { - for(auto &&call: std::as_const(calls)) { - call(args...); + for(auto pos = calls.size(); pos; --pos) { + calls[pos - 1u](args...); } } @@ -81384,24 +87380,24 @@ class sigh { */ template void collect(Func func, Args... args) const { - for(auto &&call: calls) { - if constexpr(std::is_void_v) { + for(auto pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + if constexpr(std::is_invocable_r_v) { - call(args...); if(func()) { break; } } else { - call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { - if(func(call(args...))) { + if(func(calls[pos - 1u](args...))) { break; } } else { - func(call(args...)); + func(calls[pos - 1u](args...)); } } } @@ -81553,6 +87549,7 @@ struct scoped_connection { template class sink> { using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; using difference_type = typename signal_type::container_type::difference_type; template @@ -81565,13 +87562,14 @@ class sink> { sink{*static_cast(signal)}.disconnect(); } - auto before(delegate call) { - const auto &calls = signal->calls; - const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); - - sink other{*this}; - other.offset = calls.cend() - it; - return other; + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } } public: @@ -81580,8 +87578,7 @@ class sink> { * @param ref A valid reference to a signal object. */ sink(sigh &ref) noexcept - : offset{}, - signal{&ref} {} + : signal{&ref} {} /** * @brief Returns false if at least a listener is connected to the sink. @@ -81591,89 +87588,9 @@ class sink> { return signal->calls.empty(); } - /** - * @brief Returns a sink that connects before a given free function or an - * unbound member. - * @tparam Function A valid free function pointer. - * @return A properly initialized sink object. - */ - template - [[nodiscard]] sink before() { - delegate call{}; - call.template connect(); - return before(std::move(call)); - } - - /** - * @brief Returns a sink that connects before a free function with payload - * or a bound member. - * @tparam Candidate Member or free function to look for. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - * @return A properly initialized sink object. - */ - template - [[nodiscard]] sink before(Type &&value_or_instance) { - delegate call{}; - call.template connect(value_or_instance); - return before(std::move(call)); - } - - /** - * @brief Returns a sink that connects before a given instance or specific - * payload. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - * @return A properly initialized sink object. - */ - template>, void>, sink>> - [[nodiscard]] sink before(Type &value_or_instance) { - return before(&value_or_instance); - } - - /** - * @brief Returns a sink that connects before a given instance or specific - * payload. - * @param value_or_instance A valid pointer that fits the purpose. - * @return A properly initialized sink object. - */ - [[nodiscard]] sink before(const void *value_or_instance) { - sink other{*this}; - - if(value_or_instance) { - const auto &calls = signal->calls; - const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { - return delegate.data() == value_or_instance; - }); - - other.offset = calls.cend() - it; - } - - return other; - } - - /** - * @brief Returns a sink that connects before anything else. - * @return A properly initialized sink object. - */ - [[nodiscard]] sink before() { - sink other{*this}; - other.offset = signal->calls.size(); - return other; - } - /** * @brief Connects a free function (with or without payload), a bound or an * unbound member to a signal. - * - * The signal isn't responsible for the connected object or the payload, if - * any. Users must guarantee that the lifetime of the instance overcomes the - * one of the signal. On the other side, the signal handler performs - * checks to avoid multiple connections for the same function.
- * When used to connect a free function with payload, its signature must be - * such that the instance is the first argument before the ones used to - * define the signal itself. - * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. @@ -81683,9 +87600,9 @@ class sink> { connection connect(Type &&...value_or_instance) { disconnect(value_or_instance...); - delegate call{}; + delegate_type call{}; call.template connect(value_or_instance...); - signal->calls.insert(signal->calls.end() - offset, std::move(call)); + signal->calls.push_back(std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance...); @@ -81701,21 +87618,9 @@ class sink> { */ template void disconnect(Type &&...value_or_instance) { - auto &calls = signal->calls; - delegate call{}; + delegate_type call{}; call.template connect(value_or_instance...); - calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); - } - - /** - * @brief Disconnects free functions with payload or bound members from a - * signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - */ - template>, void>>> - void disconnect(Type &value_or_instance) { - disconnect(&value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); } /** @@ -81725,9 +87630,7 @@ class sink> { */ void disconnect(const void *value_or_instance) { if(value_or_instance) { - auto &calls = signal->calls; - auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; }; - calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end()); + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); } } @@ -81737,7 +87640,6 @@ class sink> { } private: - difference_type offset; signal_type *signal; }; @@ -81819,7 +87721,7 @@ class dispatcher_handler final: public basic_dispatcher_handler { template void enqueue(Args &&...args) { - if constexpr(std::is_aggregate_v) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { events.push_back(Type{std::forward(args)...}); } else { events.emplace_back(std::forward(args)...); @@ -81923,7 +87825,9 @@ class basic_dispatcher { * @param allocator The allocator to use. */ basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept - : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {} + : pools{container_type{std::move(other.pools.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); + } /** * @brief Move assignment operator. @@ -81931,6 +87835,8 @@ class basic_dispatcher { * @return This dispatcher. */ basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); + pools = std::move(other.pools); return *this; } @@ -82218,7 +88124,9 @@ class emitter { * @param allocator The allocator to use. */ emitter(emitter &&other, const allocator_type &allocator) noexcept - : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {} + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + } /** * @brief Move assignment operator. @@ -82226,6 +88134,8 @@ class emitter { * @return This dispatcher. */ emitter &operator=(emitter &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + handlers = std::move(other.handlers); return *this; } @@ -82315,8 +88225,8 @@ class emitter { #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP -#include -#include +#include +#include #include #include #include @@ -82372,7 +88282,8 @@ class sigh { friend class sink>; using alloc_traits = std::allocator_traits; - using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using delegate_type = delegate; + using container_type = std::vector>; public: /*! @brief Allocator type. */ @@ -82484,8 +88395,8 @@ class sigh { * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { - for(auto &&call: std::as_const(calls)) { - call(args...); + for(auto pos = calls.size(); pos; --pos) { + calls[pos - 1u](args...); } } @@ -82505,24 +88416,24 @@ class sigh { */ template void collect(Func func, Args... args) const { - for(auto &&call: calls) { - if constexpr(std::is_void_v) { + for(auto pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + if constexpr(std::is_invocable_r_v) { - call(args...); if(func()) { break; } } else { - call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { - if(func(call(args...))) { + if(func(calls[pos - 1u](args...))) { break; } } else { - func(call(args...)); + func(calls[pos - 1u](args...)); } } } @@ -82674,6 +88585,7 @@ struct scoped_connection { template class sink> { using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; using difference_type = typename signal_type::container_type::difference_type; template @@ -82686,13 +88598,14 @@ class sink> { sink{*static_cast(signal)}.disconnect(); } - auto before(delegate call) { - const auto &calls = signal->calls; - const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); - - sink other{*this}; - other.offset = calls.cend() - it; - return other; + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } } public: @@ -82701,8 +88614,7 @@ class sink> { * @param ref A valid reference to a signal object. */ sink(sigh &ref) noexcept - : offset{}, - signal{&ref} {} + : signal{&ref} {} /** * @brief Returns false if at least a listener is connected to the sink. @@ -82712,89 +88624,9 @@ class sink> { return signal->calls.empty(); } - /** - * @brief Returns a sink that connects before a given free function or an - * unbound member. - * @tparam Function A valid free function pointer. - * @return A properly initialized sink object. - */ - template - [[nodiscard]] sink before() { - delegate call{}; - call.template connect(); - return before(std::move(call)); - } - - /** - * @brief Returns a sink that connects before a free function with payload - * or a bound member. - * @tparam Candidate Member or free function to look for. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - * @return A properly initialized sink object. - */ - template - [[nodiscard]] sink before(Type &&value_or_instance) { - delegate call{}; - call.template connect(value_or_instance); - return before(std::move(call)); - } - - /** - * @brief Returns a sink that connects before a given instance or specific - * payload. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - * @return A properly initialized sink object. - */ - template>, void>, sink>> - [[nodiscard]] sink before(Type &value_or_instance) { - return before(&value_or_instance); - } - - /** - * @brief Returns a sink that connects before a given instance or specific - * payload. - * @param value_or_instance A valid pointer that fits the purpose. - * @return A properly initialized sink object. - */ - [[nodiscard]] sink before(const void *value_or_instance) { - sink other{*this}; - - if(value_or_instance) { - const auto &calls = signal->calls; - const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { - return delegate.data() == value_or_instance; - }); - - other.offset = calls.cend() - it; - } - - return other; - } - - /** - * @brief Returns a sink that connects before anything else. - * @return A properly initialized sink object. - */ - [[nodiscard]] sink before() { - sink other{*this}; - other.offset = signal->calls.size(); - return other; - } - /** * @brief Connects a free function (with or without payload), a bound or an * unbound member to a signal. - * - * The signal isn't responsible for the connected object or the payload, if - * any. Users must guarantee that the lifetime of the instance overcomes the - * one of the signal. On the other side, the signal handler performs - * checks to avoid multiple connections for the same function.
- * When used to connect a free function with payload, its signature must be - * such that the instance is the first argument before the ones used to - * define the signal itself. - * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. @@ -82804,9 +88636,9 @@ class sink> { connection connect(Type &&...value_or_instance) { disconnect(value_or_instance...); - delegate call{}; + delegate_type call{}; call.template connect(value_or_instance...); - signal->calls.insert(signal->calls.end() - offset, std::move(call)); + signal->calls.push_back(std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance...); @@ -82822,21 +88654,9 @@ class sink> { */ template void disconnect(Type &&...value_or_instance) { - auto &calls = signal->calls; - delegate call{}; + delegate_type call{}; call.template connect(value_or_instance...); - calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); - } - - /** - * @brief Disconnects free functions with payload or bound members from a - * signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - */ - template>, void>>> - void disconnect(Type &value_or_instance) { - disconnect(&value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); } /** @@ -82846,9 +88666,7 @@ class sink> { */ void disconnect(const void *value_or_instance) { if(value_or_instance) { - auto &calls = signal->calls; - auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; }; - calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end()); + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); } } @@ -82858,7 +88676,6 @@ class sink> { } private: - difference_type offset; signal_type *signal; };