diff --git a/single_include/entt/entt.hpp b/single_include/entt/entt.hpp index efec35bfb..8b5e0bc7f 100644 --- a/single_include/entt/entt.hpp +++ b/single_include/entt/entt.hpp @@ -1,3 +1,4 @@ +// IWYU pragma: begin_exports // #include "config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H @@ -17,7 +18,7 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -28,14 +29,12 @@ #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -63,16 +62,23 @@ #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -116,7 +122,7 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -129,7 +135,6 @@ #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP -#include #include #include #include @@ -159,7 +164,7 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -170,14 +175,12 @@ #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -205,16 +208,23 @@ #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -249,6 +259,14 @@ #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 @@ -268,7 +286,7 @@ #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -279,14 +297,12 @@ #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -314,16 +330,23 @@ #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -350,28 +373,17 @@ #endif -// #include "type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - -#include -#include -#include -#include -// #include "../config/config.h" - // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -498,22 +510,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -524,6 +536,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -654,6 +718,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -853,7 +940,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -941,7 +1028,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -983,7 +1070,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -1023,6 +1110,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -1043,22 +1162,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -1073,22 +1192,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -1131,7 +1250,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -1139,13 +1258,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -1155,7 +1274,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -1167,7 +1286,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -1176,25 +1295,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -1202,12 +1321,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -1215,7 +1334,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -1228,7 +1347,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -1239,7 +1358,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -1265,7 +1384,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -1305,9 +1424,8 @@ struct tuple_element>: conditional #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -1317,46 +1435,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -1372,22 +1568,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -1396,17 +1594,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -1434,69 +1632,12 @@ private: namespace entt { -/** - * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). - * @tparam Type Pointer type. - * @param ptr Fancy or raw pointer. - * @return A raw pointer that represents the address of the original pointer. - */ -template -[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { - if constexpr(std::is_pointer_v>>) { - return ptr; - } else { - return to_address(std::forward(ptr).operator->()); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { - lhs = rhs; - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { - lhs = std::move(rhs); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); - - if constexpr(std::allocator_traits::propagate_on_container_swap::value) { - using std::swap; - swap(lhs, rhs); - } -} - /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ -[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } @@ -1505,8 +1646,8 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ -[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { - ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { @@ -1522,11 +1663,68 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ -[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { - ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. @@ -1542,14 +1740,14 @@ struct allocation_deleter: private Allocator { * @brief Inherited constructors. * @param alloc The allocator to use. */ - allocation_deleter(const allocator_type &alloc) + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ - void operator()(pointer ptr) { + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); @@ -1566,7 +1764,7 @@ struct allocation_deleter: private Allocator { * @return A properly initialized unique pointer with a custom deleter. */ template -auto allocate_unique(Allocator &allocator, Args &&...args) { +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; @@ -1596,14 +1794,14 @@ namespace internal { template struct uses_allocator_construction { template - static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { - return std::tuple(std::allocator_arg, allocator, std::forward(params)...); + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); @@ -1617,7 +1815,7 @@ struct uses_allocator_construction> { using type = std::pair; template - static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), @@ -1625,22 +1823,22 @@ struct uses_allocator_construction> { } template - static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template - static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template - static constexpr auto args(const Allocator &allocator, const std::pair &value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template - static constexpr auto args(const Allocator &allocator, std::pair &&value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; @@ -1666,7 +1864,7 @@ struct uses_allocator_construction> { * @return The arguments needed to create an object of the given type. */ template -constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } @@ -1704,7 +1902,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { - return std::apply([&](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt @@ -1837,22 +2035,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -1863,6 +2061,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -1993,6 +2243,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -2192,7 +2465,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -2280,7 +2553,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -2322,7 +2595,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -2362,6 +2635,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -2447,109 +2752,109 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_iterator() ENTT_NOEXCEPT + constexpr dense_map_iterator() noexcept : it{} {} - dense_map_iterator(const It iter) ENTT_NOEXCEPT + constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} - dense_map_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } - dense_map_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } - dense_map_iterator &operator--() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } - dense_map_iterator operator--(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } - dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } - dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template - friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -2568,37 +2873,37 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_local_iterator() ENTT_NOEXCEPT + constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} - dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> - dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} - dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } - dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } - [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } @@ -2608,12 +2913,12 @@ private: }; template -[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -2649,7 +2954,7 @@ class dense_map { using packed_container_type = std::vector>; template - [[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(sparse.second()(key), bucket_count()); } @@ -2708,8 +3013,8 @@ class dense_map { void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { - packed.first()[pos] = std::move(packed.first().back()); size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } @@ -2749,7 +3054,7 @@ public: /*! @brief Default constructor. */ dense_map() - : dense_map(minimum_capacity) {} + : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. @@ -2761,35 +3066,35 @@ public: /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const allocator_type &allocator) - : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) - : dense_map{bucket_count, hash, key_equal{}, allocator} {} + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ - explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { - rehash(bucket_count); + rehash(cnt); } /*! @brief Default copy constructor. */ @@ -2806,7 +3111,7 @@ public: threshold{other.threshold} {} /*! @brief Default move constructor. */ - dense_map(dense_map &&) = default; + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. @@ -2828,13 +3133,13 @@ public: * @brief Default move assignment operator. * @return This container. */ - dense_map &operator=(dense_map &&) = default; + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } @@ -2846,17 +3151,17 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } @@ -2870,17 +3175,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return packed.first().end(); } @@ -2888,7 +3193,7 @@ public: * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } @@ -2896,12 +3201,20 @@ public: * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + /*! @brief Clears the container. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); @@ -3117,6 +3430,27 @@ public: return insert_or_do_nothing(std::move(key)).first->second; } + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. @@ -3134,7 +3468,7 @@ public: /** * @brief Finds an element with a key that compares _equivalent_ to a given - * value. + * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element @@ -3153,6 +3487,46 @@ public: return constrained_find(key, key_to_bucket(key)); } + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. @@ -3216,7 +3590,7 @@ public: * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ - [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { + [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } @@ -3292,15 +3666,19 @@ public: /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. - * @param count New number of buckets. + * @param cnt New number of buckets. */ - void rehash(const size_type count) { - auto value = (std::max)(count, minimum_capacity); - value = (std::max)(value, static_cast(size() / max_load_factor())); + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); - std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits::max)()); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); @@ -3312,11 +3690,11 @@ public: /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. - * @param count New number of elements. + * @param cnt New number of elements. */ - void reserve(const size_type count) { - packed.first().reserve(count); - rehash(static_cast(std::ceil(count / max_load_factor()))); + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** @@ -3367,7 +3745,6 @@ struct uses_allocator, Allocator> #ifndef ENTT_CONTAINER_DENSE_SET_HPP #define ENTT_CONTAINER_DENSE_SET_HPP -#include #include #include #include @@ -3410,109 +3787,109 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; - dense_set_iterator() ENTT_NOEXCEPT + constexpr dense_set_iterator() noexcept : it{} {} - dense_set_iterator(const It iter) ENTT_NOEXCEPT + constexpr dense_set_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - dense_set_iterator(const dense_set_iterator &other) ENTT_NOEXCEPT + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept : it{other.it} {} - dense_set_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator++() noexcept { return ++it, *this; } - dense_set_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_set_iterator operator++(int) noexcept { dense_set_iterator orig = *this; return ++(*this), orig; } - dense_set_iterator &operator--() ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator--() noexcept { return --it, *this; } - dense_set_iterator operator--(int) ENTT_NOEXCEPT { + constexpr dense_set_iterator operator--(int) noexcept { dense_set_iterator orig = *this; return operator--(), orig; } - dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { dense_set_iterator copy = *this; return (copy += value); } - dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return it[value].second; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it->second); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } template - friend std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; template - friend bool operator==(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; template - friend bool operator<(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -3528,37 +3905,37 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; - dense_set_local_iterator() ENTT_NOEXCEPT + constexpr dense_set_local_iterator() noexcept : it{}, offset{} {} - dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> - dense_set_local_iterator(const dense_set_local_iterator &other) ENTT_NOEXCEPT + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} - dense_set_local_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_set_local_iterator &operator++() noexcept { return offset = it[offset].first, *this; } - dense_set_local_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_set_local_iterator operator++(int) noexcept { dense_set_local_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it[offset].second); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } - [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } @@ -3568,12 +3945,12 @@ private: }; template -[[nodiscard]] bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -3608,7 +3985,7 @@ class dense_set { using packed_container_type = std::vector>; template - [[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT { + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { return fast_mod(sparse.second()(value), bucket_count()); } @@ -3651,8 +4028,8 @@ class dense_set { void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { - packed.first()[pos] = std::move(packed.first().back()); size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].first) {} *curr = pos; } @@ -3690,7 +4067,7 @@ public: /*! @brief Default constructor. */ dense_set() - : dense_set(minimum_capacity) {} + : dense_set{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. @@ -3702,35 +4079,35 @@ public: /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ - dense_set(const size_type bucket_count, const allocator_type &allocator) - : dense_set{bucket_count, hasher{}, key_equal{}, allocator} {} + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ - dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) - : dense_set{bucket_count, hash, key_equal{}, allocator} {} + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ - explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { - rehash(bucket_count); + rehash(cnt); } /*! @brief Default copy constructor. */ @@ -3747,7 +4124,7 @@ public: threshold{other.threshold} {} /*! @brief Default move constructor. */ - dense_set(dense_set &&) = default; + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. @@ -3769,13 +4146,13 @@ public: * @brief Default move assignment operator. * @return This container. */ - dense_set &operator=(dense_set &&) = default; + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } @@ -3787,17 +4164,17 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } @@ -3811,17 +4188,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return packed.first().end(); } @@ -3829,7 +4206,7 @@ public: * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } @@ -3837,12 +4214,20 @@ public: * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + /*! @brief Clears the container. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); @@ -3892,7 +4277,7 @@ public: */ template std::pair emplace(Args &&...args) { - if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v>, value_type>)) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); @@ -3966,6 +4351,27 @@ public: swap(threshold, other.threshold); } + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + /** * @brief Finds an element with a given value. * @param value Value of an element to search for. @@ -4001,6 +4407,46 @@ public: return constrained_find(value, value_to_bucket(value)); } + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + /** * @brief Checks if the container contains an element with a given value. * @param value Value of an element to search for. @@ -4064,7 +4510,7 @@ public: * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ - [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { + [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } @@ -4140,15 +4586,19 @@ public: /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. - * @param count New number of buckets. + * @param cnt New number of buckets. */ - void rehash(const size_type count) { - auto value = (std::max)(count, minimum_capacity); - value = (std::max)(value, static_cast(size() / max_load_factor())); + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); - std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits::max)()); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = value_to_bucket(packed.first()[pos].second); @@ -4160,11 +4610,11 @@ public: /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. - * @param count New number of elements. + * @param cnt New number of elements. */ - void reserve(const size_type count) { - packed.first().reserve(count); - rehash(static_cast(std::ceil(count / max_load_factor()))); + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** @@ -4206,108 +4656,8 @@ private: #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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 - namespace entt { @@ -4323,7 +4673,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -4336,7 +4686,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -4347,7 +4697,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -4377,7 +4727,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -4387,13 +4737,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -4541,13 +4891,6 @@ struct radix_sort { #include #include #include -#include -// #include "../config/config.h" - -// #include "../core/utility.hpp" -#ifndef ENTT_CORE_UTILITY_HPP -#define ENTT_CORE_UTILITY_HPP - #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H @@ -4568,7 +4911,7 @@ struct radix_sort { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -4579,14 +4922,12 @@ struct radix_sort { #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -4614,16 +4955,23 @@ struct radix_sort { #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -4650,6 +4998,12 @@ struct radix_sort { #endif +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include namespace entt { @@ -4665,7 +5019,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -4678,7 +5032,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -4689,7 +5043,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -4719,7 +5073,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -4729,13 +5083,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -4751,14 +5105,13 @@ private: #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -4820,8 +5173,6 @@ using any = basic_any<>; #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -4891,12 +5242,14 @@ class basic_hashed_string: internal::basic_hashed_string { struct const_wrapper { // non-explicit constructor on purpose - constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} + 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { @@ -4907,7 +5260,7 @@ 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { @@ -4931,7 +5284,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } @@ -4942,7 +5295,7 @@ public: * @return The numeric representation of the string. */ template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } @@ -4951,12 +5304,12 @@ public: * @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) ENTT_NOEXCEPT { + [[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() ENTT_NOEXCEPT + constexpr basic_hashed_string() noexcept : base_type{} {} /** @@ -4964,7 +5317,7 @@ public: * @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) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** @@ -4973,7 +5326,7 @@ public: * @param str Human-readable identifier. */ template - constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** @@ -4985,14 +5338,14 @@ public: * * @param wrapper Helps achieving the purpose by relying on overloading. */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + 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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } @@ -5000,7 +5353,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } @@ -5008,12 +5361,12 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } @@ -5021,7 +5374,7 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; @@ -5052,7 +5405,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; * @return True if the two hashed strings are identical, false otherwise. */ template -[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } @@ -5064,7 +5417,7 @@ template * @return True if the two hashed strings differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } @@ -5076,7 +5429,7 @@ template * @return True if the first element is less than the second, false otherwise. */ template -[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } @@ -5089,7 +5442,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } @@ -5102,7 +5455,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } @@ -5115,7 +5468,7 @@ template * false otherwise. */ template -[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } @@ -5132,7 +5485,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } @@ -5141,7 +5494,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } @@ -5162,14 +5515,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -5181,26 +5534,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -5224,13 +5577,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -5246,16 +5599,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -5270,12 +5623,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -5287,7 +5640,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -5296,7 +5649,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -5304,7 +5657,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -5312,7 +5665,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -5328,7 +5681,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -5338,7 +5691,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -5348,7 +5701,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -5359,7 +5712,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -5370,7 +5723,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -5381,7 +5734,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -5397,7 +5750,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -5408,7 +5761,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -5542,22 +5895,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -5568,6 +5921,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -5698,6 +6103,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -5897,7 +6325,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -5985,7 +6413,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -6027,7 +6455,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -6067,6 +6495,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -6074,6 +6534,36 @@ using member_class_t = typename member_class::type; namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +enum class any_policy : std::uint8_t { + owner, + ref, + cref +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. @@ -6081,30 +6571,19 @@ namespace entt { */ template class basic_any { - enum class operation : std::uint8_t { - copy, - move, - transfer, - assign, - destroy, - compare, - get - }; - - enum class policy : std::uint8_t { - owner, - ref, - cref - }; - - using storage_type = std::aligned_storage_t; + using operation = internal::any_operation; + using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); - template - static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; template - static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { + 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) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; @@ -6151,7 +6630,7 @@ class basic_any { break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { - return *static_cast(element) == *static_cast(other) ? other : nullptr; + return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } @@ -6164,31 +6643,32 @@ class basic_any { template void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + if constexpr(!std::is_void_v) { - info = &type_id>>(); vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "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) { - new(&storage) Type{std::forward(args)...}; + } else if constexpr(in_situ>>) { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { - new(&storage) Type(std::forward(args)...); + new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { - instance = new Type{std::forward(args)...}; + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + instance = new std::remove_cv_t>{std::forward(args)...}; } else { - instance = new Type(std::forward(args)...); + instance = new std::remove_cv_t>(std::forward(args)...); } } } } - basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, @@ -6201,11 +6681,8 @@ public: static constexpr auto alignment = Align; /*! @brief Default constructor. */ - constexpr basic_any() ENTT_NOEXCEPT - : instance{}, - info{&type_id()}, - vtable{}, - mode{policy::owner} {} + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. @@ -6215,7 +6692,10 @@ public: */ template explicit basic_any(std::in_place_type_t, Args &&...args) - : basic_any{} { + : instance{}, + info{}, + vtable{}, + mode{policy::owner} { initialize(std::forward(args)...); } @@ -6226,9 +6706,7 @@ public: */ template, basic_any>>> basic_any(Type &&value) - : basic_any{} { - initialize>(std::forward(value)); - } + : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. @@ -6245,7 +6723,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_any(basic_any &&other) ENTT_NOEXCEPT + basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, @@ -6282,7 +6760,7 @@ public: * @param other The instance to move from. * @return This any object. */ - basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { + basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { @@ -6312,7 +6790,7 @@ public: * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ - [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { + [[nodiscard]] const type_info &type() const noexcept { return *info; } @@ -6320,7 +6798,7 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } @@ -6329,7 +6807,7 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { + [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } @@ -6337,8 +6815,8 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data() ENTT_NOEXCEPT { - return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + [[nodiscard]] void *data() noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** @@ -6346,8 +6824,8 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { - return *info == req ? data() : nullptr; + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** @@ -6367,7 +6845,7 @@ public: * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ - bool assign(const any &other) { + bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } @@ -6376,7 +6854,7 @@ public: } /*! @copydoc assign */ - bool assign(any &&other) { + bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); @@ -6394,6 +6872,8 @@ public: vtable(operation::destroy, *this, nullptr); } + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; @@ -6403,7 +6883,7 @@ public: * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } @@ -6412,7 +6892,7 @@ public: * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ - bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } @@ -6420,16 +6900,25 @@ public: return (!vtable && !other.vtable); } + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ - [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ - [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } @@ -6437,7 +6926,7 @@ public: * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ - [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } @@ -6451,19 +6940,6 @@ private: policy mode; }; -/** - * @brief Checks if two wrappers differ in their content. - * @tparam Len Size of the storage reserved for the small buffer optimization. - * @tparam Align Alignment requirement. - * @param lhs A wrapper, either empty or not. - * @param rhs A wrapper, either empty or not. - * @return True if the two wrappers differ in their content, false otherwise. - */ -template -[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. @@ -6473,7 +6949,7 @@ template * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) ENTT_NOEXCEPT { +Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -6481,7 +6957,7 @@ Type any_cast(const basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) ENTT_NOEXCEPT { +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"); @@ -6490,7 +6966,7 @@ Type any_cast(basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) ENTT_NOEXCEPT { +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)); @@ -6506,17 +6982,21 @@ Type any_cast(basic_any &&data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) ENTT_NOEXCEPT { - const auto &info = type_id>>(); +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) ENTT_NOEXCEPT { - const auto &info = type_id>>(); - // last attempt to make wrappers for const references return their values - return static_cast(static_cast, Type> *>(data)->data(info)); +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)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } } /** @@ -6543,7 +7023,7 @@ basic_any make_any(Args &&...args) { */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { - return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; + return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt @@ -6590,8 +7070,6 @@ basic_any forward_as_any(Type &&value) { #include #include #include -// #include "../config/config.h" - // #include "type_traits.hpp" @@ -6610,22 +7088,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -6640,22 +7118,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -6698,7 +7176,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -6706,13 +7184,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -6722,7 +7200,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -6734,7 +7212,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -6743,25 +7221,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -6769,12 +7247,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -6782,7 +7260,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -6795,7 +7273,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -6806,7 +7284,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -6832,7 +7310,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -6871,8 +7349,6 @@ struct tuple_element>: conditional -// #include "../config/config.h" - namespace entt { @@ -6906,21 +7382,21 @@ inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +operator|(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) | static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +operator&(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) & static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +operator^(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } @@ -6933,35 +7409,35 @@ operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT { */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator~(const Type value) ENTT_NOEXCEPT { +operator~(const Type value) noexcept { return static_cast(~static_cast>(value)); } /*! @copydoc operator~ */ template [[nodiscard]] constexpr std::enable_if_t, bool> -operator!(const Type value) ENTT_NOEXCEPT { +operator!(const Type value) noexcept { return !static_cast>(value); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> -operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +operator|=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs | rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> -operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +operator&=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs & rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> -operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +operator^=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs ^ rhs)); } @@ -6991,12 +7467,12 @@ class family { public: /*! @brief Unsigned integer type. */ - using family_type = id_type; + using value_type = id_type; /*! @brief Statically generated unique identifier for the given type. */ template // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type - inline static const family_type type = identifier++; + inline static const value_type value = identifier++; }; } // namespace entt @@ -7009,8 +7485,6 @@ public: #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -7080,12 +7554,14 @@ class basic_hashed_string: internal::basic_hashed_string { struct const_wrapper { // non-explicit constructor on purpose - constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} + 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { @@ -7096,7 +7572,7 @@ 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { @@ -7120,7 +7596,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } @@ -7131,7 +7607,7 @@ public: * @return The numeric representation of the string. */ template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } @@ -7140,12 +7616,12 @@ public: * @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) ENTT_NOEXCEPT { + [[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() ENTT_NOEXCEPT + constexpr basic_hashed_string() noexcept : base_type{} {} /** @@ -7153,7 +7629,7 @@ public: * @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) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** @@ -7162,7 +7638,7 @@ public: * @param str Human-readable identifier. */ template - constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** @@ -7174,14 +7650,14 @@ public: * * @param wrapper Helps achieving the purpose by relying on overloading. */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + 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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } @@ -7189,7 +7665,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } @@ -7197,12 +7673,12 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } @@ -7210,7 +7686,7 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; @@ -7241,7 +7717,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; * @return True if the two hashed strings are identical, false otherwise. */ template -[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } @@ -7253,7 +7729,7 @@ template * @return True if the two hashed strings differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } @@ -7265,7 +7741,7 @@ template * @return True if the first element is less than the second, false otherwise. */ template -[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } @@ -7278,7 +7754,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } @@ -7291,7 +7767,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } @@ -7304,7 +7780,7 @@ template * false otherwise. */ template -[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } @@ -7321,7 +7797,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } @@ -7330,7 +7806,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } @@ -7347,8 +7823,6 @@ inline namespace literals { #include #include #include -// #include "../config/config.h" - // #include "fwd.hpp" // #include "type_traits.hpp" @@ -7357,47 +7831,24 @@ inline namespace literals { namespace entt { /** - * @brief Types identifiers. - * - * Variable template used to generate identifiers at compile-time for the given - * types. Use the `get` member function to know what's the identifier associated - * to the specific type. - * - * @note - * Identifiers are constant expression and can be used in any context where such - * an expression is required. As an example: - * @code{.cpp} - * using id = entt::identifier; - * - * switch(a_type_identifier) { - * case id::type: - * // ... - * break; - * case id::type: - * // ... - * break; - * default: - * // ... - * } - * @endcode - * - * @tparam Types List of types for which to generate identifiers. + * @brief Type integral identifiers. + * @tparam Type List of types for which to generate identifiers. */ -template -class identifier { - template - [[nodiscard]] static constexpr id_type get(std::index_sequence) ENTT_NOEXCEPT { - static_assert((std::is_same_v || ...), "Invalid type"); - return (0 + ... + (std::is_same_v...>>> ? id_type{Index} : id_type{})); +template +class ident { + template + [[nodiscard]] static constexpr id_type get(std::index_sequence) noexcept { + static_assert((std::is_same_v || ...), "Invalid type"); + return (0 + ... + (std::is_same_v...>>> ? id_type{Index} : id_type{})); } public: /*! @brief Unsigned integer type. */ - using identifier_type = id_type; + using value_type = id_type; /*! @brief Statically generated unique identifier for the given type. */ - template - static constexpr identifier_type type = get>(std::index_sequence_for{}); + template + static constexpr value_type value = get>(std::index_sequence_for{}); }; } // namespace entt @@ -7410,9 +7861,8 @@ public: #include #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -7422,46 +7872,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -7477,22 +8005,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -7501,17 +8031,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -7539,69 +8069,12 @@ private: namespace entt { -/** - * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). - * @tparam Type Pointer type. - * @param ptr Fancy or raw pointer. - * @return A raw pointer that represents the address of the original pointer. - */ -template -[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { - if constexpr(std::is_pointer_v>>) { - return ptr; - } else { - return to_address(std::forward(ptr).operator->()); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { - lhs = rhs; - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { - lhs = std::move(rhs); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); - - if constexpr(std::allocator_traits::propagate_on_container_swap::value) { - using std::swap; - swap(lhs, rhs); - } -} - /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ -[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } @@ -7610,8 +8083,8 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ -[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { - ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { @@ -7627,11 +8100,68 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ -[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { - ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. @@ -7647,14 +8177,14 @@ struct allocation_deleter: private Allocator { * @brief Inherited constructors. * @param alloc The allocator to use. */ - allocation_deleter(const allocator_type &alloc) + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ - void operator()(pointer ptr) { + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); @@ -7671,7 +8201,7 @@ struct allocation_deleter: private Allocator { * @return A properly initialized unique pointer with a custom deleter. */ template -auto allocate_unique(Allocator &allocator, Args &&...args) { +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; @@ -7701,14 +8231,14 @@ namespace internal { template struct uses_allocator_construction { template - static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { - return std::tuple(std::allocator_arg, allocator, std::forward(params)...); + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); @@ -7722,7 +8252,7 @@ struct uses_allocator_construction> { using type = std::pair; template - static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), @@ -7730,22 +8260,22 @@ struct uses_allocator_construction> { } template - static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template - static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template - static constexpr auto args(const Allocator &allocator, const std::pair &value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template - static constexpr auto args(const Allocator &allocator, std::pair &&value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; @@ -7771,7 +8301,7 @@ struct uses_allocator_construction> { * @return The arguments needed to create an object of the given type. */ template -constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } @@ -7809,7 +8339,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { - return std::apply([&](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt @@ -7846,7 +8376,7 @@ struct monostate { * @param val User data to assign to the given key. */ template - void operator=(Type val) const ENTT_NOEXCEPT { + void operator=(Type val) const noexcept { value = val; } @@ -7856,7 +8386,7 @@ struct monostate { * @return Stored value, if any. */ template - operator Type() const ENTT_NOEXCEPT { + operator Type() const noexcept { return value; } @@ -7883,11 +8413,44 @@ inline monostate monostate_v = {}; #include #include #include -// #include "../config/config.h" - namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct is_tuple_impl: std::false_type {}; + +template +struct is_tuple_impl>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Provides the member constant `value` to true if a given type is a + * tuple, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_tuple: internal::is_tuple_impl> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_tuple_v = is_tuple::value; + /** * @brief Utility function to unwrap tuples of a single element. * @tparam Type Tuple type of any sizes. @@ -7896,7 +8459,7 @@ namespace entt { * element otherwise. */ template -constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT { +constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept { if constexpr(std::tuple_size_v> == 1u) { return std::get<0>(std::forward(value)); } else { @@ -7904,6 +8467,46 @@ constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT { } } +/** + * @brief Utility class to forward-and-apply tuple objects. + * @tparam Func Type of underlying invocable object. + */ +template +struct forward_apply: private Func { + /** + * @brief Constructs a forward-and-apply object. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v) + : Func{std::forward(args)...} {} + + /** + * @brief Forwards and applies the arguments with the underlying function. + * @tparam Type Tuple-like type to forward to the underlying function. + * @param args Parameters to forward to the underlying function. + * @return Return value of the underlying function, if any. + */ + 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 + constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval(), args))) { + return std::apply(static_cast(*this), std::forward(args)); + } +}; + +/** + * @brief Deduction guide. + * @tparam Func Type of underlying invocable object. + */ +template +forward_apply(Func) -> forward_apply>>; + } // namespace entt #endif @@ -7934,14 +8537,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -7953,26 +8556,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -7996,13 +8599,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -8018,16 +8621,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -8042,12 +8645,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -8059,7 +8662,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -8068,7 +8671,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -8076,7 +8679,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -8084,7 +8687,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -8100,7 +8703,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -8110,7 +8713,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -8120,7 +8723,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -8131,7 +8734,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -8142,7 +8745,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -8153,7 +8756,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -8169,7 +8772,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -8180,7 +8783,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -8314,22 +8917,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -8340,6 +8943,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -8470,6 +9125,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -8669,7 +9347,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -8757,7 +9435,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -8799,7 +9477,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -8839,6 +9517,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -8847,9 +9557,8 @@ using member_class_t = typename member_class::type; #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#include #include -// #include "../config/config.h" - namespace entt { @@ -8865,7 +9574,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -8878,7 +9587,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -8889,7 +9598,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -8919,7 +9628,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -8929,13 +9638,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -8972,7 +9681,7 @@ private: #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -8983,14 +9692,12 @@ private: #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -9018,16 +9725,23 @@ private: #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -9064,15 +9778,15 @@ namespace entt { namespace internal { -template -struct in_place_delete: std::false_type {}; +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) ? 0u : ENTT_PACKED_PAGE> {}; +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; template struct page_size>> @@ -9093,6 +9807,9 @@ 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. */ @@ -9104,7 +9821,7 @@ struct component_traits { * @tparam Type Type of component. */ template -inline constexpr bool ignore_as_empty_v = (component_traits::page_size == 0u); +inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_traits::page_size == 0u); } // namespace entt @@ -9124,12 +9841,12 @@ inline constexpr bool ignore_as_empty_v = (component_traits::page_size == #define ENTT_ENTITY_FWD_HPP #include +#include // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H @@ -9149,7 +9866,7 @@ inline constexpr bool ignore_as_empty_v = (component_traits::page_size == #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -9160,14 +9877,12 @@ inline constexpr bool ignore_as_empty_v = (component_traits::page_size == #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -9195,16 +9910,23 @@ inline constexpr bool ignore_as_empty_v = (component_traits::page_size == #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -9234,7 +9956,7 @@ inline constexpr bool ignore_as_empty_v = (component_traits::page_size == namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -9247,10 +9969,6 @@ using any = basic_any<>; #endif -// #include "utility.hpp" -#ifndef ENTT_ENTITY_UTILITY_HPP -#define ENTT_ENTITY_UTILITY_HPP - // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP @@ -9265,14 +9983,13 @@ using any = basic_any<>; #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -9399,22 +10116,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -9425,6 +10142,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -9555,6 +10324,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -9754,7 +10546,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -9842,7 +10634,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -9884,7 +10676,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -9924,54 +10716,37 @@ public: template using member_class_t = typename member_class::type; -} // namespace entt +/** + * @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...)); -#endif + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); -namespace entt { + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; /** - * @brief Alias for exclusion lists. - * @tparam Type List of types. + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member. */ -template -struct exclude_t: type_list {}; - -/** - * @brief Variable template for exclusion lists. - * @tparam Type List of types. - */ -template -inline constexpr exclude_t exclude{}; - -/** - * @brief Alias for lists of observed components. - * @tparam Type List of types. - */ -template -struct get_t: type_list {}; - -/** - * @brief Variable template for lists of observed components. - * @tparam Type List of types. - */ -template -inline constexpr get_t get{}; - -/** - * @brief Alias for lists of owned components. - * @tparam Type List of types. - */ -template -struct owned_t: type_list {}; - -/** - * @brief Variable template for lists of owned components. - * @tparam Type List of types. - */ -template -inline constexpr owned_t owned{}; +template +using nth_argument_t = typename nth_argument::type; } // namespace entt @@ -9980,22 +10755,34 @@ inline constexpr owned_t owned{}; namespace entt { -template> +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +template> class basic_sparse_set; -template, typename = void> +template, typename = void> class basic_storage; -template +template, typename = void> +struct storage_type; + +template>> +struct storage_for; + +template +class sigh_storage_mixin; + +template> class basic_registry; -template +template class basic_view; -template -struct basic_runtime_view; +template> +class basic_runtime_view; -template +template class basic_group; template @@ -10016,74 +10803,118 @@ class basic_snapshot_loader; template class basic_continuous_loader; -/*! @brief Default entity identifier. */ -enum class entity : id_type {}; +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +using exclude_t = type_list; + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template +inline constexpr exclude_t exclude{}; + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template +using get_t = type_list; + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + +/** + * @brief Alias for lists of owned components. + * @tparam Type List of types. + */ +template +using owned_t = type_list; + +/** + * @brief Variable template for lists of owned components. + * @tparam Type List of types. + */ +template +inline constexpr owned_t owned{}; /*! @brief Alias declaration for the most common use case. */ -using sparse_set = basic_sparse_set; +using sparse_set = basic_sparse_set<>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Type of objects assigned to the entities. + */ +template +using storage = basic_storage; + +/*! @brief Alias declaration for the most common use case. */ +using registry = basic_registry<>; + +/*! @brief Alias declaration for the most common use case. */ +using observer = basic_observer; + +/*! @brief Alias declaration for the most common use case. */ +using organizer = basic_organizer; + +/*! @brief Alias declaration for the most common use case. */ +using handle = basic_handle; + +/*! @brief Alias declaration for the most common use case. */ +using const_handle = basic_handle; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template -using storage = basic_storage; - -/*! @brief Alias declaration for the most common use case. */ -using registry = basic_registry; - -/*! @brief Alias declaration for the most common use case. */ -using observer = basic_observer; - -/*! @brief Alias declaration for the most common use case. */ -using organizer = basic_organizer; - -/*! @brief Alias declaration for the most common use case. */ -using handle = basic_handle; - -/*! @brief Alias declaration for the most common use case. */ -using const_handle = basic_handle; +using handle_view = basic_handle; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template -using handle_view = basic_handle; +using const_handle_view = basic_handle; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot = basic_snapshot; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot_loader = basic_snapshot_loader; + +/*! @brief Alias declaration for the most common use case. */ +using continuous_loader = basic_continuous_loader; /** * @brief Alias declaration for the most common use case. - * @tparam Args Other template parameters. - */ -template -using const_handle_view = basic_handle; - -/*! @brief Alias declaration for the most common use case. */ -using snapshot = basic_snapshot; - -/*! @brief Alias declaration for the most common use case. */ -using snapshot_loader = basic_snapshot_loader; - -/*! @brief Alias declaration for the most common use case. */ -using continuous_loader = basic_continuous_loader; - -/** - * @brief Alias declaration for the most common use case. - * @tparam Get Types of components iterated by the view. - * @tparam Exclude Types of components used to filter the view. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. */ template> -using view = basic_view; +using view = basic_view, type_list_transform_t>; /*! @brief Alias declaration for the most common use case. */ using runtime_view = basic_runtime_view; +/*! @brief Alias declaration for the most common use case. */ +using const_runtime_view = basic_runtime_view; + /** * @brief Alias declaration for the most common use case. - * @tparam Args Other template parameters. + * @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 -using group = basic_group; +template +using group = basic_group, type_list_transform_t, type_list_transform_t>; } // namespace entt @@ -10162,7 +10993,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { return static_cast(value); } @@ -10171,7 +11002,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { return (to_integral(value) & base_type::entity_mask); } @@ -10180,7 +11011,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { return (to_integral(value) >> base_type::entity_shift); } @@ -10194,7 +11025,7 @@ public: * @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) ENTT_NOEXCEPT { + [[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)}; } @@ -10208,7 +11039,7 @@ public: * @param rhs The identifier from which to take the version part. * @return A properly constructed identifier. */ - [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT { + [[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)}; } @@ -10219,7 +11050,7 @@ public: * @tparam Entity The value type. */ template -[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) ENTT_NOEXCEPT { +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { return entt_traits::to_integral(value); } @@ -10228,7 +11059,7 @@ template * @tparam Entity The value type. */ template -[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) ENTT_NOEXCEPT { +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { return entt_traits::to_entity(value); } @@ -10237,7 +11068,7 @@ template * @tparam Entity The value type. */ template -[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) ENTT_NOEXCEPT { +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { return entt_traits::to_version(value); } @@ -10249,7 +11080,7 @@ struct null_t { * @return The null representation for the given type. */ template - [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } @@ -10259,7 +11090,7 @@ struct null_t { * @param other A null object. * @return True in all cases. */ - [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { return true; } @@ -10268,7 +11099,7 @@ struct null_t { * @param other A null object. * @return False in all cases. */ - [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { return false; } @@ -10279,7 +11110,7 @@ struct null_t { * @return False if the two elements differ, true otherwise. */ template - [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + [[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); } @@ -10291,7 +11122,7 @@ struct null_t { * @return True if the two elements differ, false otherwise. */ template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; @@ -10304,7 +11135,7 @@ struct null_t { * @return False if the two elements differ, true otherwise. */ template -[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { return other.operator==(entity); } @@ -10316,7 +11147,7 @@ template * @return True if the two elements differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { return !(other == entity); } @@ -10328,7 +11159,7 @@ struct tombstone_t { * @return The tombstone representation for the given type. */ template - [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } @@ -10338,7 +11169,7 @@ struct tombstone_t { * @param other A tombstone object. * @return True in all cases. */ - [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { return true; } @@ -10347,7 +11178,7 @@ struct tombstone_t { * @param other A tombstone object. * @return False in all cases. */ - [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { return false; } @@ -10358,7 +11189,7 @@ struct tombstone_t { * @return False if the two elements differ, true otherwise. */ template - [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + [[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); } @@ -10370,7 +11201,7 @@ struct tombstone_t { * @return True if the two elements differ, false otherwise. */ template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; @@ -10383,7 +11214,7 @@ struct tombstone_t { * @return False if the two elements differ, true otherwise. */ template -[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { return other.operator==(entity); } @@ -10395,7 +11226,7 @@ template * @return True if the two elements differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { return !(other == entity); } @@ -10436,9 +11267,8 @@ inline constexpr tombstone_t tombstone{}; #include #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -10448,46 +11278,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -10503,22 +11411,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -10527,17 +11437,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -10570,15 +11480,15 @@ namespace entt { namespace internal { -template -struct in_place_delete: std::false_type {}; +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) ? 0u : ENTT_PACKED_PAGE> {}; +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; template struct page_size>> @@ -10599,6 +11509,9 @@ 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. */ @@ -10610,7 +11523,7 @@ struct component_traits { * @tparam Type Type of component. */ template -inline constexpr bool ignore_as_empty_v = (component_traits::page_size == 0u); +inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_traits::page_size == 0u); } // namespace entt @@ -10700,7 +11613,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { return static_cast(value); } @@ -10709,7 +11622,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { return (to_integral(value) & base_type::entity_mask); } @@ -10718,7 +11631,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { return (to_integral(value) >> base_type::entity_shift); } @@ -10732,7 +11645,7 @@ public: * @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) ENTT_NOEXCEPT { + [[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)}; } @@ -10746,7 +11659,7 @@ public: * @param rhs The identifier from which to take the version part. * @return A properly constructed identifier. */ - [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT { + [[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)}; } @@ -10757,7 +11670,7 @@ public: * @tparam Entity The value type. */ template -[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) ENTT_NOEXCEPT { +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { return entt_traits::to_integral(value); } @@ -10766,7 +11679,7 @@ template * @tparam Entity The value type. */ template -[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) ENTT_NOEXCEPT { +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { return entt_traits::to_entity(value); } @@ -10775,7 +11688,7 @@ template * @tparam Entity The value type. */ template -[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) ENTT_NOEXCEPT { +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { return entt_traits::to_version(value); } @@ -10787,7 +11700,7 @@ struct null_t { * @return The null representation for the given type. */ template - [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } @@ -10797,7 +11710,7 @@ struct null_t { * @param other A null object. * @return True in all cases. */ - [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { return true; } @@ -10806,7 +11719,7 @@ struct null_t { * @param other A null object. * @return False in all cases. */ - [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { return false; } @@ -10817,7 +11730,7 @@ struct null_t { * @return False if the two elements differ, true otherwise. */ template - [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + [[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); } @@ -10829,7 +11742,7 @@ struct null_t { * @return True if the two elements differ, false otherwise. */ template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; @@ -10842,7 +11755,7 @@ struct null_t { * @return False if the two elements differ, true otherwise. */ template -[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { return other.operator==(entity); } @@ -10854,7 +11767,7 @@ template * @return True if the two elements differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { return !(other == entity); } @@ -10866,7 +11779,7 @@ struct tombstone_t { * @return The tombstone representation for the given type. */ template - [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } @@ -10876,7 +11789,7 @@ struct tombstone_t { * @param other A tombstone object. * @return True in all cases. */ - [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { return true; } @@ -10885,7 +11798,7 @@ struct tombstone_t { * @param other A tombstone object. * @return False in all cases. */ - [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { return false; } @@ -10896,7 +11809,7 @@ struct tombstone_t { * @return False if the two elements differ, true otherwise. */ template - [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { + [[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); } @@ -10908,7 +11821,7 @@ struct tombstone_t { * @return True if the two elements differ, false otherwise. */ template - [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; @@ -10921,7 +11834,7 @@ struct tombstone_t { * @return False if the two elements differ, true otherwise. */ template -[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { return other.operator==(entity); } @@ -10933,7 +11846,7 @@ template * @return True if the two elements differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { return !(other == entity); } @@ -10986,9 +11899,8 @@ inline constexpr tombstone_t tombstone{}; #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#include #include -// #include "../config/config.h" - namespace entt { @@ -11004,7 +11916,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -11017,7 +11929,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -11028,7 +11940,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -11058,7 +11970,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -11068,13 +11980,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -11229,108 +12141,8 @@ struct radix_sort { #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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 - namespace entt { @@ -11346,7 +12158,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -11359,7 +12171,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -11370,7 +12182,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -11400,7 +12212,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -11410,13 +12222,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -11479,8 +12291,6 @@ private: #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -11550,12 +12360,14 @@ class basic_hashed_string: internal::basic_hashed_string { struct const_wrapper { // non-explicit constructor on purpose - constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} + 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { @@ -11566,7 +12378,7 @@ 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { @@ -11590,7 +12402,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } @@ -11601,7 +12413,7 @@ public: * @return The numeric representation of the string. */ template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } @@ -11610,12 +12422,12 @@ public: * @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) ENTT_NOEXCEPT { + [[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() ENTT_NOEXCEPT + constexpr basic_hashed_string() noexcept : base_type{} {} /** @@ -11623,7 +12435,7 @@ public: * @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) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** @@ -11632,7 +12444,7 @@ public: * @param str Human-readable identifier. */ template - constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** @@ -11644,14 +12456,14 @@ public: * * @param wrapper Helps achieving the purpose by relying on overloading. */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + 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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } @@ -11659,7 +12471,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } @@ -11667,12 +12479,12 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } @@ -11680,7 +12492,7 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; @@ -11711,7 +12523,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; * @return True if the two hashed strings are identical, false otherwise. */ template -[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } @@ -11723,7 +12535,7 @@ template * @return True if the two hashed strings differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } @@ -11735,7 +12547,7 @@ template * @return True if the first element is less than the second, false otherwise. */ template -[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } @@ -11748,7 +12560,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } @@ -11761,7 +12573,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } @@ -11774,7 +12586,7 @@ template * false otherwise. */ template -[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } @@ -11791,7 +12603,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } @@ -11800,7 +12612,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } @@ -11821,14 +12633,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -11840,26 +12652,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -11883,13 +12695,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -11905,16 +12717,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -11929,12 +12741,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -11946,7 +12758,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -11955,7 +12767,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -11963,7 +12775,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -11971,7 +12783,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -11987,7 +12799,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -11997,7 +12809,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -12007,7 +12819,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -12018,7 +12830,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -12029,7 +12841,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -12040,7 +12852,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -12056,7 +12868,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -12067,7 +12879,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -12201,22 +13013,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -12227,6 +13039,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -12357,6 +13221,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -12556,7 +13443,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -12644,7 +13531,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -12686,7 +13573,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -12726,6 +13613,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -12733,6 +13652,36 @@ using member_class_t = typename member_class::type; namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +enum class any_policy : std::uint8_t { + owner, + ref, + cref +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. @@ -12740,30 +13689,19 @@ namespace entt { */ template class basic_any { - enum class operation : std::uint8_t { - copy, - move, - transfer, - assign, - destroy, - compare, - get - }; - - enum class policy : std::uint8_t { - owner, - ref, - cref - }; - - using storage_type = std::aligned_storage_t; + using operation = internal::any_operation; + using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); - template - static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; template - static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { + 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) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; @@ -12810,7 +13748,7 @@ class basic_any { break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { - return *static_cast(element) == *static_cast(other) ? other : nullptr; + return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } @@ -12823,31 +13761,32 @@ class basic_any { template void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + if constexpr(!std::is_void_v) { - info = &type_id>>(); vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "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) { - new(&storage) Type{std::forward(args)...}; + } else if constexpr(in_situ>>) { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { - new(&storage) Type(std::forward(args)...); + new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { - instance = new Type{std::forward(args)...}; + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + instance = new std::remove_cv_t>{std::forward(args)...}; } else { - instance = new Type(std::forward(args)...); + instance = new std::remove_cv_t>(std::forward(args)...); } } } } - basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, @@ -12860,11 +13799,8 @@ public: static constexpr auto alignment = Align; /*! @brief Default constructor. */ - constexpr basic_any() ENTT_NOEXCEPT - : instance{}, - info{&type_id()}, - vtable{}, - mode{policy::owner} {} + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. @@ -12874,7 +13810,10 @@ public: */ template explicit basic_any(std::in_place_type_t, Args &&...args) - : basic_any{} { + : instance{}, + info{}, + vtable{}, + mode{policy::owner} { initialize(std::forward(args)...); } @@ -12885,9 +13824,7 @@ public: */ template, basic_any>>> basic_any(Type &&value) - : basic_any{} { - initialize>(std::forward(value)); - } + : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. @@ -12904,7 +13841,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_any(basic_any &&other) ENTT_NOEXCEPT + basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, @@ -12941,7 +13878,7 @@ public: * @param other The instance to move from. * @return This any object. */ - basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { + basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { @@ -12971,7 +13908,7 @@ public: * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ - [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { + [[nodiscard]] const type_info &type() const noexcept { return *info; } @@ -12979,7 +13916,7 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } @@ -12988,7 +13925,7 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { + [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } @@ -12996,8 +13933,8 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data() ENTT_NOEXCEPT { - return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + [[nodiscard]] void *data() noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** @@ -13005,8 +13942,8 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { - return *info == req ? data() : nullptr; + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** @@ -13026,7 +13963,7 @@ public: * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ - bool assign(const any &other) { + bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } @@ -13035,7 +13972,7 @@ public: } /*! @copydoc assign */ - bool assign(any &&other) { + bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); @@ -13053,6 +13990,8 @@ public: vtable(operation::destroy, *this, nullptr); } + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; @@ -13062,7 +14001,7 @@ public: * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } @@ -13071,7 +14010,7 @@ public: * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ - bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } @@ -13079,16 +14018,25 @@ public: return (!vtable && !other.vtable); } + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ - [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ - [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } @@ -13096,7 +14044,7 @@ public: * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ - [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } @@ -13110,19 +14058,6 @@ private: policy mode; }; -/** - * @brief Checks if two wrappers differ in their content. - * @tparam Len Size of the storage reserved for the small buffer optimization. - * @tparam Align Alignment requirement. - * @param lhs A wrapper, either empty or not. - * @param rhs A wrapper, either empty or not. - * @return True if the two wrappers differ in their content, false otherwise. - */ -template -[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. @@ -13132,7 +14067,7 @@ template * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) ENTT_NOEXCEPT { +Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -13140,7 +14075,7 @@ Type any_cast(const basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) ENTT_NOEXCEPT { +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"); @@ -13149,7 +14084,7 @@ Type any_cast(basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) ENTT_NOEXCEPT { +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)); @@ -13165,17 +14100,21 @@ Type any_cast(basic_any &&data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) ENTT_NOEXCEPT { - const auto &info = type_id>>(); +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) ENTT_NOEXCEPT { - const auto &info = type_id>>(); - // last attempt to make wrappers for const references return their values - return static_cast(static_cast, Type> *>(data)->data(info)); +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)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } } /** @@ -13202,7 +14141,7 @@ basic_any make_any(Args &&...args) { */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { - return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; + return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt @@ -13224,69 +14163,12 @@ basic_any forward_as_any(Type &&value) { namespace entt { -/** - * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). - * @tparam Type Pointer type. - * @param ptr Fancy or raw pointer. - * @return A raw pointer that represents the address of the original pointer. - */ -template -[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { - if constexpr(std::is_pointer_v>>) { - return ptr; - } else { - return to_address(std::forward(ptr).operator->()); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { - lhs = rhs; - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { - lhs = std::move(rhs); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); - - if constexpr(std::allocator_traits::propagate_on_container_swap::value) { - using std::swap; - swap(lhs, rhs); - } -} - /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ -[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } @@ -13295,8 +14177,8 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ -[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { - ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { @@ -13312,11 +14194,68 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ -[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { - ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. @@ -13332,14 +14271,14 @@ struct allocation_deleter: private Allocator { * @brief Inherited constructors. * @param alloc The allocator to use. */ - allocation_deleter(const allocator_type &alloc) + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ - void operator()(pointer ptr) { + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); @@ -13356,7 +14295,7 @@ struct allocation_deleter: private Allocator { * @return A properly initialized unique pointer with a custom deleter. */ template -auto allocate_unique(Allocator &allocator, Args &&...args) { +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; @@ -13386,14 +14325,14 @@ namespace internal { template struct uses_allocator_construction { template - static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { - return std::tuple(std::allocator_arg, allocator, std::forward(params)...); + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); @@ -13407,7 +14346,7 @@ struct uses_allocator_construction> { using type = std::pair; template - static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), @@ -13415,22 +14354,22 @@ struct uses_allocator_construction> { } template - static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template - static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template - static constexpr auto args(const Allocator &allocator, const std::pair &value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template - static constexpr auto args(const Allocator &allocator, std::pair &&value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; @@ -13456,7 +14395,7 @@ struct uses_allocator_construction> { * @return The arguments needed to create an object of the given type. */ template -constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } @@ -13494,7 +14433,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { - return std::apply([&](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt @@ -13527,14 +14466,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -13546,26 +14485,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -13589,13 +14528,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -13611,16 +14550,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -13635,12 +14574,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -13652,7 +14591,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -13661,7 +14600,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -13669,7 +14608,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -13677,7 +14616,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -13693,7 +14632,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -13703,7 +14642,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -13713,7 +14652,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -13724,7 +14663,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -13735,7 +14674,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -13746,7 +14685,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -13762,7 +14701,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -13773,7 +14712,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -13803,63 +14742,63 @@ struct sparse_set_iterator final { using difference_type = typename Container::difference_type; using iterator_category = std::random_access_iterator_tag; - sparse_set_iterator() ENTT_NOEXCEPT + constexpr sparse_set_iterator() noexcept : packed{}, offset{} {} - sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept : packed{std::addressof(ref)}, offset{idx} {} - sparse_set_iterator &operator++() ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator++() noexcept { return --offset, *this; } - sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator++(int) noexcept { sparse_set_iterator orig = *this; return ++(*this), orig; } - sparse_set_iterator &operator--() ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator--() noexcept { return ++offset, *this; } - sparse_set_iterator operator--(int) ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator--(int) noexcept { sparse_set_iterator orig = *this; return operator--(), orig; } - sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } - sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { sparse_set_iterator copy = *this; return (copy += value); } - sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return packed->data()[index() - value]; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return packed->data() + index(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } - [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } @@ -13869,37 +14808,37 @@ private: }; template -[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template -[[nodiscard]] bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template -[[nodiscard]] bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template -[[nodiscard]] bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -13976,7 +14915,7 @@ class basic_sparse_set { } auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; - ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available"); + ENTT_ASSERT(elem == null, "Slot not available"); return elem; } @@ -14005,31 +14944,47 @@ protected: using basic_iterator = internal::sparse_set_iterator; /** - * @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. + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. */ - virtual void swap_and_pop(basic_iterator first, basic_iterator last) { - for(; first != last; ++first) { - sparse_ref(packed.back()) = entity_traits::combine(static_cast(first.index()), entity_traits::to_integral(packed.back())); - const auto entt = std::exchange(packed[first.index()], packed.back()); - // unnecessary but it helps to detect nasty bugs - ENTT_ASSERT((packed.back() = tombstone, true), ""); - // lazy self-assignment guard - sparse_ref(entt) = null; - packed.pop_back(); - } + 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())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); } + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + 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)); + } + +protected: /** * @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. */ - virtual void in_place_pop(basic_iterator first, basic_iterator last) { - for(; first != last; ++first) { - sparse_ref(*first) = null; - packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast(first.index()), entity_traits::reserved)); + virtual void pop(basic_iterator first, basic_iterator last) { + if(mode == deletion_policy::swap_and_pop) { + for(; first != last; ++first) { + swap_and_pop(first); + } + } else { + for(; first != last; ++first) { + in_place_pop(first); + } } } @@ -14058,11 +15013,11 @@ public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = typename entity_traits::value_type; /*! @brief Underlying version type. */ using version_type = typename entity_traits::version_type; /*! @brief Unsigned integer type. */ - using size_type = typename packed_container_type::size_type; + using size_type = std::size_t; /*! @brief Pointer type to contained entities. */ using pointer = typename packed_container_type::const_pointer; /*! @brief Random access iterator type. */ @@ -14111,7 +15066,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT + basic_sparse_set(basic_sparse_set &&other) noexcept : sparse{std::move(other.sparse)}, packed{std::move(other.packed)}, info{other.info}, @@ -14123,7 +15078,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept : sparse{std::move(other.sparse), allocator}, packed{std::move(other.packed), allocator}, info{other.info}, @@ -14142,7 +15097,7 @@ public: * @param other The instance to move from. * @return This sparse set. */ - basic_sparse_set &operator=(basic_sparse_set &&other) ENTT_NOEXCEPT { + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); release_sparse_pages(); @@ -14171,7 +15126,7 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return packed.get_allocator(); } @@ -14179,7 +15134,7 @@ public: * @brief Returns the deletion policy of a sparse set. * @return The deletion policy of the sparse set. */ - [[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT { + [[nodiscard]] deletion_policy policy() const noexcept { return mode; } @@ -14200,7 +15155,7 @@ public: * allocated space for. * @return Capacity of the sparse set. */ - [[nodiscard]] virtual size_type capacity() const ENTT_NOEXCEPT { + [[nodiscard]] virtual size_type capacity() const noexcept { return packed.capacity(); } @@ -14219,7 +15174,7 @@ public: * * @return Extent of the sparse set. */ - [[nodiscard]] size_type extent() const ENTT_NOEXCEPT { + [[nodiscard]] size_type extent() const noexcept { return sparse.size() * entity_traits::page_size; } @@ -14233,7 +15188,7 @@ public: * * @return Number of elements. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.size(); } @@ -14241,7 +15196,7 @@ public: * @brief Checks whether a sparse set is empty. * @return True if the sparse set is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.empty(); } @@ -14249,7 +15204,7 @@ public: * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. */ - [[nodiscard]] pointer data() const ENTT_NOEXCEPT { + [[nodiscard]] pointer data() const noexcept { return packed.data(); } @@ -14262,13 +15217,13 @@ public: * * @return An iterator to the first entity of the sparse set. */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { const auto pos = static_cast(packed.size()); return iterator{packed, pos}; } /*! @copydoc begin */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } @@ -14282,12 +15237,12 @@ public: * @return An iterator to the element following the last entity of a sparse * set. */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] iterator end() const noexcept { return iterator{packed, {}}; } /*! @copydoc end */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return end(); } @@ -14301,12 +15256,12 @@ public: * @return An iterator to the first entity of the reversed internal packed * array. */ - [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return std::make_reverse_iterator(end()); } /*! @copydoc rbegin */ - [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); } @@ -14320,12 +15275,12 @@ public: * @return An iterator to the element following the last entity of the * reversed sparse set. */ - [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rend() const noexcept { return std::make_reverse_iterator(begin()); } /*! @copydoc rend */ - [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crend() const noexcept { return rend(); } @@ -14335,7 +15290,7 @@ public: * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? --(end() - index(entt)) : end(); } @@ -14344,7 +15299,7 @@ public: * @param entt A valid identifier. * @return True if the sparse set contains the entity, false otherwise. */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] bool contains(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); constexpr auto cap = entity_traits::to_entity(null); // testing versions permits to avoid accessing the packed array @@ -14357,7 +15312,7 @@ public: * @return The version for the given identifier if present, the tombstone * version otherwise. */ - [[nodiscard]] version_type current(const entity_type entt) const ENTT_NOEXCEPT { + [[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; @@ -14373,7 +15328,7 @@ public: * @param entt A valid identifier. * @return The position of the entity in the sparse set. */ - [[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT { + [[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))); } @@ -14383,7 +15338,7 @@ public: * @param pos The position for which to return the entity. * @return The entity at specified location if any, a null entity otherwise. */ - [[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT { + [[nodiscard]] entity_type at(const size_type pos) const noexcept { return pos < packed.size() ? packed[pos] : null; } @@ -14392,7 +15347,7 @@ public: * @param pos The position for which to return the entity. * @return The entity at specified location. */ - [[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { + [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); return packed[pos]; } @@ -14407,12 +15362,12 @@ public: * @param entt A valid identifier. * @return An opaque pointer to the element assigned to the entity, if any. */ - const void *get(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] const void *get(const entity_type entt) const noexcept { return get_at(index(entt)); } /*! @copydoc get */ - void *get(const entity_type entt) ENTT_NOEXCEPT { + [[nodiscard]] void *get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } @@ -14443,6 +15398,7 @@ public: */ 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; } @@ -14480,7 +15436,7 @@ public: */ void erase(const entity_type entt) { const auto it = --(end() - index(entt)); - (mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_pop(it, it + 1u); + pop(it, it + 1u); } /** @@ -14495,7 +15451,7 @@ public: template void erase(It first, It last) { if constexpr(std::is_same_v) { - (mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_pop(first, last); + pop(first, last); } else { for(; first != last; ++first) { erase(*first); @@ -14692,12 +15648,12 @@ public: /*! @brief Clears a sparse set. */ void clear() { if(const auto last = end(); free_list == null) { - in_place_pop(begin(), last); + pop(begin(), last); } else { for(auto &&entity: *this) { // tombstone filter on itself if(const auto it = find(entity); it != last) { - in_place_pop(it, it + 1u); + pop(it, it + 1u); } } } @@ -14710,12 +15666,12 @@ public: * @brief Returned value type, if any. * @return Returned value type, if any. */ - const type_info &type() const ENTT_NOEXCEPT { + const type_info &type() const noexcept { return *info; } - /*! @brief Forwards variables to mixins, if any. */ - virtual void bind(any) ENTT_NOEXCEPT {} + /*! @brief Forwards variables to derived classes, if any. */ + virtual void bind(any) noexcept {} private: sparse_container_type sparse; @@ -14750,8 +15706,6 @@ private: #include #include #include -// #include "../config/config.h" - // #include "type_traits.hpp" @@ -14770,22 +15724,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -14800,22 +15754,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -14858,7 +15812,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -14866,13 +15820,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -14882,7 +15836,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -14894,7 +15848,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -14903,25 +15857,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -14929,12 +15883,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -14942,7 +15896,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -14955,7 +15909,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -14966,7 +15920,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -14992,7 +15946,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -15038,7 +15992,9 @@ struct tuple_element>: conditional>: conditional #include #include +// #include "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 @@ -15075,7 +16040,7 @@ struct tuple_element>: conditional>: conditional>: conditional -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -15157,17 +16127,6 @@ struct tuple_element>: conditional -#include -#include -#include -#include -// #include "../config/config.h" - // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP @@ -15195,7 +16154,7 @@ struct tuple_element>: conditional>: conditional>: conditional -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -15281,14 +16245,13 @@ struct tuple_element>: conditional -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -15415,22 +16378,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -15441,6 +16404,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -15571,6 +16586,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -15770,7 +16808,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -15858,7 +16896,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -15900,7 +16938,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -15940,6 +16978,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -15955,10 +17025,10 @@ namespace entt { template class delegate; -template> +template> class basic_dispatcher; -template +template> class emitter; class connection; @@ -15968,12 +17038,26 @@ struct scoped_connection; template class sink; -template> +template> class sigh; /*! @brief Alias declaration for the most common use case. */ using dispatcher = basic_dispatcher<>; +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @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{}; + } // namespace entt #endif @@ -15989,22 +17073,22 @@ namespace entt { namespace internal { template -auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); template -auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); template -auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); template -auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); template -auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); template -using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); +using function_pointer_t = decltype(function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { @@ -16018,14 +17102,6 @@ template * @endcond */ -/*! @brief Used to wrap a function or a member of a specified type. */ -template -struct connect_arg_t {}; - -/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ -template -inline constexpr connect_arg_t connect_arg{}; - /** * @brief Basic delegate implementation. * @@ -16050,7 +17126,7 @@ class delegate; template class delegate { template - [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + [[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))...)); @@ -16058,7 +17134,7 @@ class delegate { } template - [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + [[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)); @@ -16067,7 +17143,7 @@ class delegate { } template - [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + [[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)); @@ -16084,30 +17160,19 @@ public: using result_type = Ret; /*! @brief Default constructor. */ - delegate() ENTT_NOEXCEPT + delegate() noexcept : instance{nullptr}, fn{nullptr} {} /** - * @brief Constructs a delegate and connects a free function or an unbound - * member. + * @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) ENTT_NOEXCEPT { - connect(); - } - - /** - * @brief Constructs a delegate and connects a free function with payload or - * a bound member. - * @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 object that fits the purpose. - */ - template - delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { - connect(std::forward(value_or_instance)); + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); } /** @@ -16116,7 +17181,7 @@ public: * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ - delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + delegate(function_type *function, const void *payload = nullptr) noexcept { connect(function, payload); } @@ -16125,7 +17190,7 @@ public: * @tparam Candidate Function or member to connect to the delegate. */ template - void connect() ENTT_NOEXCEPT { + void connect() noexcept { instance = nullptr; if constexpr(std::is_invocable_r_v) { @@ -16155,7 +17220,7 @@ public: * @param value_or_instance A valid reference that fits the purpose. */ template - void connect(Type &value_or_instance) ENTT_NOEXCEPT { + void connect(Type &value_or_instance) noexcept { instance = &value_or_instance; if constexpr(std::is_invocable_r_v) { @@ -16179,7 +17244,7 @@ public: * @param value_or_instance A valid pointer that fits the purpose. */ template - void connect(Type *value_or_instance) ENTT_NOEXCEPT { + void connect(Type *value_or_instance) noexcept { instance = value_or_instance; if constexpr(std::is_invocable_r_v) { @@ -16205,7 +17270,8 @@ public: * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ - void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); instance = payload; fn = function; } @@ -16215,7 +17281,7 @@ public: * * After a reset, a delegate cannot be invoked anymore. */ - void reset() ENTT_NOEXCEPT { + void reset() noexcept { instance = nullptr; fn = nullptr; } @@ -16224,7 +17290,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return instance; } @@ -16249,7 +17315,7 @@ public: * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { // no need to also test instance return !(fn == nullptr); } @@ -16259,7 +17325,7 @@ public: * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ - [[nodiscard]] bool operator==(const delegate &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const delegate &other) const noexcept { return fn == other.fn && instance == other.instance; } @@ -16277,7 +17343,7 @@ private: * @return True if the two contents differ, false otherwise. */ template -[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } @@ -16358,7 +17424,6 @@ class sigh { friend class sink>; using alloc_traits = std::allocator_traits; - static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; public: @@ -16370,21 +17435,21 @@ public: using sink_type = sink>; /*! @brief Default constructor. */ - sigh() + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) : sigh{allocator_type{}} {} /** * @brief Constructs a signal handler with a given allocator. * @param allocator The allocator to use. */ - explicit sigh(const allocator_type &allocator) + 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) + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) : calls{other.calls} {} /** @@ -16392,14 +17457,14 @@ public: * @param other The instance to copy from. * @param allocator The allocator to use. */ - sigh(const sigh &other, const allocator_type &allocator) + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{other.calls, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ - sigh(sigh &&other) ENTT_NOEXCEPT + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) : calls{std::move(other.calls)} {} /** @@ -16407,7 +17472,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{std::move(other.calls), allocator} {} /** @@ -16415,7 +17480,7 @@ public: * @param other The instance to copy from. * @return This signal handler. */ - sigh &operator=(const sigh &other) { + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { calls = other.calls; return *this; } @@ -16425,7 +17490,7 @@ public: * @param other The instance to move from. * @return This signal handler. */ - sigh &operator=(sigh &&other) ENTT_NOEXCEPT { + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { calls = std::move(other.calls); return *this; } @@ -16434,7 +17499,7 @@ public: * @brief Exchanges the contents with those of a given signal handler. * @param other Signal handler to exchange the content with. */ - void swap(sigh &other) { + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { using std::swap; swap(calls, other.calls); } @@ -16443,22 +17508,15 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return calls.get_allocator(); } - /** - * @brief Instance type when it comes to connecting member functions. - * @tparam Class Type of class to which the member function belongs. - */ - template - using instance_type = Class *; - /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return calls.size(); } @@ -16466,7 +17524,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return calls.empty(); } @@ -16503,14 +17561,18 @@ public: if constexpr(std::is_void_v) { if constexpr(std::is_invocable_r_v) { call(args...); - if(func()) { break; } + if(func()) { + break; + } } else { call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { - if(func(call(args...))) { break; } + if(func(call(args...))) { + break; + } } else { func(call(args...)); } @@ -16539,13 +17601,15 @@ class connection { public: /*! @brief Default constructor. */ - connection() = default; + connection() + : disconnect{}, + signal{} {} /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(disconnect); } @@ -16559,7 +17623,7 @@ public: private: delegate disconnect; - void *signal{}; + void *signal; }; /** @@ -16589,7 +17653,7 @@ struct scoped_connection { * @brief Move constructor. * @param other The scoped connection to move from. */ - scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT + scoped_connection(scoped_connection &&other) noexcept : conn{std::exchange(other.conn, {})} {} /*! @brief Automatically breaks the link on destruction. */ @@ -16608,7 +17672,7 @@ struct scoped_connection { * @param other The scoped connection to move from. * @return This scoped connection. */ - scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT { + scoped_connection &operator=(scoped_connection &&other) noexcept { conn = std::exchange(other.conn, {}); return *this; } @@ -16627,7 +17691,7 @@ struct scoped_connection { * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(conn); } @@ -16674,12 +17738,21 @@ 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; + } + 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) ENTT_NOEXCEPT + sink(sigh &ref) noexcept : offset{}, signal{&ref} {} @@ -16687,7 +17760,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return signal->calls.empty(); } @@ -16701,13 +17774,7 @@ public: [[nodiscard]] sink before() { delegate call{}; call.template connect(); - - 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; + return before(std::move(call)); } /** @@ -16722,13 +17789,7 @@ public: [[nodiscard]] sink before(Type &&value_or_instance) { delegate call{}; call.template connect(value_or_instance); - - 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; + return before(std::move(call)); } /** @@ -16738,7 +17799,7 @@ public: * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ - template + template>, void>, sink>> [[nodiscard]] sink before(Type &value_or_instance) { return before(&value_or_instance); } @@ -16746,12 +17807,10 @@ public: /** * @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 pointer that fits the purpose. * @return A properly initialized sink object. */ - template - [[nodiscard]] sink before(Type *value_or_instance) { + [[nodiscard]] sink before(const void *value_or_instance) { sink other{*this}; if(value_or_instance) { @@ -16777,81 +17836,47 @@ public: } /** - * @brief Connects a free function or an unbound member to a signal. + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. * - * The signal handler performs checks to avoid multiple connections for the - * same function. - * - * @tparam Candidate Function or member to connect to the signal. - * @return A properly initialized connection object. - */ - template - connection connect() { - disconnect(); - - delegate call{}; - call.template connect(); - signal->calls.insert(signal->calls.end() - offset, std::move(call)); - - delegate conn{}; - conn.template connect<&release>(); - return {std::move(conn), signal}; - } - - /** - * @brief Connects a free function with payload or a bound member to a - * signal. - * - * The signal 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 signal. On the other side, the signal handler performs + * 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. - * @param value_or_instance A valid object that fits the purpose. + * @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); + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); delegate call{}; - call.template connect(value_or_instance); + call.template connect(value_or_instance...); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; - conn.template connect<&release>(value_or_instance); + conn.template connect<&release>(value_or_instance...); return {std::move(conn), signal}; } /** - * @brief Disconnects a free function or an unbound member from a signal. + * @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() { + template + void disconnect(Type &&...value_or_instance) { auto &calls = signal->calls; delegate call{}; - call.template connect(); - calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); - } - - /** - * @brief Disconnects a free function with payload or a bound member from a - * signal. - * @tparam Candidate Function or member to disconnect from the signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - */ - template - void disconnect(Type &&value_or_instance) { - auto &calls = signal->calls; - delegate call{}; - call.template connect(value_or_instance); + call.template connect(value_or_instance...); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } @@ -16861,7 +17886,7 @@ public: * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ - template + template>, void>>> void disconnect(Type &value_or_instance) { disconnect(&value_or_instance); } @@ -16869,11 +17894,9 @@ public: /** * @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 disconnect(Type *value_or_instance) { + 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; }; @@ -16928,29 +17951,22 @@ namespace entt { */ 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; - template - void notify_destruction(basic_iterator first, basic_iterator last, Func func) { + void pop(basic_iterator first, basic_iterator last) override { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); for(; first != last; ++first) { const auto entt = *first; destruction.publish(*owner, entt); const auto it = Type::find(entt); - func(it, it + 1u); + Type::pop(it, it + 1u); } } - void swap_and_pop(basic_iterator first, basic_iterator last) final { - notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); }); - } - - void in_place_pop(basic_iterator first, basic_iterator last) final { - notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); }); - } - - basic_iterator try_emplace(const typename Type::entity_type entt, const bool force_back, const void *value) final { + 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); @@ -16958,11 +17974,77 @@ class sigh_storage_mixin final: public Type { } 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 Inherited constructors. */ - using Type::Type; + /*! @brief Default constructor. */ + sigh_storage_mixin() + : sigh_storage_mixin{allocator_type{}} {} + + /** + * @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 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 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 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 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 Returns a sink object. @@ -16975,7 +18057,7 @@ public: * * @return A temporary sink object. */ - [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { + [[nodiscard]] auto on_construct() noexcept { return sink{construction}; } @@ -16990,7 +18072,7 @@ public: * * @return A temporary sink object. */ - [[nodiscard]] auto on_update() ENTT_NOEXCEPT { + [[nodiscard]] auto on_update() noexcept { return sink{update}; } @@ -17005,7 +18087,7 @@ public: * * @return A temporary sink object. */ - [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { + [[nodiscard]] auto on_destroy() noexcept { return sink{destruction}; } @@ -17060,28 +18142,26 @@ public: } /** - * @brief Forwards variables to mixins, if any. + * @brief Forwards variables to derived classes, if any. * @param value A variable wrapped in an opaque container. */ - void bind(any value) ENTT_NOEXCEPT final { - auto *reg = any_cast>(&value); + void bind(any value) noexcept final { + auto *reg = any_cast(&value); owner = reg ? reg : owner; Type::bind(std::move(value)); } private: - sigh &, const entity_type)> construction{}; - sigh &, const entity_type)> destruction{}; - sigh &, const entity_type)> update{}; - basic_registry *owner{}; + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; }; } // namespace entt #endif -// #include "sparse_set.hpp" - namespace entt { @@ -17098,7 +18178,7 @@ class storage_iterator final { using container_type = std::remove_const_t; using alloc_traits = std::allocator_traits; - using comp_traits = component_traits; + using comp_traits = component_traits>; using iterator_traits = std::iterator_traits, @@ -17112,68 +18192,67 @@ public: using difference_type = typename iterator_traits::difference_type; using iterator_category = std::random_access_iterator_tag; - storage_iterator() ENTT_NOEXCEPT = default; + constexpr storage_iterator() noexcept = default; - storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept : packed{ref}, offset{idx} {} template, typename = std::enable_if_t> - storage_iterator(const storage_iterator> &other) ENTT_NOEXCEPT - : packed{other.packed}, - offset{other.offset} {} + constexpr storage_iterator(const storage_iterator> &other) noexcept + : storage_iterator{other.packed, other.offset} {} - storage_iterator &operator++() ENTT_NOEXCEPT { + constexpr storage_iterator &operator++() noexcept { return --offset, *this; } - storage_iterator operator++(int) ENTT_NOEXCEPT { + constexpr storage_iterator operator++(int) noexcept { storage_iterator orig = *this; return ++(*this), orig; } - storage_iterator &operator--() ENTT_NOEXCEPT { + constexpr storage_iterator &operator--() noexcept { return ++offset, *this; } - storage_iterator operator--(int) ENTT_NOEXCEPT { + constexpr storage_iterator operator--(int) noexcept { storage_iterator orig = *this; return operator--(), orig; } - storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr storage_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } - storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr storage_iterator operator+(const difference_type value) const noexcept { storage_iterator copy = *this; return (copy += value); } - storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr storage_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr storage_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[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)]; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { const auto pos = index(); return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } - [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } @@ -17183,37 +18262,37 @@ private: }; template -[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template -[[nodiscard]] bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template -[[nodiscard]] bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template -[[nodiscard]] bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -17229,46 +18308,47 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - extended_storage_iterator() = default; + constexpr extended_storage_iterator() + : it{} {} - extended_storage_iterator(It base, Other... other) + constexpr extended_storage_iterator(It base, Other... other) : it{base, other...} {} template && ...) && (std::is_constructible_v && ...)>> - extended_storage_iterator(const extended_storage_iterator &other) + constexpr extended_storage_iterator(const extended_storage_iterator &other) : it{other.it} {} - extended_storage_iterator &operator++() ENTT_NOEXCEPT { + constexpr extended_storage_iterator &operator++() noexcept { return ++std::get(it), (++std::get(it), ...), *this; } - extended_storage_iterator operator++(int) ENTT_NOEXCEPT { + constexpr extended_storage_iterator operator++(int) noexcept { extended_storage_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {*std::get(it), *std::get(it)...}; } template - friend bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; private: std::tuple it; }; template -[[nodiscard]] bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_NOEXCEPT { +[[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]] bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -17290,20 +18370,20 @@ template * 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 Entity A valid entity type (see entt_traits for more details). * @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 +template class basic_storage: public basic_sparse_set::template rebind_alloc> { - static_assert(std::is_move_constructible_v && std::is_move_assignable_v, "The type must be at least move constructible/assignable"); - 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)]; } @@ -17339,12 +18419,7 @@ class basic_storage: public basic_sparse_set(args)...); } ENTT_CATCH { - if constexpr(comp_traits::in_place_delete) { - base_type::in_place_pop(it, it + 1u); - } else { - base_type::swap_and_pop(it, it + 1u); - } - + base_type::pop(it, it + 1u); ENTT_THROW; } @@ -17378,42 +18453,51 @@ private: return std::addressof(element_at(pos)); } - void swap_at(const std::size_t lhs, const std::size_t rhs) final { - using std::swap; - swap(element_at(lhs), element_at(rhs)); - } + 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"); - void move_element(const std::size_t from, const std::size_t to) final { - 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)); - } - -protected: - /** - * @brief Erases elements from a storage. - * @param first An iterator to the first element to erase. - * @param last An iterator past the last element to erase. - */ - void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { - for(; first != last; ++first) { - auto &elem = element_at(base_type::size() - 1u); - // destroying on exit allows reentrant destructors - [[maybe_unused]] auto unused = std::exchange(element_at(static_cast(first.index())), std::move(elem)); - std::destroy_at(std::addressof(elem)); - base_type::swap_and_pop(first, first + 1u); + if constexpr(!is_pinned_type_v) { + using std::swap; + swap(element_at(lhs), element_at(rhs)); } } + 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"); + + 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)); + } + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = typename underlying_type::basic_iterator; + /** - * @brief Erases elements from a storage. - * @param first An iterator to the first element to erase. - * @param last An iterator past the last element to erase. + * @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 in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { + void pop(basic_iterator first, basic_iterator last) override { for(; first != last; ++first) { - base_type::in_place_pop(first, first + 1u); - std::destroy_at(std::addressof(element_at(static_cast(first.index())))); + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + 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); + } } } @@ -17424,7 +18508,7 @@ protected: * @param force_back Force back insertion. * @return Iterator pointing to the emplaced element. */ - typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, const bool force_back, const void *value) override { + 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)); @@ -17484,7 +18568,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_storage(basic_storage &&other) ENTT_NOEXCEPT + basic_storage(basic_storage &&other) noexcept : base_type{std::move(other)}, packed{std::move(other.packed)} {} @@ -17493,7 +18577,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - basic_storage(basic_storage &&other, const allocator_type &allocator) ENTT_NOEXCEPT + 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"); @@ -17509,7 +18593,7 @@ public: * @param other The instance to move from. * @return This storage. */ - basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT { + 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"); shrink_to_size(0u); @@ -17534,7 +18618,7 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{packed.second()}; } @@ -17558,7 +18642,7 @@ public: * allocated space for. * @return Capacity of the storage. */ - [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT override { + [[nodiscard]] size_type capacity() const noexcept override { return packed.first().size() * comp_traits::page_size; } @@ -17572,12 +18656,12 @@ public: * @brief Direct access to the array of objects. * @return A pointer to the array of objects. */ - [[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT { + [[nodiscard]] const_pointer raw() const noexcept { return packed.first().data(); } /*! @copydoc raw */ - [[nodiscard]] pointer raw() ENTT_NOEXCEPT { + [[nodiscard]] pointer raw() noexcept { return packed.first().data(); } @@ -17589,18 +18673,18 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { const auto pos = static_cast(base_type::size()); return const_iterator{&packed.first(), pos}; } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { const auto pos = static_cast(base_type::size()); return iterator{&packed.first(), pos}; } @@ -17615,17 +18699,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return const_iterator{&packed.first(), {}}; } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return iterator{&packed.first(), {}}; } @@ -17638,17 +18722,17 @@ public: * * @return An iterator to the first instance of the reversed internal array. */ - [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return std::make_reverse_iterator(cend()); } /*! @copydoc crbegin */ - [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return crbegin(); } /*! @copydoc rbegin */ - [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rbegin() noexcept { return std::make_reverse_iterator(end()); } @@ -17662,17 +18746,17 @@ public: * @return An iterator to the element following the last instance of the * reversed internal array. */ - [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crend() const noexcept { return std::make_reverse_iterator(cbegin()); } /*! @copydoc crend */ - [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator rend() const noexcept { return crend(); } /*! @copydoc rend */ - [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rend() noexcept { return std::make_reverse_iterator(begin()); } @@ -17686,12 +18770,12 @@ public: * @param entt A valid identifier. * @return The object assigned to the entity. */ - [[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { return element_at(base_type::index(entt)); } /*! @copydoc get */ - [[nodiscard]] value_type &get(const entity_type entt) ENTT_NOEXCEPT { + [[nodiscard]] value_type &get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } @@ -17700,12 +18784,12 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { return std::forward_as_tuple(get(entt)); } /*! @copydoc get_as_tuple */ - [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) ENTT_NOEXCEPT { + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { return std::forward_as_tuple(get(entt)); } @@ -17794,12 +18878,12 @@ public: * * @return An iterable object to use to _visit_ the storage. */ - [[nodiscard]] iterable each() ENTT_NOEXCEPT { + [[nodiscard]] iterable each() noexcept { return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; } /*! @copydoc each */ - [[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; } @@ -17808,8 +18892,8 @@ private: }; /*! @copydoc basic_storage */ -template -class 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"); @@ -17847,14 +18931,14 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_storage(basic_storage &&other) ENTT_NOEXCEPT = default; + basic_storage(basic_storage &&other) noexcept = default; /** * @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) ENTT_NOEXCEPT + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept : base_type{std::move(other), allocator} {} /** @@ -17862,13 +18946,13 @@ public: * @param other The instance to move from. * @return This storage. */ - basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT = default; + basic_storage &operator=(basic_storage &&other) noexcept = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{base_type::get_allocator()}; } @@ -17881,7 +18965,7 @@ public: * * @param entt A valid identifier. */ - void get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + void get([[maybe_unused]] const entity_type entt) const noexcept { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); } @@ -17895,7 +18979,7 @@ public: * @param entt A valid identifier. * @return Returns an empty tuple. */ - [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + [[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{}; } @@ -17948,142 +19032,181 @@ public: * * @return An iterable object to use to _visit_ the storage. */ - [[nodiscard]] iterable each() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; } }; /** - * @brief Provides a common way to access certain properties of storage types. + * @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 Type Type of objects managed by the storage class. + * @tparam Allocator Type of allocator used to manage memory and elements. */ -template -struct storage_traits { - /*! @brief Resulting type after component-to-storage conversion. */ - using storage_type = sigh_storage_mixin>; +template +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; + +/** + * 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>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_for_t = typename storage_for::type; + } // namespace entt #endif -// #include "utility.hpp" - namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +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) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + 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*(); + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +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); +} + +} // 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 +template class basic_group; /** * @brief Non-owning group. * - * A non-owning group returns all entities and only the entities that have at - * least the given components. Moreover, it's guaranteed that the entity list - * is tightly packed in memory for fast iterations. + * 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 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 other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * - * @note - * Groups 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 all the - * groups.
- * Moreover, sorting a non-owning group affects all the instances of the same - * group (it means that users don't have to call `sort` on each instance to sort - * all of them because they _share_ entities and components). - * - * @warning - * Lifetime of a group must not overcome that of the registry that generated it. - * In any other case, attempting to use a group results in undefined behavior. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Get Type of components observed by the group. - * @tparam Exclude Types of components used to filter 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> { - /*! @brief A registry is allowed to create groups. */ - friend class basic_registry; +template +class basic_group, get_t, exclude_t> { + using underlying_type = std::common_type_t; + using basic_common_type = std::common_type_t; - template - using storage_type = constness_as_t>::storage_type, Comp>; - - using basic_common_type = std::common_type_t::base_type...>; - - struct extended_group_iterator final { - using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); - using pointer = input_iterator_pointer; - using reference = value_type; - using iterator_category = std::input_iterator_tag; - - extended_group_iterator() = default; - - extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *...> &args) - : it{from}, - pools{args} {} - - extended_group_iterator &operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - extended_group_iterator operator++(int) ENTT_NOEXCEPT { - extended_group_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - const auto entt = *it; - return std::tuple_cat(std::make_tuple(entt), std::get *>(pools)->get_as_tuple(entt)...); - } - - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { - return operator*(); - } - - [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return other.it == it; - } - - [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - private: - typename basic_common_type::iterator it; - std::tuple *...> pools; - }; - - basic_group(basic_common_type &ref, storage_type &...gpool) ENTT_NOEXCEPT - : handler{&ref}, - pools{&gpool...} {} + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; public: /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ @@ -18093,45 +19216,54 @@ public: /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable group type. */ - using iterable = iterable_adaptor; + using iterable = iterable_adaptor, get_t>>; /*! @brief Default constructor to use to create empty, invalid groups. */ - basic_group() ENTT_NOEXCEPT + 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. */ - const base_type &handle() const ENTT_NOEXCEPT { + [[nodiscard]] const base_type &handle() const noexcept { return *handler; } /** * @brief Returns the storage for a given component type. - * @tparam Comp Type of component of which to return the storage. + * @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 ENTT_NOEXCEPT { - return *std::get *>(pools); + template + [[nodiscard]] decltype(auto) storage() const noexcept { + return storage>(); } /** - * @brief Returns the storage for a given component type. - * @tparam Comp Index of component of which to return the storage. - * @return The storage for the given component type. + * @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 ENTT_NOEXCEPT { - return *std::get(pools); + template + [[nodiscard]] decltype(auto) storage() const noexcept { + return *std::get(pools); } /** - * @brief Returns the number of entities that have the given components. - * @return Number of entities that have the given components. + * @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 ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return *this ? handler->size() : size_type{}; } @@ -18140,7 +19272,7 @@ public: * allocated space for. * @return Capacity of the group. */ - [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { + [[nodiscard]] size_type capacity() const noexcept { return *this ? handler->capacity() : size_type{}; } @@ -18155,7 +19287,7 @@ public: * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return !*this || handler->empty(); } @@ -18167,7 +19299,7 @@ public: * * @return An iterator to the first entity of the group. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() const noexcept { return *this ? handler->begin() : iterator{}; } @@ -18181,7 +19313,7 @@ public: * @return An iterator to the entity following the last entity of the * group. */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] iterator end() const noexcept { return *this ? handler->end() : iterator{}; } @@ -18193,7 +19325,7 @@ public: * * @return An iterator to the first entity of the reversed group. */ - [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rbegin() const noexcept { return *this ? handler->rbegin() : reverse_iterator{}; } @@ -18208,7 +19340,7 @@ public: * @return An iterator to the entity following the last entity of the * reversed group. */ - [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rend() const noexcept { return *this ? handler->rend() : reverse_iterator{}; } @@ -18217,7 +19349,7 @@ public: * @return The first entity of the group if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type front() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } @@ -18227,7 +19359,7 @@ public: * @return The last entity of the group if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type back() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type back() const noexcept { const auto it = rbegin(); return it != rend() ? *it : null; } @@ -18238,7 +19370,7 @@ public: * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] iterator find(const entity_type entt) const noexcept { const auto it = *this ? handler->find(entt) : iterator{}; return it != end() && *it == entt ? it : end(); } @@ -18256,7 +19388,7 @@ public: * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return handler != nullptr; } @@ -18265,7 +19397,7 @@ public: * @param entt A valid identifier. * @return True if the group contains the given entity, false otherwise. */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] bool contains(const entity_type entt) const noexcept { return *this && handler->contains(entt); } @@ -18280,20 +19412,18 @@ public: * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * - * @tparam Comp Types of components to get. + * @tparam Type 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 { - ENTT_ASSERT(contains(entt), "Group does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { - return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)...); - } else if constexpr(sizeof...(Comp) == 1) { - return (std::get *>(pools)->get(entt), ...); + 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)...); + return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); } } @@ -18343,9 +19473,8 @@ public: * * @return An iterable object to use to _visit_ the group. */ - [[nodiscard]] iterable each() const ENTT_NOEXCEPT { - return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}} - : iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}}; + [[nodiscard]] iterable each() const noexcept { + return iterable{{begin(), pools}, {end(), pools}}; } /** @@ -18360,12 +19489,12 @@ public: * comparison function should be equivalent to one of the following: * * @code{.cpp} - * bool(std::tuple, std::tuple); - * bool(const Component &..., const Component &...); + * bool(std::tuple, std::tuple); + * bool(const Type &..., const Type &...); * bool(const Entity, const Entity); * @endcode * - * Where `Component` are such that they are iterated by the group.
+ * 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. * @@ -18376,7 +19505,7 @@ public: * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * - * @tparam Comp Optional types of components to compare. + * @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. @@ -18384,18 +19513,18 @@ public: * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ - template + template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { if(*this) { - if constexpr(sizeof...(Comp) == 0) { + 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...(Comp) == 1) { - return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(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)...)); + return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); } }; @@ -18418,140 +19547,62 @@ public: * can quickly ruin the order imposed to the pool of entities shared between * the non-owning groups. * - * @tparam Comp Type of component to use to impose the order. + * @tparam Type Type of component to use to impose the order. */ - template + template void sort() const { if(*this) { - handler->respect(*std::get *>(pools)); + handler->respect(*std::get>(pools)); } } private: base_type *const handler; - const std::tuple *...> pools; + const std::tuple pools; }; /** * @brief Owning group. * - * Owning groups return all entities and only the entities that have at least - * the given components. Moreover: + * 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 the lists of owned components are tightly packed in - * memory for even faster iterations and to allow direct access. - * * They stay true to the order of the owned components and all instances have - * the same order in memory. + * * 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 components are owned by a group, the faster it is to - * iterate them. + * The more types of storage are owned, the faster it is to iterate a group. * * @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 other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * - * @note - * Groups 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 all the - * groups. - * Moreover, sorting an owning group affects all the instance of the same group - * (it means that users don't have to call `sort` on each instance to sort all - * of them because they share the underlying data structure). - * - * @warning - * Lifetime of a group must not overcome that of the registry that generated it. - * In any other case, attempting to use a group results in undefined behavior. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Owned Types of components owned by the group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components used to filter the group. + * @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> { - /*! @brief A registry is allowed to create groups. */ - friend class basic_registry; +template +class basic_group, get_t, exclude_t> { + using underlying_type = std::common_type_t; + using basic_common_type = std::common_type_t; - template - using storage_type = constness_as_t>::storage_type, Comp>; - - using basic_common_type = std::common_type_t::base_type..., typename storage_type::base_type...>; - - class extended_group_iterator final { - template - auto index_to_element(storage_type &cpool) const { - if constexpr(ignore_as_empty_v>) { - return std::make_tuple(); - } else { - return std::forward_as_tuple(cpool.rbegin()[it.index()]); - } - } - - public: - using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); - using pointer = input_iterator_pointer; - using reference = value_type; - using iterator_category = std::input_iterator_tag; - - extended_group_iterator() = default; - - template - extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *..., storage_type *...> &cpools) - : it{from}, - pools{cpools} {} - - extended_group_iterator &operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - extended_group_iterator operator++(int) ENTT_NOEXCEPT { - extended_group_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] reference operator*() const ENTT_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 ENTT_NOEXCEPT { - return operator*(); - } - - [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return other.it == it; - } - - [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - private: - typename basic_common_type::iterator it; - std::tuple *..., storage_type *...> pools; - }; - - basic_group(const std::size_t &extent, storage_type &...opool, storage_type &...gpool) ENTT_NOEXCEPT - : pools{&opool..., &gpool...}, - length{&extent} {} + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; public: /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ @@ -18561,37 +19612,47 @@ public: /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable group type. */ - using iterable = iterable_adaptor; + using iterable = iterable_adaptor, get_t>>; /*! @brief Default constructor to use to create empty, invalid groups. */ - basic_group() ENTT_NOEXCEPT + basic_group() noexcept : length{} {} /** - * @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 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. */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get *>(pools); - } + basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept + : pools{&opool..., &gpool...}, + length{&extent} {} /** * @brief Returns the storage for a given component type. - * @tparam Comp Index of component of which to return the storage. + * @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 ENTT_NOEXCEPT { - return *std::get(pools); + template + [[nodiscard]] decltype(auto) storage() const noexcept { + return storage>(); } /** - * @brief Returns the number of entities that have the given components. - * @return Number of entities that have the given components. + * @brief Returns the storage for a given index. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + template + [[nodiscard]] decltype(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. + */ + [[nodiscard]] size_type size() const noexcept { return *this ? *length : size_type{}; } @@ -18599,7 +19660,7 @@ public: * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return !*this || !*length; } @@ -18611,7 +19672,7 @@ public: * * @return An iterator to the first entity of the group. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() const noexcept { return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; } @@ -18625,7 +19686,7 @@ public: * @return An iterator to the entity following the last entity of the * group. */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] iterator end() const noexcept { return *this ? std::get<0>(pools)->base_type::end() : iterator{}; } @@ -18637,7 +19698,7 @@ public: * * @return An iterator to the first entity of the reversed group. */ - [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rbegin() const noexcept { return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; } @@ -18652,7 +19713,7 @@ public: * @return An iterator to the entity following the last entity of the * reversed group. */ - [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rend() const noexcept { return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; } @@ -18661,7 +19722,7 @@ public: * @return The first entity of the group if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type front() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } @@ -18671,7 +19732,7 @@ public: * @return The last entity of the group if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type back() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type back() const noexcept { const auto it = rbegin(); return it != rend() ? *it : null; } @@ -18682,7 +19743,7 @@ public: * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { + [[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(); } @@ -18700,7 +19761,7 @@ public: * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return length != nullptr; } @@ -18709,7 +19770,7 @@ public: * @param entt A valid identifier. * @return True if the group contains the given entity, false otherwise. */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] bool contains(const entity_type entt) const noexcept { return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); } @@ -18724,20 +19785,18 @@ public: * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * - * @tparam Comp Types of components to get. + * @tparam Type 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 { - ENTT_ASSERT(contains(entt), "Group does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { - return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)..., std::get *>(pools)->get_as_tuple(entt)...); - } else if constexpr(sizeof...(Comp) == 1) { - return (std::get *>(pools)->get(entt), ...); + 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)...); + return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); } } @@ -18787,9 +19846,8 @@ public: * * @return An iterable object to use to _visit_ the group. */ - [[nodiscard]] iterable each() const ENTT_NOEXCEPT { - iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{}; - return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}}; + [[nodiscard]] iterable each() const noexcept { + return {{begin(), pools}, {end(), pools}}; } /** @@ -18804,13 +19862,13 @@ public: * comparison function should be equivalent to one of the following: * * @code{.cpp} - * bool(std::tuple, std::tuple); - * bool(const Component &, const Component &); + * bool(std::tuple, std::tuple); + * bool(const Type &, const Type &); * bool(const Entity, const Entity); * @endcode * - * Where `Component` are either owned types or not but still such that they - * are iterated by the group.
+ * 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. * @@ -18821,7 +19879,7 @@ public: * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * - * @tparam Comp Optional types of components to compare. + * @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. @@ -18829,36 +19887,35 @@ public: * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ - template + template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { - auto *cpool = std::get<0>(pools); - - if constexpr(sizeof...(Comp) == 0) { + if constexpr(sizeof...(Type) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); - cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); + 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...(Comp) == 1) { - return compare((std::get *>(pools)->get(lhs), ...), (std::get *>(pools)->get(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)...)); + return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); } }; - cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); + std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); } - [this](auto *head, auto *...other) { + 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), ...); } - }(std::get *>(pools)...); + }, + pools); } private: - const std::tuple *..., storage_type *...> pools; + const std::tuple pools; const size_type *const length; }; @@ -18870,6 +19927,406 @@ private: #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()}}; + } + + /** + * @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. + */ + 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{}; + } + + /** + * @brief Converts a handle to its underlying entity. + * @return The contained identifier. + */ + [[nodiscard]] operator entity_type() const noexcept { + return entity(); + } + + /** + * @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 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 Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] registry_type *registry() const noexcept { + return reg; + } + + /** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ + [[nodiscard]] entity_type entity() const noexcept { + return entt; + } + + /*! @brief Destroys the entity associated with a handle. */ + void destroy() { + reg->destroy(entt); + } + + /** + * @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); + } + + /** + * @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 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 + 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)...); + } + + /** + * @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 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 + 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)...); + } + + /** + * @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 Erases the given components from a handle. + * @tparam Component Types of components to erase. + */ + template + void erase() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + reg->template erase(entt); + } + + /** + * @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. + */ + template + [[nodiscard]] decltype(auto) all_of() const { + return reg->template all_of(entt); + } + + /** + * @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. + */ + template + [[nodiscard]] decltype(auto) any_of() const { + return reg->template any_of(entt); + } + + /** + * @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. + */ + template + [[nodiscard]] decltype(auto) get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template get(entt); + } + + /** + * @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. + */ + 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 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. + */ + 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: + 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 @@ -18879,15 +20336,3557 @@ private: // #include "fwd.hpp" -// #include "registry.hpp" -#ifndef ENTT_ENTITY_REGISTRY_HPP -#define ENTT_ENTITY_REGISTRY_HPP -#include +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))...)); + }; + } + + 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))...)); + }; + } + +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 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); + } + + /** + * @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; + + 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 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; + + 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 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; + + 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 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; + } + + /** + * @brief Resets a delegate. + * + * 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. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @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)...); + } + + /** + * @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 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); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @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>>; + +/** + * @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; + +} // namespace entt + +#endif + +// #include "component.hpp" + +// #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/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +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) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + 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*(); + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +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); +} + +} // 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. */ + 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; + } + + /** + * @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>(); + } + + /** + * @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); + } + + /** + * @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{}; + } + + /** + * @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{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + handler->shrink_to_fit(); + } + } + + /** + * @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; + } + + /** + * @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; + } + + /** + * @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(); + } + + /** + * @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 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; + } + + /** + * @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); + } + + /** + * @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)...); + } + } + + /** + * @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 { + 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)...); + } + } + } + + /** + * @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. + */ + template + void sort() const { + if(*this) { + handler->respect(*std::get>(pools)); + } + } + +private: + base_type *const handler; + const std::tuple pools; +}; + +/** + * @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 and using them results in undefined behavior. + * + * @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 + 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. */ + 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 + : length{} {} + + /** + * @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. + */ + basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept + : pools{&opool..., &gpool...}, + length{&extent} {} + + /** + * @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>(); + } + + /** + * @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); + } + + /** + * @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 ? *length : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !*length; + } + + /** + * @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 ? (std::get<0>(pools)->base_type::end() - *length) : 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{}; + } + + /** + * @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{}; + } + + /** + * @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{}; + } + + /** + * @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; + } + + /** + * @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; + } + + /** + * @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 ? std::get<0>(pools)->find(entt) : iterator{}; + return it != end() && it >= begin() && *it == entt ? it : 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. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @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 length != nullptr; + } + + /** + * @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 && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); + } + + /** + * @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)...); + } + } + + /** + * @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 "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +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) + && std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); + } + +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 all_of, std::array none_of) noexcept + : it{curr}, + last{to}, + pools{all_of}, + filter{none_of} { + 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 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; + + constexpr extended_view_iterator() + : it{}, + pools{} {} + + extended_view_iterator(It from, std::tuple storage) + : it{from}, + pools{storage} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[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); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + template + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +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 { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @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_view; + +/** + * @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. + */ +template +class basic_view, exclude_t> { + using underlying_type = std::common_type_t; + using basic_common_type = std::common_type_t; + + template + friend class basic_view; + + template + 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; + } + + [[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)); + } + } + + [[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)) { + 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 { + ((&storage() == view ? each(func, seq) : void()), ...); + } + +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 base_type = basic_common_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 views. */ + basic_view() noexcept + : pools{}, + filter{}, + view{} {} + + /** + * @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. + */ + 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...)} {} + + /** + * @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. + */ + 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)} {} + + /** + * @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. + */ + template + [[nodiscard]] basic_view use() const noexcept { + return 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. + */ + template + [[nodiscard]] basic_view use() const noexcept { + basic_view other{*this}; + other.view = &storage(); + return other; + } + + /** + * @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 Returns the leading storage of a view. + * @return The leading storage of the view. + */ + [[nodiscard]] const base_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. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const noexcept { + return storage>(); + } + + /** + * @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); + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept { + return view->size(); + } + + /** + * @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 iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; + } + + /** + * @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()}; + } + + /** + * @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; + } + + /** + * @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 { + auto it = view->rbegin(); + for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} + return it == view->rend() ? null : *it; + } + + /** + * @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 contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter_as_array()} : end(); + } + + /** + * @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 Checks if a view is properly initialized. + * @return True if the view is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return view != nullptr; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @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); + } + + /** + * @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. + */ + 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)...); + } + } + + /** + * @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. + */ + 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)...); + } + } + + /** + * @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. + */ + template + void each(Func func) const { + pick_and_each(func, std::index_sequence_for{}); + } + + /** + * @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. + */ + [[nodiscard]] iterable each() const noexcept { + return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; + } + + /** + * @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))); + } + +private: + std::tuple pools; + std::tuple filter; + const base_type *view; +}; + +/** + * @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 + * + * 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 pool iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @tparam Get Type of storage iterated by the view. + */ +template +class basic_view, exclude_t<>, std::void_t::in_place_delete>>> { + template + friend class basic_view; + +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()); + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : pools{}, + filter{} {} + + /** + * @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{} {} + + /** + * @brief Constructs a single-type view from a storage class. + * @param ref The storage for the type to iterate. + */ + basic_view(std::tuple ref, std::tuple<> = {}) noexcept + : pools{&std::get<0>(ref)}, + filter{} {} + + /** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ + [[nodiscard]] const base_type &handle() const noexcept { + return storage(); + } + + /** + * @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 { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return storage<0>(); + } + + /** + * @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); + } + + /** + * @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 handle().size(); + } + + /** + * @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 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(); + } + + /** + * @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(); + } + + /** + * @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(); + } + + /** + * @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(); + } + + /** + * @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(); + } + + /** + * @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 { + return empty() ? null : *rbegin(); + } + + /** + * @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 contains(entt) ? handle().find(entt) : 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. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @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 storage().get(entt); + } + + /** + * @brief Checks if a view is properly initialized. + * @return True if the view is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return std::get<0>(pools) != nullptr; + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return handle().contains(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the view results in + * undefined behavior. + * + * @tparam Type Type or index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + 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); + } + + /** + * @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: + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename 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 { + 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); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * 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 view. + */ + [[nodiscard]] iterable each() const noexcept { + return storage().each(); + } + + /** + * @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, 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...>{}); + } + +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_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 = 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. + */ + 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) { + 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 + +// #include "entity/observer.hpp" +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + +#include +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "fwd.hpp" + +// #include "storage.hpp" + + +namespace entt { + +/*! @brief Grouping matcher. */ +template +struct matcher {}; + +/** + * @brief Collector. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +struct basic_collector; + +/** + * @brief Collector. + * + * A collector contains a set of rules (literally, matchers) to use to track + * entities.
+ * Its main purpose is to generate a descriptor that allows an observer to know + * how to connect to a registry. + */ +template<> +struct basic_collector<> { + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) noexcept { + return basic_collector, type_list<>, type_list, AllOf...>>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() noexcept { + return basic_collector, type_list<>, AnyOf>>{}; + } +}; + +/** + * @brief Collector. + * @copydetails basic_collector<> + * @tparam Reject Untracked types used to filter out entities. + * @tparam Require Untracked types required by the matcher. + * @tparam Rule Specific details of the current matcher. + * @tparam Other Other matchers. + */ +template +struct basic_collector, type_list, Rule...>, Other...> { + /*! @brief Current matcher. */ + using current_type = matcher, type_list, Rule...>; + + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) noexcept { + return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() noexcept { + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of components required by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto where(exclude_t = {}) noexcept { + using extended_type = matcher, type_list, Rule...>; + return basic_collector{}; + } +}; + +/*! @brief Variable template used to ease the definition of collectors. */ +inline constexpr basic_collector<> collector{}; + +/** + * @brief Observer. + * + * An observer returns all the entities and only the entities that fit the + * requirements of at least one matcher. Moreover, it's guaranteed that the + * entity list is tightly packed in memory for fast iterations.
+ * In general, observers don't stay true to the order of any set of components. + * + * Observers work mainly with two types of matchers, provided through a + * collector: + * + * * Observing matcher: an observer will return at least all the living entities + * for which one or more of the given components have been updated and not yet + * destroyed. + * * Grouping matcher: an observer will return at least all the living entities + * that would have entered the given group if it existed and that would have + * not yet left it. + * + * If an entity respects the requirements of multiple matchers, it will be + * returned once and only once by the observer in any case. + * + * Matchers support also filtering by means of a _where_ clause that accepts + * both a list of types and an exclusion list.
+ * Whenever a matcher finds that an entity matches its requirements, the + * condition of the filter is verified before to register the entity itself. + * Moreover, a registered entity isn't returned by the observer if the condition + * set by the filter is broken in the meantime. + * + * @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). + * * 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. + * + * @warning + * Lifetime of an observer doesn't necessarily have to overcome that of the + * registry to which it is connected. However, the observer must be disconnected + * from the registry before being destroyed to avoid crashes due to dangling + * pointers. + * + * @tparam Registry Basic registry type. + */ +template +class basic_observer: private basic_storage { + using base_type = basic_storage; + + template + struct matcher_handler; + + template + struct matcher_handler, type_list, AnyOf>> { + template + static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) { + if(reg.template all_of(entt) && !reg.template any_of(entt)) { + if(!obs.contains(entt)) { + obs.emplace(entt); + } + + obs.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { + if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { + obs.erase(entt); + } + } + + template + static void connect(basic_observer &obs, Registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + reg.template on_update().template connect<&maybe_valid_if>(obs); + reg.template on_destroy().template connect<&discard_if>(obs); + } + + static void disconnect(basic_observer &obs, Registry ®) { + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + reg.template on_update().disconnect(obs); + reg.template on_destroy().disconnect(obs); + } + }; + + template + struct matcher_handler, type_list, type_list, AllOf...>> { + template + static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) { + auto condition = [®, entt]() { + if constexpr(sizeof...(Ignore) == 0) { + return reg.template all_of(entt) && !reg.template any_of(entt); + } else { + return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); + } + }; + + if(condition()) { + if(!obs.contains(entt)) { + obs.emplace(entt); + } + + obs.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { + if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { + obs.erase(entt); + } + } + + template + static void connect(basic_observer &obs, Registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + } + + static void disconnect(basic_observer &obs, Registry ®) { + (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), ...); + } + }; + + template + static void disconnect(Registry ®, basic_observer &obs) { + (matcher_handler::disconnect(obs, reg), ...); + } + + template + void connect(Registry ®, std::index_sequence) { + static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); + (matcher_handler::template connect(*this, reg), ...); + release.template connect<&basic_observer::disconnect>(reg); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename registry_type::base_type::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : release{} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_observer(const basic_observer &) = delete; + /*! @brief Default move constructor, deleted on purpose. */ + basic_observer(basic_observer &&) = delete; + + /** + * @brief Creates an observer and connects it to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + basic_observer(registry_type ®, basic_collector) + : basic_observer{} { + connect(reg, std::index_sequence_for{}); + } + + /*! @brief Default destructor. */ + ~basic_observer() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer &operator=(const basic_observer &) = delete; + + /** + * @brief Default move assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer &operator=(basic_observer &&) = delete; + + /** + * @brief Connects an observer to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + void connect(registry_type ®, basic_collector) { + disconnect(); + connect(reg, std::index_sequence_for{}); + base_type::clear(); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*this); + release.reset(); + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return base_type::size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return base_type::empty(); + } + + /** + * @brief Direct access to the list of entities of the observer. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type *data() const noexcept { + return base_type::data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * The returned iterator points to the first entity of the observer. If the + * container is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + [[nodiscard]] iterator begin() const noexcept { + return base_type::base_type::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * + * The returned iterator points to the entity following the last entity of + * the observer. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * observer. + */ + [[nodiscard]] iterator end() const noexcept { + return base_type::base_type::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() noexcept { + base_type::clear(); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity.
+ * The signature of the function must be equivalent to the following form: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + + /** + * @brief Iterates entities and applies the given function object to them, + * then clears the observer. + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) { + std::as_const(*this).each(std::move(func)); + clear(); + } + +private: + delegate release; +}; + +} // namespace entt + +#endif + +// #include "entity/organizer.hpp" +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include +#include +#include +#include +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#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; + + /** + * @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); + } +}; + +/** + * @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; +} + +/** + * @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; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +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 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)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "../graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + #include #include #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 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 + +#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/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#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 + +#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 + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[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; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[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; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "../graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include #include #include #include @@ -18897,7 +23896,6 @@ private: #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP -#include #include #include #include @@ -18927,7 +23925,7 @@ private: #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -18938,14 +23936,12 @@ private: #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -18973,16 +23969,23 @@ private: #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -19017,6 +24020,14 @@ private: #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 @@ -19036,7 +24047,7 @@ private: #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -19047,14 +24058,12 @@ private: #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -19082,16 +24091,23 @@ private: #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -19118,28 +24134,17 @@ private: #endif -// #include "type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - -#include -#include -#include -#include -// #include "../config/config.h" - // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -19266,22 +24271,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -19292,6 +24297,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -19422,6 +24479,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -19621,7 +24701,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -19709,7 +24789,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -19751,7 +24831,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -19791,6 +24871,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -19811,22 +24923,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -19841,22 +24953,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -19899,7 +25011,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -19907,13 +25019,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -19923,7 +25035,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -19935,7 +25047,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -19944,25 +25056,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -19970,12 +25082,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -19983,7 +25095,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -19996,7 +25108,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -20007,7 +25119,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -20033,7 +25145,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -20073,9 +25185,8 @@ struct tuple_element>: conditional #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -20085,46 +25196,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -20140,22 +25329,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -20164,17 +25355,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -20202,69 +25393,12 @@ private: namespace entt { -/** - * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). - * @tparam Type Pointer type. - * @param ptr Fancy or raw pointer. - * @return A raw pointer that represents the address of the original pointer. - */ -template -[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { - if constexpr(std::is_pointer_v>>) { - return ptr; - } else { - return to_address(std::forward(ptr).operator->()); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { - lhs = rhs; - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { - lhs = std::move(rhs); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); - - if constexpr(std::allocator_traits::propagate_on_container_swap::value) { - using std::swap; - swap(lhs, rhs); - } -} - /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ -[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } @@ -20273,8 +25407,8 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ -[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { - ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { @@ -20290,11 +25424,68 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ -[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { - ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. @@ -20310,14 +25501,14 @@ struct allocation_deleter: private Allocator { * @brief Inherited constructors. * @param alloc The allocator to use. */ - allocation_deleter(const allocator_type &alloc) + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ - void operator()(pointer ptr) { + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); @@ -20334,7 +25525,7 @@ struct allocation_deleter: private Allocator { * @return A properly initialized unique pointer with a custom deleter. */ template -auto allocate_unique(Allocator &allocator, Args &&...args) { +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; @@ -20364,14 +25555,14 @@ namespace internal { template struct uses_allocator_construction { template - static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { - return std::tuple(std::allocator_arg, allocator, std::forward(params)...); + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); @@ -20385,7 +25576,7 @@ struct uses_allocator_construction> { using type = std::pair; template - static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), @@ -20393,22 +25584,22 @@ struct uses_allocator_construction> { } template - static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template - static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template - static constexpr auto args(const Allocator &allocator, const std::pair &value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template - static constexpr auto args(const Allocator &allocator, std::pair &&value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; @@ -20434,7 +25625,7 @@ struct uses_allocator_construction> { * @return The arguments needed to create an object of the given type. */ template -constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } @@ -20472,7 +25663,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { - return std::apply([&](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt @@ -20605,22 +25796,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -20631,6 +25822,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -20761,6 +26004,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -20960,7 +26226,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -21048,7 +26314,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -21090,7 +26356,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -21130,6 +26396,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -21215,109 +26513,109 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_iterator() ENTT_NOEXCEPT + constexpr dense_map_iterator() noexcept : it{} {} - dense_map_iterator(const It iter) ENTT_NOEXCEPT + constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} - dense_map_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } - dense_map_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } - dense_map_iterator &operator--() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } - dense_map_iterator operator--(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } - dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } - dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template - friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -21336,37 +26634,37 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_local_iterator() ENTT_NOEXCEPT + constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} - dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> - dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} - dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } - dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } - [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } @@ -21376,12 +26674,12 @@ private: }; template -[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -21417,7 +26715,7 @@ class dense_map { using packed_container_type = std::vector>; template - [[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(sparse.second()(key), bucket_count()); } @@ -21476,8 +26774,8 @@ class dense_map { void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { - packed.first()[pos] = std::move(packed.first().back()); size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } @@ -21517,7 +26815,7 @@ public: /*! @brief Default constructor. */ dense_map() - : dense_map(minimum_capacity) {} + : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. @@ -21529,35 +26827,35 @@ public: /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const allocator_type &allocator) - : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) - : dense_map{bucket_count, hash, key_equal{}, allocator} {} + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ - explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { - rehash(bucket_count); + rehash(cnt); } /*! @brief Default copy constructor. */ @@ -21574,7 +26872,7 @@ public: threshold{other.threshold} {} /*! @brief Default move constructor. */ - dense_map(dense_map &&) = default; + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. @@ -21596,13 +26894,13 @@ public: * @brief Default move assignment operator. * @return This container. */ - dense_map &operator=(dense_map &&) = default; + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } @@ -21614,17 +26912,17 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } @@ -21638,17 +26936,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return packed.first().end(); } @@ -21656,7 +26954,7 @@ public: * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } @@ -21664,12 +26962,20 @@ public: * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + /*! @brief Clears the container. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); @@ -21885,6 +27191,27 @@ public: return insert_or_do_nothing(std::move(key)).first->second; } + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. @@ -21902,7 +27229,7 @@ public: /** * @brief Finds an element with a key that compares _equivalent_ to a given - * value. + * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element @@ -21921,6 +27248,46 @@ public: return constrained_find(key, key_to_bucket(key)); } + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. @@ -21984,7 +27351,7 @@ public: * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ - [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { + [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } @@ -22060,15 +27427,19 @@ public: /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. - * @param count New number of buckets. + * @param cnt New number of buckets. */ - void rehash(const size_type count) { - auto value = (std::max)(count, minimum_capacity); - value = (std::max)(value, static_cast(size() / max_load_factor())); + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); - std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits::max)()); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); @@ -22080,11 +27451,6925 @@ public: /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. - * @param count New number of elements. + * @param cnt New number of elements. */ - void reserve(const size_type count) { - packed.first().reserve(count); - rehash(static_cast(std::ceil(count / max_load_factor()))); + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + 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; + +private: + It it; +}; + +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 { + return lhs.it == rhs.it; +} + +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 { + return lhs.it < rhs.it; +} + +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 { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +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 { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + 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 value_to_bucket(const Other &value) const noexcept { + return fast_mod(sparse.second()(value), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @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. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @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 packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#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" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @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; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +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 {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @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 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 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 Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @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> {}; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 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 Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @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 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 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 Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @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>>; +}; + +/*! @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 Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @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; + +/** + * @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...> {}; + +/** + * @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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @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 Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 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 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 value. */ + static constexpr auto value = Value; +}; + +/** + * @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 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 {}; +} + +/*! @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<>; +}; + +/** + * @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; +}; + +/** + * @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; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @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 {}; + +/** + * @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 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; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_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 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 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 {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>, void>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @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 Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @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 {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (is_equality_comparable>::value && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { + return true; +} + +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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::bool_constant(choice<2>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @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; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @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 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"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @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 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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal + +/** + * 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; + +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; + + /** + * @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{} {} + + /** + * @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; + + /** + * @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; + + /** + * @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)} {} + + /** + * @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{}} {} + + /** + * @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; + + /** + * @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; + + /** + * @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(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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()); + } + + /** + * @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(); + } + } + + /*! @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(); + } + } +}; + +/** + * @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>; + +/** + * @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); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @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 {}; + +/** + * @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"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#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; + + /** + * @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); + } +}; + +/** + * @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; +} + +/** + * @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; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +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 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)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[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; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[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; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + 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>>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{}, + deps{}, + sync_on{} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[pos]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = sync_on = {}; + vertices.clear(); + deps.clear(); + } + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @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(); + + 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); + } + } + } + } + } + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "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" + +// #include "component.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "view.hpp" + + +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 = std::remove_const_t; + + /** + * @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 = 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. + */ + 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) { + 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 + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct is_view: std::false_type {}; + +template +struct is_view>: std::true_type {}; + +template +inline constexpr bool is_view_v = is_view::value; + +template +struct unpack_type { + using ro = std::conditional_t< + type_list_contains_v || (std::is_const_v && !type_list_contains_v>), + type_list>, + type_list<>>; + + using rw = std::conditional_t< + type_list_contains_v> || (!std::is_const_v && !type_list_contains_v), + type_list, + type_list<>>; +}; + +template +struct unpack_type, type_list> { + using ro = type_list<>; + using rw = type_list<>; +}; + +template +struct unpack_type, type_list> + : unpack_type, type_list> {}; + +template +struct unpack_type, exclude_t>, type_list> { + using ro = type_list_cat_t, typename unpack_type, type_list>::ro...>; + using rw = type_list_cat_t, type_list>::rw...>; +}; + +template +struct unpack_type, exclude_t>, type_list> + : unpack_type, exclude_t>, type_list> {}; + +template +struct resource_traits; + +template +struct resource_traits, type_list> { + using args = type_list...>; + using ro = type_list_cat_t>::ro..., typename unpack_type>::ro...>; + using rw = type_list_cat_t>::rw..., typename unpack_type>::rw...>; +}; + +template +resource_traits...>, type_list> free_function_to_resource_traits(Ret (*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Utility class for creating a static task graph. + * + * This class offers minimal support (but sufficient in many cases) for creating + * an execution graph from functions and their requirements on resources.
+ * Note that the resulting tasks aren't executed in any case. This isn't the + * goal of the tool. Instead, they are returned to the user in the form of a + * graph that allows for safe execution. + * + * @tparam Registry Basic registry type. + */ +template +class basic_organizer final { + using callback_type = void(const void *, Registry &); + using prepare_type = void(Registry &); + using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); + + struct vertex_data final { + std::size_t ro_count{}; + std::size_t rw_count{}; + const char *name{}; + const void *payload{}; + callback_type *callback{}; + dependency_type *dependency; + prepare_type *prepare{}; + const type_info *info{}; + }; + + template + [[nodiscard]] static decltype(auto) extract(Registry ®) { + if constexpr(std::is_same_v) { + return reg; + } else if constexpr(internal::is_view_v) { + return as_view{reg}; + } else { + return reg.ctx().template emplace>(); + } + } + + template + [[nodiscard]] static auto to_args(Registry ®, type_list) { + return std::tuple(reg))...>(extract(reg)...); + } + + template + static std::size_t fill_dependencies(type_list, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { + if constexpr(sizeof...(Type) == 0u) { + return {}; + } else { + const type_info *info[sizeof...(Type)]{&type_id()...}; + const auto length = count < sizeof...(Type) ? count : sizeof...(Type); + + for(std::size_t pos{}; pos < length; ++pos) { + buffer[pos] = info[pos]; + } + + return length; + } + } + + template + void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { + builder.bind(static_cast(index)); + builder.set(type_hash::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); + (builder.ro(type_hash::value()), ...); + (builder.rw(type_hash::value()), ...); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Raw task function type. */ + using function_type = callback_type; + + /*! @brief Vertex type of a task graph defined as an adjacency list. */ + struct vertex { + /** + * @brief Constructs a vertex of the task graph. + * @param vtype True if the vertex is a top-level one, false otherwise. + * @param data The data associated with the vertex. + * @param edges The indices of the children in the adjacency list. + */ + vertex(const bool vtype, vertex_data data, std::vector edges) + : is_top_level{vtype}, + node{std::move(data)}, + reachable{std::move(edges)} {} + + /** + * @brief Fills a buffer with the type info objects for the writable + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(false, buffer, length); + } + + /** + * @brief Fills a buffer with the type info objects for the read-only + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(true, buffer, length); + } + + /** + * @brief Returns the number of read-only resources of a vertex. + * @return The number of read-only resources of the vertex. + */ + size_type ro_count() const noexcept { + return node.ro_count; + } + + /** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ + size_type rw_count() const noexcept { + return node.rw_count; + } + + /** + * @brief Checks if a vertex is also a top-level one. + * @return True if the vertex is a top-level one, false otherwise. + */ + bool top_level() const noexcept { + return is_top_level; + } + + /** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ + const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns a user defined name associated with a vertex, if any. + * @return The user defined name associated with the vertex, if any. + */ + const char *name() const noexcept { + return node.name; + } + + /** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ + function_type *callback() const noexcept { + return node.callback; + } + + /** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ + const void *data() const noexcept { + return node.payload; + } + + /** + * @brief Returns the list of nodes reachable from a given vertex. + * @return The list of nodes reachable from the vertex. + */ + const std::vector &children() const noexcept { + return reachable; + } + + /** + * @brief Prepares a registry and assures that all required resources + * are properly instantiated before using them. + * @param reg A valid registry. + */ + void prepare(registry_type ®) const { + node.prepare ? node.prepare(reg) : void(); + } + + private: + bool is_top_level; + vertex_data node; + std::vector reachable; + }; + + /** + * @brief Adds a free function to the task list. + * @tparam Candidate Function to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param name Optional name to associate with the task. + */ + template + void emplace(const char *name = nullptr) { + using resource_type = decltype(internal::free_function_to_resource_traits(Candidate)); + constexpr auto requires_registry = type_list_contains_v; + + callback_type *callback = +[](const void *, registry_type ®) { + std::apply(Candidate, to_args(reg, typename resource_type::args{})); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + nullptr, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds a free function with payload or a member function with an + * instance to the task list. + * @tparam Candidate Function or member to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @param name Optional name to associate with the task. + */ + template + void emplace(Type &value_or_instance, const char *name = nullptr) { + using resource_type = decltype(internal::constrained_function_to_resource_traits(Candidate)); + constexpr auto requires_registry = type_list_contains_v; + + callback_type *callback = +[](const void *payload, registry_type ®) { + Type *curr = static_cast(const_cast *>(payload)); + std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + &value_or_instance, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds an user defined function with optional payload to the task + * list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param func Function to add to the task list. + * @param payload User defined arbitrary data. + * @param name Optional name to associate with the task. + */ + template + void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { + using resource_type = internal::resource_traits, type_list>; + track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + payload, + func, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + nullptr, + &type_id()}; + + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ + std::vector graph() { + std::vector adjacency_list{}; + adjacency_list.reserve(vertices.size()); + auto adjacency_matrix = builder.graph(); + + for(auto curr: adjacency_matrix.vertices()) { + const auto iterable = adjacency_matrix.in_edges(curr); + std::vector reachable{}; + + for(auto &&edge: adjacency_matrix.out_edges(curr)) { + reachable.push_back(edge.second); + } + + adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + builder.clear(); + vertices.clear(); + } + +private: + std::vector vertices; + flow builder; +}; + +} // namespace entt + +#endif + +// #include "entity/registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#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 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 + +#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 + +#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 "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @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; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +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 {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @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 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 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 Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @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> {}; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 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 Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @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 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 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 Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @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>>; +}; + +/*! @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 Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @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; + +/** + * @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...> {}; + +/** + * @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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @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 Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 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 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 value. */ + static constexpr auto value = Value; +}; + +/** + * @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 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 {}; +} + +/*! @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<>; +}; + +/** + * @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; +}; + +/** + * @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; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @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 {}; + +/** + * @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 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; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_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 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 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 {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>, void>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @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 Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @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 {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (is_equality_comparable>::value && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { + return true; +} + +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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::bool_constant(choice<2>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @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; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @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 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"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @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 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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal + +/** + * 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; + +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; + + /** + * @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{} {} + + /** + * @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; + + /** + * @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; + + /** + * @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)} {} + + /** + * @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{}} {} + + /** + * @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; + + /** + * @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; + + /** + * @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(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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()); + } + + /** + * @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(); + } + } + + /*! @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(); + } + } +}; + +/** + * @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>; + +/** + * @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); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @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 {}; + +/** + * @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"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @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; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @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; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +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 {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @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 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 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 Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @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> {}; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 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 Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @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 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 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 Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @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>>; +}; + +/*! @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 Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @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; + +/** + * @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...> {}; + +/** + * @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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @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 Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 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 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 value. */ + static constexpr auto value = Value; +}; + +/** + * @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 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 {}; +} + +/*! @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<>; +}; + +/** + * @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; +}; + +/** + * @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; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @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 {}; + +/** + * @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 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; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_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 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 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 {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>, void>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @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 Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @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 {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (is_equality_comparable>::value && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { + return true; +} + +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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::bool_constant(choice<2>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @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; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @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 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"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @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 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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + 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 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 { + return lhs.it - rhs.it; +} + +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 { + return !(lhs == rhs); +} + +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 { + return rhs < lhs; +} + +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 { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +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 { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = typename 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()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @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. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @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 packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** @@ -22135,117 +34420,19 @@ struct uses_allocator, Allocator> // #include "../core/any.hpp" +// #include "../core/compressed_pair.hpp" + // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" +// #include "../core/memory.hpp" + // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../core/utility.hpp" -#ifndef ENTT_CORE_UTILITY_HPP -#define ENTT_CORE_UTILITY_HPP - -#include -// #include "../config/config.h" - - -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; - - /** - * @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 ENTT_NOEXCEPT { - return std::forward(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) ENTT_NOEXCEPT { - return member; -} - -/** - * @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) ENTT_NOEXCEPT { - return func; -} - -/** - * @brief Helper type for visitors. - * @tparam Func Types of function objects. - */ -template -struct overloaded: Func... { - using Func::operator()...; -}; - -/** - * @brief Deduction guide. - * @tparam Func Types of function objects. - */ -template -overloaded(Func...) -> overloaded; - -/** - * @brief Basic implementation of a y-combinator. - * @tparam Func Type of a potentially recursive function. - */ -template -struct y_combinator { - /** - * @brief Constructs a y-combinator from a given function. - * @param recursive A potentially recursive function. - */ - y_combinator(Func recursive) - : func{std::move(recursive)} {} - - /** - * @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 - decltype(auto) operator()(Args &&...args) const { - return func(*this, std::forward(args)...); - } - - /*! @copydoc operator()() */ - template - decltype(auto) operator()(Args &&...args) { - return func(*this, std::forward(args)...); - } - -private: - Func func; -}; - -} // namespace entt - -#endif // #include "component.hpp" @@ -22254,937 +34441,12 @@ private: // #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/iterator.hpp" - -// #include "../core/type_traits.hpp" - -// #include "component.hpp" - -// #include "entity.hpp" - -// #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" -// #include "utility.hpp" - - -namespace entt { - -/** - * @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 have at - * least the given components. Moreover, it's guaranteed that the entity list - * is tightly packed in memory for fast iterations. - * - * @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). - * * 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. - * - * @note - * Groups 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 all the - * groups.
- * Moreover, sorting a non-owning group affects all the instances of the same - * group (it means that users don't have to call `sort` on each instance to sort - * all of them because they _share_ entities and components). - * - * @warning - * Lifetime of a group must not overcome that of the registry that generated it. - * In any other case, attempting to use a group results in undefined behavior. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Get Type of components observed by the group. - * @tparam Exclude Types of components used to filter the group. - */ -template -class basic_group, get_t, exclude_t> { - /*! @brief A registry is allowed to create groups. */ - friend class basic_registry; - - template - using storage_type = constness_as_t>::storage_type, Comp>; - - using basic_common_type = std::common_type_t::base_type...>; - - struct extended_group_iterator final { - using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); - using pointer = input_iterator_pointer; - using reference = value_type; - using iterator_category = std::input_iterator_tag; - - extended_group_iterator() = default; - - extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *...> &args) - : it{from}, - pools{args} {} - - extended_group_iterator &operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - extended_group_iterator operator++(int) ENTT_NOEXCEPT { - extended_group_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - const auto entt = *it; - return std::tuple_cat(std::make_tuple(entt), std::get *>(pools)->get_as_tuple(entt)...); - } - - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { - return operator*(); - } - - [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return other.it == it; - } - - [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - private: - typename basic_common_type::iterator it; - std::tuple *...> pools; - }; - - basic_group(basic_common_type &ref, storage_type &...gpool) ENTT_NOEXCEPT - : handler{&ref}, - pools{&gpool...} {} - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @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; - - /*! @brief Default constructor to use to create empty, invalid groups. */ - basic_group() ENTT_NOEXCEPT - : handler{} {} - - /** - * @brief Returns a const reference to the underlying handler. - * @return A const reference to the underlying handler. - */ - const base_type &handle() const ENTT_NOEXCEPT { - return *handler; - } - - /** - * @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. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get *>(pools); - } - - /** - * @brief Returns the storage for a given component type. - * @tparam Comp Index of component of which to return the storage. - * @return The storage for the given component type. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get(pools); - } - - /** - * @brief Returns the number of entities that have the given components. - * @return Number of entities that have the given components. - */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return *this ? handler->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 ENTT_NOEXCEPT { - return *this ? handler->capacity() : size_type{}; - } - - /*! @brief Requests the removal of unused capacity. */ - void shrink_to_fit() { - if(*this) { - handler->shrink_to_fit(); - } - } - - /** - * @brief Checks whether a group is empty. - * @return True if the group is empty, false otherwise. - */ - [[nodiscard]] bool empty() const ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { - const auto it = begin(); - return it != end() ? *it : null; - } - - /** - * @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 ENTT_NOEXCEPT { - const auto it = rbegin(); - return it != rend() ? *it : null; - } - - /** - * @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 ENTT_NOEXCEPT { - const auto it = *this ? handler->find(entt) : iterator{}; - return it != end() && *it == entt ? it : 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. - */ - [[nodiscard]] entity_type operator[](const size_type pos) const { - return begin()[pos]; - } - - /** - * @brief Checks if a group is properly initialized. - * @return True if the group is properly initialized, false otherwise. - */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return handler != nullptr; - } - - /** - * @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 ENTT_NOEXCEPT { - return *this && handler->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. - * - * @tparam Comp 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 { - ENTT_ASSERT(contains(entt), "Group does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { - return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)...); - } else if constexpr(sizeof...(Comp) == 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(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 ENTT_NOEXCEPT { - return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}} - : iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, 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 Component &..., const Component &...); - * bool(const Entity, const Entity); - * @endcode - * - * Where `Component` 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 Comp 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...(Comp) == 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...(Comp) == 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)...); - } - } - } - - /** - * @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 Comp Type of component to use to impose the order. - */ - template - void sort() const { - if(*this) { - handler->respect(*std::get *>(pools)); - } - } - -private: - base_type *const handler; - const std::tuple *...> pools; -}; - -/** - * @brief Owning group. - * - * Owning groups return all entities and only the entities that have at least - * the given components. Moreover: - * - * * It's guaranteed that the entity list is tightly packed in memory for fast - * iterations. - * * It's guaranteed that the lists of owned components are tightly packed in - * memory for even faster iterations and to allow direct access. - * * They stay true to the order of the owned components and all instances have - * the same order in memory. - * - * The more types of components are owned by a group, the faster it is to - * iterate them. - * - * @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). - * * 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. - * - * @note - * Groups 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 all the - * groups. - * Moreover, sorting an owning group affects all the instance of the same group - * (it means that users don't have to call `sort` on each instance to sort all - * of them because they share the underlying data structure). - * - * @warning - * Lifetime of a group must not overcome that of the registry that generated it. - * In any other case, attempting to use a group results in undefined behavior. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Owned Types of components owned by the group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components used to filter the group. - */ -template -class basic_group, get_t, exclude_t> { - /*! @brief A registry is allowed to create groups. */ - friend class basic_registry; - - template - using storage_type = constness_as_t>::storage_type, Comp>; - - using basic_common_type = std::common_type_t::base_type..., typename storage_type::base_type...>; - - class extended_group_iterator final { - template - auto index_to_element(storage_type &cpool) const { - if constexpr(ignore_as_empty_v>) { - return std::make_tuple(); - } else { - return std::forward_as_tuple(cpool.rbegin()[it.index()]); - } - } - - public: - using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); - using pointer = input_iterator_pointer; - using reference = value_type; - using iterator_category = std::input_iterator_tag; - - extended_group_iterator() = default; - - template - extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *..., storage_type *...> &cpools) - : it{from}, - pools{cpools} {} - - extended_group_iterator &operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - extended_group_iterator operator++(int) ENTT_NOEXCEPT { - extended_group_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] reference operator*() const ENTT_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 ENTT_NOEXCEPT { - return operator*(); - } - - [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return other.it == it; - } - - [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - private: - typename basic_common_type::iterator it; - std::tuple *..., storage_type *...> pools; - }; - - basic_group(const std::size_t &extent, storage_type &...opool, storage_type &...gpool) ENTT_NOEXCEPT - : pools{&opool..., &gpool...}, - length{&extent} {} - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @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; - - /*! @brief Default constructor to use to create empty, invalid groups. */ - basic_group() ENTT_NOEXCEPT - : length{} {} - - /** - * @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. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get *>(pools); - } - - /** - * @brief Returns the storage for a given component type. - * @tparam Comp Index of component of which to return the storage. - * @return The storage for the given component type. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get(pools); - } - - /** - * @brief Returns the number of entities that have the given components. - * @return Number of entities that have the given components. - */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return *this ? *length : size_type{}; - } - - /** - * @brief Checks whether a group is empty. - * @return True if the group is empty, false otherwise. - */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - return !*this || !*length; - } - - /** - * @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 ENTT_NOEXCEPT { - return *this ? (std::get<0>(pools)->base_type::end() - *length) : 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 ENTT_NOEXCEPT { - return *this ? std::get<0>(pools)->base_type::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 ENTT_NOEXCEPT { - return *this ? std::get<0>(pools)->base_type::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 ENTT_NOEXCEPT { - return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : 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 ENTT_NOEXCEPT { - const auto it = begin(); - return it != end() ? *it : null; - } - - /** - * @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 ENTT_NOEXCEPT { - const auto it = rbegin(); - return it != rend() ? *it : null; - } - - /** - * @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 ENTT_NOEXCEPT { - const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; - return it != end() && it >= begin() && *it == entt ? it : 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. - */ - [[nodiscard]] entity_type operator[](const size_type pos) const { - return begin()[pos]; - } - - /** - * @brief Checks if a group is properly initialized. - * @return True if the group is properly initialized, false otherwise. - */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return length != nullptr; - } - - /** - * @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 ENTT_NOEXCEPT { - return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); - } - - /** - * @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 Comp 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 { - ENTT_ASSERT(contains(entt), "Group does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { - return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)..., std::get *>(pools)->get_as_tuple(entt)...); - } else if constexpr(sizeof...(Comp) == 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 ENTT_NOEXCEPT { - iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{}; - return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, 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 Component &, const Component &); - * bool(const Entity, const Entity); - * @endcode - * - * Where `Component` 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 Comp 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 { - auto *cpool = std::get<0>(pools); - - if constexpr(sizeof...(Comp) == 0) { - static_assert(std::is_invocable_v, "Invalid comparison function"); - cpool->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...(Comp) == 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)...)); - } - }; - - cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); - } - - [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), ...); - } - }(std::get *>(pools)...); - } - -private: - const std::tuple *..., storage_type *...> pools; - const size_type *const length; -}; - -} // namespace entt - -#endif - -// #include "runtime_view.hpp" -#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP -#define ENTT_ENTITY_RUNTIME_VIEW_HPP - -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "entity.hpp" - -// #include "fwd.hpp" - -// #include "sparse_set.hpp" +// #include "view.hpp" namespace entt { @@ -23196,80 +34458,203 @@ namespace entt { namespace internal { -template -class runtime_view_iterator final { - using iterator_type = typename Set::iterator; +template +class registry_storage_iterator final { + template + friend class registry_storage_iterator; - [[nodiscard]] bool valid() const { - return (!tombstone_check || *it != tombstone) - && std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) - && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); - } + using mapped_type = std::remove_reference_t()->second)>; public: - using difference_type = typename iterator_type::difference_type; - using value_type = typename iterator_type::value_type; - using pointer = typename iterator_type::pointer; - using reference = typename iterator_type::reference; - using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::pair &>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; - runtime_view_iterator() ENTT_NOEXCEPT - : pools{}, - filter{}, - it{}, - tombstone_check{} {} + constexpr registry_storage_iterator() noexcept + : it{} {} - runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) ENTT_NOEXCEPT - : pools{&cpools}, - filter{&ignore}, - it{curr}, - tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { - if(it != (*pools)[0]->end() && !valid()) { - ++(*this); - } + constexpr registry_storage_iterator(It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr registry_storage_iterator(const registry_storage_iterator &other) noexcept + : registry_storage_iterator{other.it} {} + + constexpr registry_storage_iterator &operator++() noexcept { + return ++it, *this; } - runtime_view_iterator &operator++() { - while(++it != (*pools)[0]->end() && !valid()) {} - return *this; - } - - runtime_view_iterator operator++(int) { - runtime_view_iterator orig = *this; + constexpr registry_storage_iterator operator++(int) noexcept { + registry_storage_iterator orig = *this; return ++(*this), orig; } - runtime_view_iterator &operator--() { - while(--it != (*pools)[0]->begin() && !valid()) {} - return *this; + constexpr registry_storage_iterator &operator--() noexcept { + return --it, *this; } - runtime_view_iterator operator--(int) { - runtime_view_iterator orig = *this; + constexpr registry_storage_iterator operator--(int) noexcept { + registry_storage_iterator orig = *this; return operator--(), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { - return it.operator->(); + constexpr registry_storage_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return *operator->(); + constexpr registry_storage_iterator operator+(const difference_type value) const noexcept { + registry_storage_iterator copy = *this; + return (copy += value); } - [[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT { - return it == other.it; + constexpr registry_storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); } - [[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); + constexpr registry_storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, *it[value].second}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, *it->second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + 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; + +private: + It it; +}; + +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 { + return lhs.it == rhs.it; +} + +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 { + return lhs.it < rhs.it; +} + +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 { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +class registry_context { + using key_type = id_type; + using mapped_type = basic_any<0u>; + using container_type = dense_map; + +public: + template + [[deprecated("Use ::emplace_as instead")]] Type &emplace_hint(const id_type id, Args &&...args) { + return emplace_as(id, std::forward(args)...); + } + + template + Type &emplace_as(const id_type id, Args &&...args) { + return any_cast(ctx.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); + } + + template + Type &emplace(Args &&...args) { + return emplace_as(type_id().hash(), std::forward(args)...); + } + + template + Type &insert_or_assign(const id_type id, Type &&value) { + return any_cast> &>(ctx.insert_or_assign(id, std::forward(value)).first->second); + } + + template + Type &insert_or_assign(Type &&value) { + return insert_or_assign(type_id().hash(), std::forward(value)); + } + + template + bool erase(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + 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)); + } + + template + [[nodiscard]] Type &get(const id_type id = type_id().hash()) { + return any_cast(ctx.at(id)); + } + + template + [[nodiscard]] const Type *find(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] Type *find(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() && it->second.type() == type_id(); } private: - const std::vector *pools; - const std::vector *filter; - iterator_type it; - bool tombstone_check; + container_type ctx; }; } // namespace internal @@ -23280,1228 +34665,19 @@ private: */ /** - * @brief Runtime view implementation. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. - */ -template -struct basic_runtime_view; - -/** - * @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. - * - * @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). - * * 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. - * + * @brief Fast and reliable entity-component system. * @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 basic_runtime_view> { - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Common type among all storage types. */ - using base_type = basic_sparse_set; - /*! @brief Bidirectional iterator type. */ - using iterator = internal::runtime_view_iterator; - - /*! @brief Default constructor to use to create empty, invalid views. */ - basic_runtime_view() ENTT_NOEXCEPT - : pools{}, - filter{} {} - - /** - * @brief Appends an opaque storage object to a runtime view. - * @param base An opaque reference to a storage object. - * @return This runtime view. - */ - basic_runtime_view &iterate(const base_type &base) { - if(pools.empty() || !(base.size() < pools[0u]->size())) { - pools.push_back(&base); - } else { - pools.push_back(std::exchange(pools[0u], &base)); - } - - return *this; - } - - /** - * @brief Adds an opaque storage object as a filter of a runtime view. - * @param base An opaque reference to a storage object. - * @return This runtime view. - */ - basic_runtime_view &exclude(const base_type &base) { - filter.push_back(&base); - return *this; - } - - /** - * @brief Estimates the number of entities iterated by the view. - * @return Estimated number of entities iterated by the view. - */ - [[nodiscard]] size_type size_hint() const { - return pools.empty() ? size_type{} : pools.front()->size(); - } - - /** - * @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()`. - * - * @return An iterator to the first entity that has the given components. - */ - [[nodiscard]] iterator begin() const { - return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()}; - } - - /** - * @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. - */ - [[nodiscard]] iterator end() const { - return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()}; - } - - /** - * @brief Checks if a view contains an entity. - * @param entt A valid identifier. - * @return True if the view contains the given entity, false otherwise. - */ - [[nodiscard]] bool contains(const entity_type entt) const { - return !pools.empty() - && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) - && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); - } - - /** - * @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 signature of the function should be equivalent to the following: - * - * @code{.cpp} - * void(const entity_type); - * @endcode - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - for(const auto entity: *this) { - func(entity); - } - } - -private: - std::vector pools; - std::vector filter; -}; - -} // namespace entt - -#endif - -// #include "sparse_set.hpp" - -// #include "storage.hpp" - -// #include "utility.hpp" - -// #include "view.hpp" -#ifndef ENTT_ENTITY_VIEW_HPP -#define ENTT_ENTITY_VIEW_HPP - -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/iterator.hpp" - -// #include "../core/type_traits.hpp" - -// #include "component.hpp" - -// #include "entity.hpp" - -// #include "fwd.hpp" - -// #include "sparse_set.hpp" - -// #include "storage.hpp" - -// #include "utility.hpp" - - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -class view_iterator final { - using iterator_type = typename Type::const_iterator; - - [[nodiscard]] bool valid() const ENTT_NOEXCEPT { - return ((Component != 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); - } - -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; - - view_iterator() ENTT_NOEXCEPT = default; - - view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) ENTT_NOEXCEPT - : it{curr}, - last{to}, - pools{all_of}, - filter{none_of} { - if(it != last && !valid()) { - ++(*this); - } - } - - view_iterator &operator++() ENTT_NOEXCEPT { - while(++it != last && !valid()) {} - return *this; - } - - view_iterator operator++(int) ENTT_NOEXCEPT { - view_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { - return &*it; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return *operator->(); - } - - template - friend bool operator==(const view_iterator &, const view_iterator &) ENTT_NOEXCEPT; - -private: - iterator_type it; - iterator_type last; - std::array pools; - std::array filter; -}; - -template -[[nodiscard]] bool operator==(const view_iterator &lhs, const view_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it == rhs.it; -} - -template -[[nodiscard]] bool operator!=(const view_iterator &lhs, const view_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -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; - - extended_view_iterator() = default; - - extended_view_iterator(It from, std::tuple storage) - : it{from}, - pools{storage} {} - - extended_view_iterator &operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - extended_view_iterator operator++(int) ENTT_NOEXCEPT { - extended_view_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - 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 ENTT_NOEXCEPT { - return operator*(); - } - - template - friend bool operator==(const extended_view_iterator &, const extended_view_iterator &) ENTT_NOEXCEPT; - -private: - It it; - std::tuple pools; -}; - -template -[[nodiscard]] bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it == rhs.it; -} - -template -[[nodiscard]] bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @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_view; - -/** - * @brief Multi component view. - * - * Multi component views iterate over those entities that have at least all the - * given components in their bags. 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 iterate. - * - * @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). - * * 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 Entity A valid entity type (see entt_traits for more details). - * @tparam Component Types of components iterated by the view. - * @tparam Exclude Types of components used to filter the view. - */ -template -class basic_view, exclude_t> { - template - friend class basic_view; - - template - using storage_type = constness_as_t>::storage_type, Comp>; - - template - [[nodiscard]] auto pools_to_array(std::index_sequence) const ENTT_NOEXCEPT { - std::size_t pos{}; - std::array other{}; - (static_cast(std::get(pools) == view ? void() : void(other[pos++] = std::get(pools))), ...); - return other; - } - - template - [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { - if constexpr(Comp == 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()) { - const auto entt = std::get<0>(curr); - - if(((sizeof...(Component) != 1u) || (entt != tombstone)) - && ((Comp == Index || std::get(pools)->contains(entt)) && ...) - && std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { - 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(std::move(func), seq) : void()), ...); - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Common type among all storage types. */ - using base_type = std::common_type_t::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 views. */ - basic_view() ENTT_NOEXCEPT - : pools{}, - filter{}, - view{} {} - - /** - * @brief Constructs a multi-type view from a set of storage classes. - * @param component The storage for the types to iterate. - * @param epool The storage for the types used to filter the view. - */ - basic_view(storage_type &...component, const storage_type &...epool) ENTT_NOEXCEPT - : pools{&component...}, - filter{&epool...}, - view{(std::min)({&static_cast(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {} - - /** - * @brief Creates a new view driven by a given component in its iterations. - * @tparam Comp Type of component used to drive the iteration. - * @return A new view driven by the given component in its iterations. - */ - template - [[nodiscard]] basic_view use() const ENTT_NOEXCEPT { - basic_view other{*this}; - other.view = std::get *>(pools); - return other; - } - - /** - * @brief Creates a new view driven by a given component in its iterations. - * @tparam Comp Index of the component used to drive the iteration. - * @return A new view driven by the given component in its iterations. - */ - template - [[nodiscard]] basic_view use() const ENTT_NOEXCEPT { - basic_view other{*this}; - other.view = std::get(pools); - return other; - } - - /** - * @brief Returns the leading storage of a view. - * @return The leading storage of the view. - */ - const base_type &handle() const ENTT_NOEXCEPT { - return *view; - } - - /** - * @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. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get *>(pools); - } - - /** - * @brief Returns the storage for a given component type. - * @tparam Comp Index of component of which to return the storage. - * @return The storage for the given component type. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get(pools); - } - - /** - * @brief Estimates the number of entities iterated by the view. - * @return Estimated number of entities iterated by the view. - */ - [[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT { - return view->size(); - } - - /** - * @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 ENTT_NOEXCEPT { - return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; - } - - /** - * @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 ENTT_NOEXCEPT { - return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; - } - - /** - * @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 ENTT_NOEXCEPT { - const auto it = begin(); - return it != end() ? *it : null; - } - - /** - * @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 ENTT_NOEXCEPT { - auto it = view->rbegin(); - for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} - return it == view->rend() ? null : *it; - } - - /** - * @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 ENTT_NOEXCEPT { - return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for{}), filter} : end(); - } - - /** - * @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 Checks if a view is properly initialized. - * @return True if the view is properly initialized, false otherwise. - */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return view != nullptr; - } - - /** - * @brief Checks if a view contains an entity. - * @param entt A valid identifier. - * @return True if the view contains the given entity, false otherwise. - */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) - && std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); - } - - /** - * @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 Comp 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 { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { - return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); - } else if constexpr(sizeof...(Comp) == 1) { - return (std::get *>(pools)->get(entt), ...); - } else { - return std::tuple_cat(std::get *>(pools)->get_as_tuple(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. - * - * @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. - */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - - if constexpr(sizeof...(Other) == 0) { - return std::get(pools)->get(entt); - } else { - return std::tuple_cat(std::get(pools)->get_as_tuple(entt), 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 - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - pick_and_each(std::move(func), std::index_sequence_for{}); - } - - /** - * @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. - */ - [[nodiscard]] iterable each() const ENTT_NOEXCEPT { - return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; - } - - /** - * @brief Combines two views in a _more specific_ one (friend function). - * @tparam Get Component list of the view to combine with. - * @tparam Excl 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 ENTT_NOEXCEPT { - using view_type = basic_view, exclude_t>; - return std::make_from_tuple(std::tuple_cat( - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools), - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), - std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, filter), - std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); - } - -private: - std::tuple *...> pools; - std::array filter; - const base_type *view; -}; - -/** - * @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 - * - * Iterators aren't invalidated if: - * - * * New instances of the given component are created and assigned to entities. - * * The entity currently pointed is modified (as an example, the given - * component is removed from the entity to which the iterator points). - * * 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. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Component Type of component iterated by the view. - */ -template -class basic_view, exclude_t<>, std::void_t>::in_place_delete>>> { - template - friend class basic_view; - - using storage_type = constness_as_t>::storage_type, Component>; - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Common type among all storage types. */ - using base_type = typename storage_type::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()); - - /*! @brief Default constructor to use to create empty, invalid views. */ - basic_view() ENTT_NOEXCEPT - : pools{}, - filter{}, - view{} {} - - /** - * @brief Constructs a single-type view from a storage class. - * @param ref The storage for the type to iterate. - */ - basic_view(storage_type &ref) ENTT_NOEXCEPT - : pools{&ref}, - filter{}, - view{&ref} {} - - /** - * @brief Returns the leading storage of a view. - * @return The leading storage of the view. - */ - const base_type &handle() const ENTT_NOEXCEPT { - return *view; - } - - /** - * @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. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - static_assert(std::is_same_v, "Invalid component type"); - return *std::get<0>(pools); - } - - /** - * @brief Returns the storage for a given component type. - * @tparam Comp Index of component of which to return the storage. - * @return The storage for the given component type. - */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get(pools); - } - - /** - * @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 ENTT_NOEXCEPT { - return view->size(); - } - - /** - * @brief Checks whether a view is empty. - * @return True if the view is empty, false otherwise. - */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - return view->empty(); - } - - /** - * @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 ENTT_NOEXCEPT { - return view->begin(); - } - - /** - * @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 ENTT_NOEXCEPT { - return view->end(); - } - - /** - * @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 ENTT_NOEXCEPT { - return view->rbegin(); - } - - /** - * @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 ENTT_NOEXCEPT { - return view->rend(); - } - - /** - * @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 ENTT_NOEXCEPT { - return empty() ? null : *begin(); - } - - /** - * @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 ENTT_NOEXCEPT { - return empty() ? null : *rbegin(); - } - - /** - * @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 ENTT_NOEXCEPT { - return contains(entt) ? view->find(entt) : 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. - */ - [[nodiscard]] entity_type operator[](const size_type pos) const { - return begin()[pos]; - } - - /** - * @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 get(entt); - } - - /** - * @brief Checks if a view is properly initialized. - * @return True if the view is properly initialized, false otherwise. - */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return view != nullptr; - } - - /** - * @brief Checks if a view contains an entity. - * @param entt A valid identifier. - * @return True if the view contains the given entity, false otherwise. - */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return view->contains(entt); - } - - /** - * @brief Returns the component assigned to the given entity. - * - * @warning - * Attempting to use an entity that doesn't belong to the view results in - * undefined behavior. - * - * @tparam Comp Type or index of the component to get. - * @param entt A valid identifier. - * @return The component assigned to the entity. - */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { - return std::get<0>(pools)->get_as_tuple(entt); - } else { - static_assert(std::is_same_v, "Invalid component type"); - return std::get<0>(pools)->get(entt); - } - } - - /*! @copydoc get */ - template - [[nodiscard]] decltype(auto) get(const entity_type entt) const { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - return std::get<0>(pools)->get(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 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, Component &); - * void(Component &); - * @endcode - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - if constexpr(is_applicable_v) { - for(const auto pack: each()) { - std::apply(func, pack); - } - } else if constexpr(std::is_invocable_v) { - for(auto &&component: *std::get<0>(pools)) { - func(component); - } - } else if constexpr(std::is_invocable_v) { - for(auto entity: *view) { - func(entity); - } - } else { - for(size_type pos{}, last = size(); pos < last; ++pos) { - func(); - } - } - } - - /** - * @brief Returns an iterable object to use to _visit_ a view. - * - * 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 view. - */ - [[nodiscard]] iterable each() const ENTT_NOEXCEPT { - return std::get<0>(pools)->each(); - } - - /** - * @brief Combines two views in a _more specific_ one (friend function). - * @tparam Get Component list of the view to combine with. - * @tparam Excl 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 ENTT_NOEXCEPT { - using view_type = basic_view, exclude_t>; - return std::make_from_tuple(std::tuple_cat( - std::forward_as_tuple(*std::get<0>(pools)), - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), - std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); - } - -private: - std::tuple pools; - std::array filter; - const base_type *view; -}; - -/** - * @brief Deduction guide. - * @tparam Storage Type of storage classes used to create the view. - * @param storage The storage for the types to iterate. - */ -template -basic_view(Storage &...storage) -> basic_view, get_t...>, exclude_t<>>; - -} // namespace entt - -#endif - - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -class storage_proxy_iterator final { - template - friend class storage_proxy_iterator; - - using mapped_type = std::remove_reference_t()->second)>; - -public: - using value_type = std::pair &>; - using pointer = input_iterator_pointer; - using reference = value_type; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; - - storage_proxy_iterator() ENTT_NOEXCEPT - : it{} {} - - storage_proxy_iterator(const It iter) ENTT_NOEXCEPT - : it{iter} {} - - template && std::is_constructible_v>> - storage_proxy_iterator(const storage_proxy_iterator &other) ENTT_NOEXCEPT - : it{other.it} {} - - storage_proxy_iterator &operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - storage_proxy_iterator operator++(int) ENTT_NOEXCEPT { - storage_proxy_iterator orig = *this; - return ++(*this), orig; - } - - storage_proxy_iterator &operator--() ENTT_NOEXCEPT { - return --it, *this; - } - - storage_proxy_iterator operator--(int) ENTT_NOEXCEPT { - storage_proxy_iterator orig = *this; - return operator--(), orig; - } - - storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { - it += value; - return *this; - } - - storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { - storage_proxy_iterator copy = *this; - return (copy += value); - } - - storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { - return (*this += -value); - } - - storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { - return (*this + -value); - } - - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { - return {it[value].first, *it[value].second}; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return {it->first, *it->second}; - } - - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { - return operator*(); - } - - template - friend std::ptrdiff_t operator-(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; - - template - friend bool operator==(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; - - template - friend bool operator<(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; - -private: - It it; -}; - -template -[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it - rhs.it; -} - -template -[[nodiscard]] bool operator==(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it == rhs.it; -} - -template -[[nodiscard]] bool operator!=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -template -[[nodiscard]] bool operator<(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it < rhs.it; -} - -template -[[nodiscard]] bool operator>(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return rhs < lhs; -} - -template -[[nodiscard]] bool operator<=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs > rhs); -} - -template -[[nodiscard]] bool operator>=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs < rhs); -} - -struct registry_context { - template - Type &emplace_hint(const id_type id, Args &&...args) { - return any_cast(data.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); - } - - template - Type &emplace(Args &&...args) { - return emplace_hint(type_id().hash(), std::forward(args)...); - } - - template - bool erase(const id_type id = type_id().hash()) { - const auto it = data.find(id); - return it != data.end() && it->second.type() == type_id() ? (data.erase(it), true) : false; - } - - template - [[nodiscard]] std::add_const_t &at(const id_type id = type_id().hash()) const { - return any_cast &>(data.at(id)); - } - - template - [[nodiscard]] Type &at(const id_type id = type_id().hash()) { - return any_cast(data.at(id)); - } - - template - [[nodiscard]] std::add_const_t *find(const id_type id = type_id().hash()) const { - const auto it = data.find(id); - return it != data.cend() ? any_cast>(&it->second) : nullptr; - } - - template - [[nodiscard]] Type *find(const id_type id = type_id().hash()) { - const auto it = data.find(id); - return it != data.end() ? any_cast(&it->second) : nullptr; - } - - template - [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { - const auto it = data.find(id); - return it != data.end() && it->second.type() == type_id(); - } - -private: - dense_map, identity> data; -}; - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Fast and reliable entity-component system. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -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; - using basic_common_type = basic_sparse_set; - template - using storage_type = typename storage_traits::storage_type; + template + using storage_for_type = typename storage_for>>::type; template struct group_handler; @@ -24510,15 +34686,20 @@ class basic_registry { 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"); - std::conditional_t current{}; + using value_type = std::conditional_t; + value_type current{}; - template + template + group_handler(Args &&...args) + : current{std::forward(args)...} {} + + template void maybe_valid_if(basic_registry &owner, const Entity entt) { [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); - 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)) && ...); + 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 constexpr(sizeof...(Owned) == 0) { if(is_valid && !current.contains(entt)) { @@ -24527,7 +34708,7 @@ class basic_registry { } 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), ...); + (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); } } } @@ -24538,7 +34719,7 @@ class basic_registry { } 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), ...); + (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); } } } @@ -24546,59 +34727,67 @@ class basic_registry { struct group_data { std::size_t size; - std::unique_ptr group; - bool (*owned)(const id_type) ENTT_NOEXCEPT; - bool (*get)(const id_type) ENTT_NOEXCEPT; - bool (*exclude)(const id_type) ENTT_NOEXCEPT; + 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]; + 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.reset(new storage_type{}); + 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"); + 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); + ENTT_ASSERT(it->second->type() == type_id(), "Unexpected type"); + return static_cast &>(*it->second); } - static storage_type placeholder{}; + static storage_for_type placeholder{}; return placeholder; } - auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { - ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available"); + 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), {}); } - auto recycle_identifier() ENTT_NOEXCEPT { + 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(entities[curr]), tombstone); - return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr]))); + 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]))); } - auto release_entity(const Entity entity, const typename entity_traits::version_type version) { + 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)); - entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); - free_list = entity_traits::combine(entity_traits::to_integral(entity), 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; } + void rebind() { + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + } + public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Underlying version type. */ @@ -24612,22 +34801,26 @@ public: /*! @brief Default constructor. */ basic_registry() - : pools{}, - groups{}, - entities{}, - free_list{tombstone}, - vars{} {} + : basic_registry{allocator_type{}} {} + + /** + * @brief Constructs an empty registry with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_registry(const allocator_type &allocator) + : basic_registry{0u, allocator} {} /** * @brief Allocates enough memory upon construction to store `count` pools. * @param count The number of pools to allocate memory for. + * @param allocator The allocator to use. */ - basic_registry(const size_type count) - : pools{}, - groups{}, - entities{}, + basic_registry(const size_type count, const allocator_type &allocator = allocator_type{}) + : vars{}, free_list{tombstone}, - vars{} { + epool{allocator}, + pools{allocator}, + groups{allocator} { pools.reserve(count); } @@ -24635,15 +34828,13 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_registry(basic_registry &&other) - : pools{std::move(other.pools)}, - groups{std::move(other.groups)}, - entities{std::move(other.entities)}, - free_list{other.free_list}, - vars{std::move(other.vars)} { - for(auto &&curr: pools) { - curr.second->bind(forward_as_any(*this)); - } + 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)} { + rebind(); } /** @@ -24651,20 +34842,42 @@ public: * @param other The instance to move from. * @return This registry. */ - basic_registry &operator=(basic_registry &&other) { + 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); - free_list = other.free_list; - vars = std::move(other.vars); - for(auto &&curr: pools) { - curr.second->bind(forward_as_any(*this)); - } + rebind(); return *this; } + /** + * @brief Exchanges the contents with those of a given registry. + * @param other Registry to exchange the content with. + */ + 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); + + rebind(); + other.rebind(); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return epool.get_allocator(); + } + /** * @brief Returns an iterable object to use to _visit_ a registry. * @@ -24673,48 +34886,43 @@ public: * * @return An iterable object to use to _visit_ the registry. */ - [[nodiscard]] auto storage() ENTT_NOEXCEPT { - return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}}; + [[nodiscard]] auto storage() noexcept { + return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}}; } /*! @copydoc storage */ - [[nodiscard]] auto storage() const ENTT_NOEXCEPT { - return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}}; + [[nodiscard]] auto storage() const noexcept { + return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}}; } /** * @brief Finds the storage associated with a given name, if any. * @param id Name used to map the storage within the registry. - * @return An iterator to the given storage if it's found, past the end - * iterator otherwise. + * @return A pointer to the storage if it exists, a null pointer otherwise. */ - [[nodiscard]] auto storage(const id_type id) { - return internal::storage_proxy_iterator{pools.find(id)}; + [[nodiscard]] base_type *storage(const id_type id) { + return const_cast(std::as_const(*this).storage(id)); } /** * @brief Finds the storage associated with a given name, if any. * @param id Name used to map the storage within the registry. - * @return An iterator to the given storage if it's found, past the end - * iterator otherwise. + * @return A pointer to the storage if it exists, a null pointer otherwise. */ - [[nodiscard]] auto storage(const id_type id) const { - return internal::storage_proxy_iterator{pools.find(id)}; + [[nodiscard]] const base_type *storage(const id_type id) const { + const auto it = pools.find(id); + return it == pools.cend() ? nullptr : it->second.get(); } /** * @brief Returns the storage for a given component type. - * @tparam Component Type of component of which to return the storage. + * @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()) { - if constexpr(std::is_const_v) { - return std::as_const(*this).template storage>(id); - } else { - return assure(id); - } + template + decltype(auto) storage(const id_type id = type_hash::value()) { + return assure(id); } /** @@ -24724,21 +34932,21 @@ public: * If a storage for the given component doesn't exist yet, a temporary * placeholder is returned instead. * - * @tparam Component Type of component of which to return the storage. + * @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 { - return assure>(id); + template + decltype(auto) storage(const id_type id = type_hash::value()) const { + return assure(id); } /** * @brief Returns the number of entities created so far. * @return Number of entities created so far. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return entities.size(); + [[nodiscard]] size_type size() const noexcept { + return epool.size(); } /** @@ -24746,10 +34954,10 @@ public: * @return Number of entities still in use. */ [[nodiscard]] size_type alive() const { - auto sz = entities.size(); + auto sz = epool.size(); for(auto curr = free_list; curr != null; --sz) { - curr = entities[entity_traits::to_entity(curr)]; + curr = epool[entity_traits::to_entity(curr)]; } return sz; @@ -24760,7 +34968,7 @@ public: * @param cap Desired capacity. */ void reserve(const size_type cap) { - entities.reserve(cap); + epool.reserve(cap); } /** @@ -24768,8 +34976,8 @@ public: * allocated space for. * @return Capacity of the registry. */ - [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { - return entities.capacity(); + [[nodiscard]] size_type capacity() const noexcept { + return epool.capacity(); } /** @@ -24792,8 +35000,8 @@ public: * * @return A pointer to the array of entities. */ - [[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT { - return entities.data(); + [[nodiscard]] const entity_type *data() const noexcept { + return epool.data(); } /** @@ -24804,29 +35012,29 @@ public: * * @return The head of the list of released entities. */ - [[nodiscard]] entity_type released() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type released() const noexcept { return free_list; } /** * @brief Checks if an identifier refers to a valid entity. - * @param entity An identifier, either valid or not. + * @param entt An identifier, either valid or not. * @return True if the identifier is valid, false otherwise. */ - [[nodiscard]] bool valid(const entity_type entity) const { - const auto pos = size_type(entity_traits::to_entity(entity)); - return (pos < entities.size() && entities[pos] == entity); + [[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); } /** * @brief Returns the actual version for an identifier. - * @param entity A valid identifier. + * @param entt A valid identifier. * @return The version for the given identifier if valid, the tombstone * version otherwise. */ - [[nodiscard]] version_type current(const entity_type entity) const { - const auto pos = size_type(entity_traits::to_entity(entity)); - return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone); + [[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); } /** @@ -24834,7 +35042,7 @@ public: * @return A valid identifier. */ [[nodiscard]] entity_type create() { - return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier(); + return (free_list == null) ? epool.emplace_back(generate_identifier(epool.size())) : recycle_identifier(); } /** @@ -24847,25 +35055,25 @@ public: * @return A valid identifier. */ [[nodiscard]] entity_type create(const entity_type hint) { - const auto length = entities.size(); + const auto length = epool.size(); if(hint == null || hint == tombstone) { return create(); } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { - entities.resize(size_type(req) + 1u, null); + epool.resize(size_type(req) + 1u, null); for(auto pos = length; pos < req; ++pos) { release_entity(generate_identifier(pos), {}); } - return (entities[req] = hint); - } else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) { + 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 = &entities[entity_traits::to_entity(*it)]) {} + 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 (entities[req] = hint); + return (epool[req] = hint); } } @@ -24884,11 +35092,11 @@ public: *first = recycle_identifier(); } - const auto length = entities.size(); - entities.resize(length + std::distance(first, last), null); + const auto length = epool.size(); + epool.resize(length + std::distance(first, last), null); for(auto pos = length; first != last; ++first, ++pos) { - *first = entities[pos] = generate_identifier(pos); + *first = epool[pos] = generate_identifier(pos); } } @@ -24896,7 +35104,7 @@ public: * @brief Assigns identifiers to an empty registry. * * This function is intended for use in conjunction with `data`, `size` and - * `destroyed`.
+ * `released`.
* Don't try to inject ranges of randomly generated entities nor the _wrong_ * head for the list of destroyed entities. There is no guarantee that a * registry will continue to work properly in this case. @@ -24912,7 +35120,7 @@ public: template void assign(It first, It last, const entity_type destroyed) { ENTT_ASSERT(!alive(), "Entities still alive"); - entities.assign(first, last); + epool.assign(first, last); free_list = destroyed; } @@ -24924,11 +35132,11 @@ public: * @warning * Attempting to use an invalid entity results in undefined behavior. * - * @param entity A valid identifier. + * @param entt A valid identifier. * @return The version of the recycled entity. */ - version_type release(const entity_type entity) { - return release(entity, static_cast(entity_traits::to_version(entity) + 1u)); + version_type release(const entity_type entt) { + return release(entt, static_cast(entity_traits::to_version(entt) + 1u)); } /** @@ -24939,13 +35147,14 @@ public: * * @sa release * - * @param entity A valid identifier. + * @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 entity, const version_type version) { - ENTT_ASSERT(orphan(entity), "Non-orphan entity"); - return release_entity(entity, version); + 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); } /** @@ -24974,11 +35183,11 @@ public: * result in undefined behavior. Attempting to use an invalid entity results * in undefined behavior. * - * @param entity A valid identifier. + * @param entt A valid identifier. * @return The version of the recycled entity. */ - version_type destroy(const entity_type entity) { - return destroy(entity, static_cast(entity_traits::to_version(entity) + 1u)); + version_type destroy(const entity_type entt) { + return destroy(entt, static_cast(entity_traits::to_version(entt) + 1u)); } /** @@ -24989,18 +35198,16 @@ public: * * @sa destroy * - * @param entity A valid identifier. + * @param entt A valid identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ - version_type destroy(const entity_type entity, const version_type version) { - ENTT_ASSERT(valid(entity), "Invalid 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(entity); + pools.begin()[pos - 1u].second->remove(entt); } - return release_entity(entity, version); + return release(entt, version); } /** @@ -25025,19 +35232,18 @@ public: * The component must have a proper constructor or be of aggregate type. * * @warning - * Attempting to use an invalid entity or to assign a component to an entity - * that already owns it results in undefined behavior. + * Attempting to assign a component to an entity that already owns it + * results in undefined behavior. * - * @tparam Component Type of component to create. + * @tparam Type Type of component to create. * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. + * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ - template - decltype(auto) emplace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure().emplace(entity, std::forward(args)...); + template + decltype(auto) emplace(const entity_type entt, Args &&...args) { + return assure().emplace(entt, std::forward(args)...); } /** @@ -25045,16 +35251,15 @@ public: * * @sa emplace * - * @tparam Component Type of component to create. + * @tparam Type Type of component to create. * @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 component to assign. */ - template - void insert(It first, It last, const Component &value = {}) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure().insert(first, last, value); + template + void insert(It first, It last, const Type &value = {}) { + assure().insert(first, last, value); } /** @@ -25062,39 +35267,37 @@ public: * * @sa emplace * - * @tparam Component Type of component to create. + * @tparam Type Type of component to create. * @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 components. */ - template::value_type, Component>>> + template::value_type, Type>>> void insert(EIt first, EIt last, CIt from) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure().insert(first, last, from); + assure().insert(first, last, from); } /** * @brief Assigns or replaces the given component for an entity. * - * @warning - * Attempting to use an invalid entity results in undefined behavior. + * @sa emplace + * @sa replace * - * @tparam Component Type of component to assign or replace. + * @tparam Type Type of component to assign or replace. * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. + * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ - template - decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - auto &cpool = assure(); - - return cpool.contains(entity) - ? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }) - : cpool.emplace(entity, std::forward(args)...); + template + decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) { + if(auto &cpool = assure(); cpool.contains(entt)) { + return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); + } else { + return cpool.emplace(entt, std::forward(args)...); + } } /** @@ -25103,7 +35306,7 @@ public: * The signature of the function should be equivalent to the following: * * @code{.cpp} - * void(Component &); + * void(Type &); * @endcode * * @note @@ -25112,19 +35315,18 @@ public: * for them. * * @warning - * Attempting to use an invalid entity or to patch a component of an entity - * that doesn't own it results in undefined behavior. + * Attempting to to patch a component of an entity that doesn't own it + * results in undefined behavior. * - * @tparam Component Type of component to patch. + * @tparam Type Type of component to patch. * @tparam Func Types of the function objects to invoke. - * @param entity A valid identifier. + * @param entt A valid identifier. * @param func Valid function objects. * @return A reference to the patched component. */ - template - decltype(auto) patch(const entity_type entity, Func &&...func) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure().patch(entity, std::forward(func)...); + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + return assure().patch(entt, std::forward(func)...); } /** @@ -25133,36 +35335,31 @@ public: * The component must have a proper constructor or be of aggregate type. * * @warning - * Attempting to use an invalid entity or to replace a component of an - * entity that doesn't own it results in undefined behavior. + * Attempting to replace a component of an entity that doesn't own it + * results in undefined behavior. * - * @tparam Component Type of component to replace. + * @tparam Type Type of component to replace. * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. + * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return A reference to the component being replaced. */ - template - decltype(auto) replace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }); + template + decltype(auto) replace(const entity_type entt, Args &&...args) { + return patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); } /** * @brief Removes the given components from an entity. * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Type of component to remove. + * @tparam Type Type of component to remove. * @tparam Other Other types of components to remove. - * @param entity A valid identifier. + * @param entt A valid identifier. * @return The number of components actually removed. */ - template - size_type remove(const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return (assure().remove(entity) + ... + assure().remove(entity)); + template + size_type remove(const entity_type entt) { + return (assure().remove(entt) + ... + assure().remove(entt)); } /** @@ -25170,23 +35367,21 @@ public: * * @sa remove * - * @tparam Component Type of component to remove. + * @tparam Type Type of component to remove. * @tparam Other Other types of components to remove. * @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. * @return The number of components actually removed. */ - template + template size_type remove(It first, It last) { if constexpr(sizeof...(Other) == 0u) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - return assure().remove(std::move(first), std::move(last)); + return assure().remove(std::move(first), std::move(last)); } else { size_type count{}; - for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { - ENTT_ASSERT(valid(*first), "Invalid entity"); + 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); } @@ -25198,17 +35393,16 @@ public: * @brief Erases the given components from an entity. * * @warning - * Attempting to use an invalid entity or to erase a component from an - * entity that doesn't own it results in undefined behavior. + * Attempting to erase a component from an entity that doesn't own it + * results in undefined behavior. * - * @tparam Component Types of components to erase. + * @tparam Type Types of components to erase. * @tparam Other Other types of components to erase. - * @param entity A valid identifier. + * @param entt A valid identifier. */ - template - void erase(const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - (assure().erase(entity), (assure().erase(entity), ...)); + template + void erase(const entity_type entt) { + (assure().erase(entt), (assure().erase(entt), ...)); } /** @@ -25216,20 +35410,18 @@ public: * * @sa erase * - * @tparam Component Types of components to erase. + * @tparam Type Types of components to erase. * @tparam Other Other types of components to erase. * @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 + template void erase(It first, It last) { if constexpr(sizeof...(Other) == 0u) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure().erase(std::move(first), std::move(last)); + assure().erase(std::move(first), std::move(last)); } else { - for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { - ENTT_ASSERT(valid(*first), "Invalid entity"); + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); } } @@ -25238,3527 +35430,70 @@ public: /** * @brief Removes all tombstones from a registry or only the pools for the * given components. - * @tparam Component Types of components for which to clear all tombstones. + * @tparam Type Types of components for which to clear all tombstones. */ - template - void compact() { - if constexpr(sizeof...(Component) == 0) { - for(auto &&curr: pools) { - curr.second->compact(); - } - } else { - (assure().compact(), ...); - } - } - - /** - * @brief Checks if an entity has all the given components. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Components for which to perform the check. - * @param entity A valid identifier. - * @return True if the entity has all the components, false otherwise. - */ - template - [[nodiscard]] bool all_of(const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return (assure>().contains(entity) && ...); - } - - /** - * @brief Checks if an entity has at least one of the given components. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Components for which to perform the check. - * @param entity A valid identifier. - * @return True if the entity has at least one of the given components, - * false otherwise. - */ - template - [[nodiscard]] bool any_of(const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return (assure>().contains(entity) || ...); - } - - /** - * @brief Returns references to the given components for an entity. - * - * @warning - * Attempting to use an invalid entity or to get a component from an entity - * that doesn't own it results in undefined behavior. - * - * @tparam Component Types of components to get. - * @param entity A valid identifier. - * @return References to the components owned by the entity. - */ - template - [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return view().template get(entity); - } - - /*! @copydoc get */ - template - [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return view().template get(entity); - } - - /** - * @brief Returns a reference to the given component for an entity. - * - * In case the entity doesn't own the component, the parameters provided are - * used to construct it. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Type of component to get. - * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. - * @param args Parameters to use to initialize the component. - * @return Reference to the component owned by the entity. - */ - template - [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - auto &cpool = assure(); - return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward(args)...); - } - - /** - * @brief Returns pointers to the given components for an entity. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @note - * The registry retains ownership of the pointed-to components. - * - * @tparam Component Types of components to get. - * @param entity A valid identifier. - * @return Pointers to the components owned by the entity. - */ - template - [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - - if constexpr(sizeof...(Component) == 1) { - const auto &cpool = assure...>(); - return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr; - } else { - return std::make_tuple(try_get(entity)...); - } - } - - /*! @copydoc try_get */ - template - [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { - if constexpr(sizeof...(Component) == 1) { - return (const_cast(std::as_const(*this).template try_get(entity)), ...); - } else { - return std::make_tuple(try_get(entity)...); - } - } - - /** - * @brief Clears a whole registry or the pools for the given components. - * @tparam Component Types of components to remove from their entities. - */ - template - void clear() { - if constexpr(sizeof...(Component) == 0) { - for(auto &&curr: pools) { - curr.second->clear(); - } - - each([this](const auto entity) { this->release(entity); }); - } else { - (assure().clear(), ...); - } - } - - /** - * @brief Iterates all the entities that are still in use. - * - * The signature of the function should be equivalent to the following: - * - * @code{.cpp} - * void(const Entity); - * @endcode - * - * It's not defined whether entities created during iteration are returned. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - if(free_list == null) { - for(auto pos = entities.size(); pos; --pos) { - func(entities[pos - 1]); - } - } else { - for(auto pos = entities.size(); pos; --pos) { - if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { - func(entity); - } - } - } - } - - /** - * @brief Checks if an entity has components assigned. - * @param entity A valid identifier. - * @return True if the entity has no components assigned, false otherwise. - */ - [[nodiscard]] bool orphan(const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); }); - } - - /** - * @brief Returns a sink object for the given component. - * - * Use this function to receive notifications whenever a new instance of the - * given component is created and assigned to an entity.
- * The function type for a listener is equivalent to: - * - * @code{.cpp} - * void(basic_registry &, Entity); - * @endcode - * - * Listeners are invoked **after** assigning the component to the entity. - * - * @sa sink - * - * @tparam Component Type of component of which to get the sink. - * @return A temporary sink object. - */ - template - [[nodiscard]] auto on_construct() { - return assure().on_construct(); - } - - /** - * @brief Returns a sink object for the given component. - * - * Use this function to receive notifications whenever an instance of the - * given component is explicitly updated.
- * The function type for a listener is equivalent to: - * - * @code{.cpp} - * void(basic_registry &, Entity); - * @endcode - * - * Listeners are invoked **after** updating the component. - * - * @sa sink - * - * @tparam Component Type of component of which to get the sink. - * @return A temporary sink object. - */ - template - [[nodiscard]] auto on_update() { - return assure().on_update(); - } - - /** - * @brief Returns a sink object for the given component. - * - * Use this function to receive notifications whenever an instance of the - * given component is removed from an entity and thus destroyed.
- * The function type for a listener is equivalent to: - * - * @code{.cpp} - * void(basic_registry &, Entity); - * @endcode - * - * Listeners are invoked **before** removing the component from the entity. - * - * @sa sink - * - * @tparam Component Type of component of which to get the sink. - * @return A temporary sink object. - */ - template - [[nodiscard]] auto on_destroy() { - return assure().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 Component 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. - * @return A newly created view. - */ - template - [[nodiscard]] basic_view, std::add_const_t...>, exclude_t> view(exclude_t = {}) const { - return {assure>(), assure>()..., assure()...}; - } - - /*! @copydoc view */ - template - [[nodiscard]] basic_view, exclude_t> view(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 Types of components owned by the group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components used to filter the group. - * @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()) && ...); - }); - - if(it != groups.cend()) { - handler = static_cast(it->group.get()); - } else { - group_data candidate = { - size, - {new handler_type{}, [](void *instance) { delete static_cast(instance); }}, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, - []([[maybe_unused]] const id_type ctype) ENTT_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), ...); - - (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), ...); - - 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); - } - } - } - - return {handler->current, std::get> &>(cpools)..., std::get> &>(cpools)...}; - } - - /*! @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()) && ...); - }); - - 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>()...}; - } - } - - /*! @copydoc group */ - template - [[nodiscard]] basic_group, get_t<>, exclude_t> group(exclude_t = {}) { - return group(get_t<>{}, exclude); - } - - /*! @copydoc group */ - template - [[nodiscard]] basic_group...>, get_t<>, exclude_t> group_if_exists(exclude_t = {}) const { - return group_if_exists...>(get_t<>{}, exclude); - } - - /** - * @brief Checks whether the given components belong to any group. - * @tparam Component Types of components in which one is interested. - * @return True if the pools of the given components are _free_, false - * otherwise. - */ - 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 Types of components owned by the group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components 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> &) ENTT_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(); - } - - /** - * @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: - * - * @code{.cpp} - * bool(const Entity, const Entity); - * bool(const Component &, const Component &); - * @endcode - * - * Moreover, it shall induce a _strict weak ordering_ on the values.
- * The sort function object offers an `operator()` that accepts: - * - * * 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 object to use to compare the elements. - * - * The comparison function object hasn't necessarily the type of the one - * passed along with the other parameters to this member function. - * - * @warning - * Pools of components owned by a group cannot be sorted. - * - * @tparam Component Type of components to sort. - * @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) { - ENTT_ASSERT(!owned(), "Cannot sort owned storage"); - auto &cpool = assure(); - - if constexpr(std::is_invocable_v) { - auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; - cpool.sort(std::move(comp), std::move(algo), std::forward(args)...); - } else { - cpool.sort(std::move(compare), std::move(algo), std::forward(args)...); - } - } - - /** - * @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`. - * - * @warning - * Pools of components owned by a group cannot be sorted. - * - * @tparam To Type of components to sort. - * @tparam From Type of components to use to sort. - */ - template - void sort() { - ENTT_ASSERT(!owned(), "Cannot sort owned storage"); - assure().respect(assure()); - } - - /** - * @brief Returns the context object, that is, a general purpose container. - * @return The context object, that is, a general purpose container. - */ - context &ctx() ENTT_NOEXCEPT { - return vars; - } - - /*! @copydoc ctx */ - const context &ctx() const ENTT_NOEXCEPT { - return vars; - } - -private: - dense_map, identity> pools; - std::vector groups; - std::vector entities; - entity_type free_list; - context vars; -}; - -} // namespace entt - -#endif - - -namespace entt { - -/** - * @brief Non-owning handle to an entity. - * - * Tiny wrapper around a registry and an entity. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Type 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 = constness_as_t>, Entity>; - /*! @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() ENTT_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) ENTT_NOEXCEPT - : reg{&ref}, - entt{value} {} - - /** - * @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. - */ - template - operator basic_handle() const ENTT_NOEXCEPT { - static_assert(std::is_same_v || std::is_same_v, Entity>, "Invalid conversion between different handles"); - static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); - - return reg ? basic_handle{*reg, entt} : basic_handle{}; - } - - /** - * @brief Converts a handle to its underlying entity. - * @return The contained identifier. - */ - [[nodiscard]] operator entity_type() const ENTT_NOEXCEPT { - return entity(); - } - - /** - * @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 ENTT_NOEXCEPT { - return reg && reg->valid(entt); - } - - /** - * @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 Returns a pointer to the underlying registry, if any. - * @return A pointer to the underlying registry, if any. - */ - [[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT { - return reg; - } - - /** - * @brief Returns the entity associated with a handle. - * @return The entity associated with the handle. - */ - [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT { - return entt; - } - - /** - * @brief Destroys the entity associated with a handle. - * @sa basic_registry::destroy - */ - void destroy() { - reg->destroy(entt); - } - - /** - * @brief Destroys the entity associated with a handle. - * @sa basic_registry::destroy - * @param version A desired version upon destruction. - */ - void destroy(const version_type version) { - reg->destroy(entt, version); - } - - /** - * @brief Assigns the given component to a handle. - * @sa basic_registry::emplace - * @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...(Type) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template emplace(entt, std::forward(args)...); - } - - /** - * @brief Assigns or replaces the given component for a handle. - * @sa basic_registry::emplace_or_replace - * @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 - decltype(auto) emplace_or_replace(Args &&...args) const { - static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template emplace_or_replace(entt, std::forward(args)...); - } - - /** - * @brief Patches the given component for a handle. - * @sa basic_registry::patch - * @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...(Type) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template patch(entt, std::forward(func)...); - } - - /** - * @brief Replaces the given component for a handle. - * @sa basic_registry::replace - * @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 - decltype(auto) replace(Args &&...args) const { - static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template replace(entt, std::forward(args)...); - } - - /** - * @brief Removes the given components from a handle. - * @sa basic_registry::remove - * @tparam Component Types of components to remove. - * @return The number of components actually removed. - */ - template - size_type remove() const { - static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); - return reg->template remove(entt); - } - - /** - * @brief Erases the given components from a handle. - * @sa basic_registry::erase - * @tparam Component Types of components to erase. - */ - template - void erase() const { - static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); - reg->template erase(entt); - } - - /** - * @brief Checks if a handle has all the given components. - * @sa basic_registry::all_of - * @tparam Component Components for which to perform the check. - * @return True if the handle has all the components, false otherwise. - */ - template - [[nodiscard]] decltype(auto) all_of() const { - return reg->template all_of(entt); - } - - /** - * @brief Checks if a handle has at least one of the given components. - * @sa basic_registry::any_of - * @tparam Component Components for which to perform the check. - * @return True if the handle has at least one of the given components, - * false otherwise. - */ - template - [[nodiscard]] decltype(auto) any_of() const { - return reg->template any_of(entt); - } - - /** - * @brief Returns references to the given components for a handle. - * @sa basic_registry::get - * @tparam Component Types of components to get. - * @return References to the components owned by the handle. - */ - template - [[nodiscard]] decltype(auto) get() const { - static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); - return reg->template get(entt); - } - - /** - * @brief Returns a reference to the given component for a handle. - * @sa basic_registry::get_or_emplace - * @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. - */ - template - [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { - static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); - return reg->template get_or_emplace(entt, std::forward(args)...); - } - - /** - * @brief Returns pointers to the given components for a handle. - * @sa basic_registry::try_get - * @tparam Component Types of components to get. - * @return Pointers to the components owned by the handle. - */ - template - [[nodiscard]] auto try_get() const { - static_assert(sizeof...(Type) == 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); - } - - /** - * @brief Visits a handle and returns the pools for its components. - * - * The signature of the function should be equivalent to the following: - * - * @code{.cpp} - * void(id_type, const basic_sparse_set &); - * @endcode - * - * Returned pools are those that contain the entity associated with the - * handle. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void visit(Func &&func) const { - for(auto [id, storage]: reg->storage()) { - if(storage.contains(entt)) { - func(id, storage); - } - } - } - -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) ENTT_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) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -basic_handle(basic_registry &, Entity) -> basic_handle; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -basic_handle(const basic_registry &, Entity) -> basic_handle; - -} // namespace entt - -#endif - -// #include "entity/helper.hpp" -#ifndef ENTT_ENTITY_HELPER_HPP -#define ENTT_ENTITY_HELPER_HPP - -#include -// #include "../config/config.h" - -// #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 -auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); - -template -auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); - -template -auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); - -template -auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); - -template -auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); - -template -using function_pointer_t = decltype(internal::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 Used to wrap a function or a member of a specified type. */ -template -struct connect_arg_t {}; - -/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ -template -inline constexpr connect_arg_t connect_arg{}; - -/** - * @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) ENTT_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) ENTT_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))...)); - }; - } - - template - [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_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))...)); - }; - } - -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() ENTT_NOEXCEPT - : instance{nullptr}, - fn{nullptr} {} - - /** - * @brief Constructs a delegate and connects a free function or an unbound - * member. - * @tparam Candidate Function or member to connect to the delegate. - */ - template - delegate(connect_arg_t) ENTT_NOEXCEPT { - connect(); - } - - /** - * @brief Constructs a delegate and connects a free function with payload or - * a bound member. - * @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 object that fits the purpose. - */ - template - delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { - connect(std::forward(value_or_instance)); - } - - /** - * @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) ENTT_NOEXCEPT { - connect(function, payload); - } - - /** - * @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() ENTT_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{})); - } - } - - /** - * @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) ENTT_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 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) ENTT_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 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) ENTT_NOEXCEPT { - instance = payload; - fn = function; - } - - /** - * @brief Resets a delegate. - * - * After a reset, a delegate cannot be invoked anymore. - */ - void reset() ENTT_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. - */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { - return instance; - } - - /** - * @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)...); - } - - /** - * @brief Checks whether a delegate actually stores a listener. - * @return False if the delegate is empty, true otherwise. - */ - [[nodiscard]] explicit operator bool() const ENTT_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 ENTT_NOEXCEPT { - return fn == other.fn && instance == other.instance; - } - -private: - const void *instance; - function_type *fn; -}; - -/** - * @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) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -/** - * @brief Deduction guide. - * @tparam Candidate Function or member to connect to the delegate. - */ -template -delegate(connect_arg_t) -> delegate>>; - -/** - * @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>>; - -/** - * @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; - -} // namespace entt - -#endif - -// #include "fwd.hpp" - -// #include "registry.hpp" - - -namespace entt { - -/** - * @brief Converts a registry to a view. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct as_view { - /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; - /*! @brief Type of registry to convert. */ - using registry_type = constness_as_t, Entity>; - - /** - * @brief Constructs a converter for a given registry. - * @param source A valid reference to a registry. - */ - as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} - - /** - * @brief Conversion function from a registry to a view. - * @tparam Exclude Types of components used to filter the view. - * @tparam Component Type of components used to construct the view. - * @return A newly created view. - */ - template - operator basic_view, Exclude>() const { - return reg.template view(Exclude{}); - } - -private: - registry_type ® -}; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_view(basic_registry &) -> as_view; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_view(const basic_registry &) -> as_view; - -/** - * @brief Converts a registry to a group. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct as_group { - /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; - /*! @brief Type of registry to convert. */ - using registry_type = constness_as_t, Entity>; - - /** - * @brief Constructs a converter for a given registry. - * @param source A valid reference to a registry. - */ - as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} - - /** - * @brief Conversion function from a registry to a group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components used to filter the group. - * @tparam Owned Types of components owned by the group. - * @return A newly created group. - */ - template - operator basic_group, Get, Exclude>() const { - if constexpr(std::is_const_v) { - return reg.template group_if_exists(Get{}, Exclude{}); - } else { - return reg.template group(Get{}, Exclude{}); - } - } - -private: - registry_type ® -}; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_group(basic_registry &) -> as_group; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_group(const basic_registry &) -> as_group; - -/** - * @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 Entity A valid entity type (see entt_traits for more details). - * @param reg A registry that contains the given entity and its components. - * @param entt Entity from which to get the component. - */ -template -void invoke(basic_registry ®, const Entity entt) { - static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); - delegate &, const Entity)> 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 Entity A valid entity type (see entt_traits for more details). - * @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 -Entity to_entity(const basic_registry ®, const Component &instance) { - const auto &storage = reg.template storage(); - const typename basic_registry::base_type &base = storage; - const auto *addr = std::addressof(instance); - - for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) { - if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { - return *(it + dist); - } - } - - return null; -} - -} // namespace entt - -#endif - -// #include "entity/observer.hpp" -#ifndef ENTT_ENTITY_OBSERVER_HPP -#define ENTT_ENTITY_OBSERVER_HPP - -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/type_traits.hpp" - -// #include "../signal/delegate.hpp" - -// #include "entity.hpp" - -// #include "fwd.hpp" - -// #include "registry.hpp" - -// #include "storage.hpp" - -// #include "utility.hpp" - - -namespace entt { - -/*! @brief Grouping matcher. */ -template -struct matcher {}; - -/** - * @brief Collector. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. - */ -template -struct basic_collector; - -/** - * @brief Collector. - * - * A collector contains a set of rules (literally, matchers) to use to track - * entities.
- * Its main purpose is to generate a descriptor that allows an observer to know - * how to connect to a registry. - */ -template<> -struct basic_collector<> { - /** - * @brief Adds a grouping matcher to the collector. - * @tparam AllOf Types of components tracked by the matcher. - * @tparam NoneOf Types of components used to filter out entities. - * @return The updated collector. - */ - template - static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { - return basic_collector, type_list<>, type_list, AllOf...>>{}; - } - - /** - * @brief Adds an observing matcher to the collector. - * @tparam AnyOf Type of component for which changes should be detected. - * @return The updated collector. - */ - template - static constexpr auto update() ENTT_NOEXCEPT { - return basic_collector, type_list<>, AnyOf>>{}; - } -}; - -/** - * @brief Collector. - * @copydetails basic_collector<> - * @tparam Reject Untracked types used to filter out entities. - * @tparam Require Untracked types required by the matcher. - * @tparam Rule Specific details of the current matcher. - * @tparam Other Other matchers. - */ -template -struct basic_collector, type_list, Rule...>, Other...> { - /*! @brief Current matcher. */ - using current_type = matcher, type_list, Rule...>; - - /** - * @brief Adds a grouping matcher to the collector. - * @tparam AllOf Types of components tracked by the matcher. - * @tparam NoneOf Types of components used to filter out entities. - * @return The updated collector. - */ - template - static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { - return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; - } - - /** - * @brief Adds an observing matcher to the collector. - * @tparam AnyOf Type of component for which changes should be detected. - * @return The updated collector. - */ - template - static constexpr auto update() ENTT_NOEXCEPT { - return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; - } - - /** - * @brief Updates the filter of the last added matcher. - * @tparam AllOf Types of components required by the matcher. - * @tparam NoneOf Types of components used to filter out entities. - * @return The updated collector. - */ - template - static constexpr auto where(exclude_t = {}) ENTT_NOEXCEPT { - using extended_type = matcher, type_list, Rule...>; - return basic_collector{}; - } -}; - -/*! @brief Variable template used to ease the definition of collectors. */ -inline constexpr basic_collector<> collector{}; - -/** - * @brief Observer. - * - * An observer returns all the entities and only the entities that fit the - * requirements of at least one matcher. Moreover, it's guaranteed that the - * entity list is tightly packed in memory for fast iterations.
- * In general, observers don't stay true to the order of any set of components. - * - * Observers work mainly with two types of matchers, provided through a - * collector: - * - * * Observing matcher: an observer will return at least all the living entities - * for which one or more of the given components have been updated and not yet - * destroyed. - * * Grouping matcher: an observer will return at least all the living entities - * that would have entered the given group if it existed and that would have - * not yet left it. - * - * If an entity respects the requirements of multiple matchers, it will be - * returned once and only once by the observer in any case. - * - * Matchers support also filtering by means of a _where_ clause that accepts - * both a list of types and an exclusion list.
- * Whenever a matcher finds that an entity matches its requirements, the - * condition of the filter is verified before to register the entity itself. - * Moreover, a registered entity isn't returned by the observer if the condition - * set by the filter is broken in the meantime. - * - * @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). - * * 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. - * - * @warning - * Lifetime of an observer doesn't necessarily have to overcome that of the - * registry to which it is connected. However, the observer must be disconnected - * from the registry before being destroyed to avoid crashes due to dangling - * pointers. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -class basic_observer { - using payload_type = std::uint32_t; - - template - struct matcher_handler; - - template - struct matcher_handler, type_list, AnyOf>> { - template - static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { - if(reg.template all_of(entt) && !reg.template any_of(entt)) { - if(!obs.storage.contains(entt)) { - obs.storage.emplace(entt); - } - - obs.storage.get(entt) |= (1 << Index); - } - } - - template - static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { - if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { - obs.storage.erase(entt); - } - } - - template - static void connect(basic_observer &obs, basic_registry ®) { - (reg.template on_destroy().template connect<&discard_if>(obs), ...); - (reg.template on_construct().template connect<&discard_if>(obs), ...); - reg.template on_update().template connect<&maybe_valid_if>(obs); - reg.template on_destroy().template connect<&discard_if>(obs); - } - - static void disconnect(basic_observer &obs, basic_registry ®) { - (reg.template on_destroy().disconnect(obs), ...); - (reg.template on_construct().disconnect(obs), ...); - reg.template on_update().disconnect(obs); - reg.template on_destroy().disconnect(obs); - } - }; - - template - struct matcher_handler, type_list, type_list, AllOf...>> { - template - static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { - auto condition = [®, entt]() { - if constexpr(sizeof...(Ignore) == 0) { - return reg.template all_of(entt) && !reg.template any_of(entt); - } else { - return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); - } - }; - - if(condition()) { - if(!obs.storage.contains(entt)) { - obs.storage.emplace(entt); - } - - obs.storage.get(entt) |= (1 << Index); - } - } - - template - static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { - if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { - obs.storage.erase(entt); - } - } - - template - static void connect(basic_observer &obs, basic_registry ®) { - (reg.template on_destroy().template connect<&discard_if>(obs), ...); - (reg.template on_construct().template connect<&discard_if>(obs), ...); - (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); - (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); - (reg.template on_destroy().template connect<&discard_if>(obs), ...); - (reg.template on_construct().template connect<&discard_if>(obs), ...); - } - - static void disconnect(basic_observer &obs, basic_registry ®) { - (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), ...); - } - }; - - template - static void disconnect(basic_registry ®, basic_observer &obs) { - (matcher_handler::disconnect(obs, reg), ...); - } - - template - void connect(basic_registry ®, std::index_sequence) { - static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); - (matcher_handler::template connect(*this, reg), ...); - release.template connect<&basic_observer::disconnect>(reg); - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Random access iterator type. */ - using iterator = typename basic_sparse_set::iterator; - - /*! @brief Default constructor. */ - basic_observer() - : release{}, - storage{} {} - - /*! @brief Default copy constructor, deleted on purpose. */ - basic_observer(const basic_observer &) = delete; - /*! @brief Default move constructor, deleted on purpose. */ - basic_observer(basic_observer &&) = delete; - - /** - * @brief Creates an observer and connects it to a given registry. - * @tparam Matcher Types of matchers to use to initialize the observer. - * @param reg A valid reference to a registry. - */ - template - basic_observer(basic_registry ®, basic_collector) - : basic_observer{} { - connect(reg, std::index_sequence_for{}); - } - - /*! @brief Default destructor. */ - ~basic_observer() = default; - - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This observer. - */ - basic_observer &operator=(const basic_observer &) = delete; - - /** - * @brief Default move assignment operator, deleted on purpose. - * @return This observer. - */ - basic_observer &operator=(basic_observer &&) = delete; - - /** - * @brief Connects an observer to a given registry. - * @tparam Matcher Types of matchers to use to initialize the observer. - * @param reg A valid reference to a registry. - */ - template - void connect(basic_registry ®, basic_collector) { - disconnect(); - connect(reg, std::index_sequence_for{}); - storage.clear(); - } - - /*! @brief Disconnects an observer from the registry it keeps track of. */ - void disconnect() { - if(release) { - release(*this); - release.reset(); - } - } - - /** - * @brief Returns the number of elements in an observer. - * @return Number of elements. - */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { - return storage.size(); - } - - /** - * @brief Checks whether an observer is empty. - * @return True if the observer is empty, false otherwise. - */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - return storage.empty(); - } - - /** - * @brief Direct access to the list of entities of the observer. - * - * The returned pointer is such that range `[data(), data() + size())` is - * always a valid range, even if the container is empty. - * - * @note - * Entities are in the reverse order as returned by the `begin`/`end` - * iterators. - * - * @return A pointer to the array of entities. - */ - [[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT { - return storage.data(); - } - - /** - * @brief Returns an iterator to the first entity of the observer. - * - * The returned iterator points to the first entity of the observer. If the - * container is empty, the returned iterator will be equal to `end()`. - * - * @return An iterator to the first entity of the observer. - */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return storage.basic_sparse_set::begin(); - } - - /** - * @brief Returns an iterator that is past the last entity of the observer. - * - * The returned iterator points to the entity following the last entity of - * the observer. Attempting to dereference the returned iterator results in - * undefined behavior. - * - * @return An iterator to the entity following the last entity of the - * observer. - */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return storage.basic_sparse_set::end(); - } - - /*! @brief Clears the underlying container. */ - void clear() ENTT_NOEXCEPT { - storage.clear(); - } - - /** - * @brief Iterates entities and applies the given function object to them. - * - * The function object is invoked for each entity.
- * The signature of the function must be equivalent to the following form: - * - * @code{.cpp} - * void(const entity_type); - * @endcode - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - for(const auto entity: *this) { - func(entity); - } - } - - /** - * @brief Iterates entities and applies the given function object to them, - * then clears the observer. - * - * @sa each - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) { - std::as_const(*this).each(std::move(func)); - clear(); - } - -private: - delegate release; - basic_storage storage; -}; - -} // namespace entt - -#endif - -// #include "entity/organizer.hpp" -#ifndef ENTT_ENTITY_ORGANIZER_HPP -#define ENTT_ENTITY_ORGANIZER_HPP - -#include -#include -#include -#include -#include -// #include "../container/dense_map.hpp" - -// #include "../core/type_info.hpp" - -// #include "../core/type_traits.hpp" - -// #include "../core/utility.hpp" - -// #include "fwd.hpp" - -// #include "helper.hpp" -#ifndef ENTT_ENTITY_HELPER_HPP -#define ENTT_ENTITY_HELPER_HPP - -#include -// #include "../config/config.h" - -// #include "../core/fwd.hpp" - -// #include "../core/type_traits.hpp" - -// #include "../signal/delegate.hpp" - -// #include "fwd.hpp" - -// #include "registry.hpp" - - -namespace entt { - -/** - * @brief Converts a registry to a view. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct as_view { - /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; - /*! @brief Type of registry to convert. */ - using registry_type = constness_as_t, Entity>; - - /** - * @brief Constructs a converter for a given registry. - * @param source A valid reference to a registry. - */ - as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} - - /** - * @brief Conversion function from a registry to a view. - * @tparam Exclude Types of components used to filter the view. - * @tparam Component Type of components used to construct the view. - * @return A newly created view. - */ - template - operator basic_view, Exclude>() const { - return reg.template view(Exclude{}); - } - -private: - registry_type ® -}; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_view(basic_registry &) -> as_view; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_view(const basic_registry &) -> as_view; - -/** - * @brief Converts a registry to a group. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct as_group { - /*! @brief Underlying entity identifier. */ - using entity_type = std::remove_const_t; - /*! @brief Type of registry to convert. */ - using registry_type = constness_as_t, Entity>; - - /** - * @brief Constructs a converter for a given registry. - * @param source A valid reference to a registry. - */ - as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} - - /** - * @brief Conversion function from a registry to a group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components used to filter the group. - * @tparam Owned Types of components owned by the group. - * @return A newly created group. - */ - template - operator basic_group, Get, Exclude>() const { - if constexpr(std::is_const_v) { - return reg.template group_if_exists(Get{}, Exclude{}); - } else { - return reg.template group(Get{}, Exclude{}); - } - } - -private: - registry_type ® -}; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_group(basic_registry &) -> as_group; - -/** - * @brief Deduction guide. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_group(const basic_registry &) -> as_group; - -/** - * @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 Entity A valid entity type (see entt_traits for more details). - * @param reg A registry that contains the given entity and its components. - * @param entt Entity from which to get the component. - */ -template -void invoke(basic_registry ®, const Entity entt) { - static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); - delegate &, const Entity)> 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 Entity A valid entity type (see entt_traits for more details). - * @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 -Entity to_entity(const basic_registry ®, const Component &instance) { - const auto &storage = reg.template storage(); - const typename basic_registry::base_type &base = storage; - const auto *addr = std::addressof(instance); - - for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) { - if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { - return *(it + dist); - } - } - - return null; -} - -} // namespace entt - -#endif - - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -struct is_view: std::false_type {}; - -template -struct is_view, exclude_t>>: std::true_type {}; - -template -inline constexpr bool is_view_v = is_view::value; - -template -struct unpack_type { - using ro = std::conditional_t< - type_list_contains_v> || (std::is_const_v && !type_list_contains_v>), - type_list>, - type_list<>>; - - using rw = std::conditional_t< - type_list_contains_v> || (!std::is_const_v && !type_list_contains_v>), - type_list, - type_list<>>; -}; - -template -struct unpack_type, type_list> { - using ro = type_list<>; - using rw = type_list<>; -}; - -template -struct unpack_type, type_list> - : unpack_type, type_list> {}; - -template -struct unpack_type, exclude_t>, type_list> { - using ro = type_list_cat_t, typename unpack_type>::ro...>; - using rw = type_list_cat_t>::rw...>; -}; - -template -struct unpack_type, exclude_t>, type_list> - : unpack_type, exclude_t>, type_list> {}; - -template -struct resource_traits; - -template -struct resource_traits, type_list> { - using args = type_list...>; - using ro = type_list_cat_t>::ro..., typename unpack_type>::ro...>; - using rw = type_list_cat_t>::rw..., typename unpack_type>::rw...>; -}; - -template -resource_traits...>, type_list> free_function_to_resource_traits(Ret (*)(Args...)); - -template -resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); - -template -resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); - -template -resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Utility class for creating a static task graph. - * - * This class offers minimal support (but sufficient in many cases) for creating - * an execution graph from functions and their requirements on resources.
- * Note that the resulting tasks aren't executed in any case. This isn't the - * goal of the tool. Instead, they are returned to the user in the form of a - * graph that allows for safe execution. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -class basic_organizer final { - using callback_type = void(const void *, basic_registry &); - using prepare_type = void(basic_registry &); - using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); - - struct vertex_data final { - std::size_t ro_count{}; - std::size_t rw_count{}; - const char *name{}; - const void *payload{}; - callback_type *callback{}; - dependency_type *dependency; - prepare_type *prepare{}; - const type_info *info{}; - }; - - template - [[nodiscard]] static decltype(auto) extract(basic_registry ®) { - if constexpr(std::is_same_v>) { - return reg; - } else if constexpr(internal::is_view_v) { - return as_view{reg}; - } else { - return reg.ctx().template emplace>(); - } - } - - template - [[nodiscard]] static auto to_args(basic_registry ®, type_list) { - return std::tuple(reg))...>(extract(reg)...); - } - template - static std::size_t fill_dependencies(type_list, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { - if constexpr(sizeof...(Type) == 0u) { - return {}; - } else { - const type_info *info[sizeof...(Type)]{&type_id()...}; - const auto length = (std::min)(count, sizeof...(Type)); - std::copy_n(info, length, buffer); - return length; - } - } - - template - void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { - dependencies[type_hash>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); - (dependencies[type_hash::value()].emplace_back(index, false), ...); - (dependencies[type_hash::value()].emplace_back(index, true), ...); - } - - [[nodiscard]] std::vector adjacency_matrix() { - const auto length = vertices.size(); - std::vector edges(length * length, false); - - // creates the adjacency matrix - for(const auto &deps: dependencies) { - const auto last = deps.second.cend(); - auto it = deps.second.cbegin(); - - while(it != last) { - if(it->second) { - // rw item - if(auto curr = it++; it != last) { - if(it->second) { - edges[curr->first * length + it->first] = true; - } else { - if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { - for(; it != next; ++it) { - edges[curr->first * length + it->first] = true; - edges[it->first * length + next->first] = true; - } - } else { - for(; it != next; ++it) { - edges[curr->first * length + it->first] = true; - } - } - } - } - } else { - // ro item, possibly only on first iteration - if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { - for(; it != next; ++it) { - edges[it->first * length + next->first] = true; - } - } 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) { - edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]); - } - } - } - - // applies the transitive reduction - for(std::size_t vert{}; vert < length; ++vert) { - edges[vert * length + vert] = false; - } - - for(std::size_t vj{}; vj < length; ++vj) { - for(std::size_t vi{}; vi < length; ++vi) { - if(edges[vi * length + vj]) { - for(std::size_t vk{}; vk < length; ++vk) { - if(edges[vj * length + vk]) { - edges[vi * length + vk] = false; - } - } - } - } - } - - return edges; - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Raw task function type. */ - using function_type = callback_type; - - /*! @brief Vertex type of a task graph defined as an adjacency list. */ - struct vertex { - /** - * @brief Constructs a vertex of the task graph. - * @param vtype True if the vertex is a top-level one, false otherwise. - * @param data The data associated with the vertex. - * @param edges The indices of the children in the adjacency list. - */ - vertex(const bool vtype, vertex_data data, std::vector edges) - : is_top_level{vtype}, - node{std::move(data)}, - reachable{std::move(edges)} {} - - /** - * @brief Fills a buffer with the type info objects for the writable - * resources of a vertex. - * @param buffer A buffer pre-allocated by the user. - * @param length The length of the user-supplied buffer. - * @return The number of type info objects written to the buffer. - */ - size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT { - return node.dependency(false, buffer, length); - } - - /** - * @brief Fills a buffer with the type info objects for the read-only - * resources of a vertex. - * @param buffer A buffer pre-allocated by the user. - * @param length The length of the user-supplied buffer. - * @return The number of type info objects written to the buffer. - */ - size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT { - return node.dependency(true, buffer, length); - } - - /** - * @brief Returns the number of read-only resources of a vertex. - * @return The number of read-only resources of the vertex. - */ - size_type ro_count() const ENTT_NOEXCEPT { - return node.ro_count; - } - - /** - * @brief Returns the number of writable resources of a vertex. - * @return The number of writable resources of the vertex. - */ - size_type rw_count() const ENTT_NOEXCEPT { - return node.rw_count; - } - - /** - * @brief Checks if a vertex is also a top-level one. - * @return True if the vertex is a top-level one, false otherwise. - */ - bool top_level() const ENTT_NOEXCEPT { - return is_top_level; - } - - /** - * @brief Returns a type info object associated with a vertex. - * @return A properly initialized type info object. - */ - const type_info &info() const ENTT_NOEXCEPT { - return *node.info; - } - - /** - * @brief Returns a user defined name associated with a vertex, if any. - * @return The user defined name associated with the vertex, if any. - */ - const char *name() const ENTT_NOEXCEPT { - return node.name; - } - - /** - * @brief Returns the function associated with a vertex. - * @return The function associated with the vertex. - */ - function_type *callback() const ENTT_NOEXCEPT { - return node.callback; - } - - /** - * @brief Returns the payload associated with a vertex, if any. - * @return The payload associated with the vertex, if any. - */ - const void *data() const ENTT_NOEXCEPT { - return node.payload; - } - - /** - * @brief Returns the list of nodes reachable from a given vertex. - * @return The list of nodes reachable from the vertex. - */ - const std::vector &children() const ENTT_NOEXCEPT { - return reachable; - } - - /** - * @brief Prepares a registry and assures that all required resources - * are properly instantiated before using them. - * @param reg A valid registry. - */ - void prepare(basic_registry ®) const { - node.prepare ? node.prepare(reg) : void(); - } - - private: - bool is_top_level; - vertex_data node; - std::vector reachable; - }; - - /** - * @brief Adds a free function to the task list. - * @tparam Candidate Function to add to the task list. - * @tparam Req Additional requirements and/or override resource access mode. - * @param name Optional name to associate with the task. - */ - template - void emplace(const char *name = nullptr) { - using resource_type = decltype(internal::free_function_to_resource_traits(Candidate)); - constexpr auto requires_registry = type_list_contains_v>; - - callback_type *callback = +[](const void *, basic_registry ®) { - std::apply(Candidate, to_args(reg, typename resource_type::args{})); - }; - - vertex_data vdata{ - resource_type::ro::size, - resource_type::rw::size, - name, - nullptr, - callback, - +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, - +[](basic_registry ®) { void(to_args(reg, typename resource_type::args{})); }, - &type_id>()}; - - track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); - vertices.push_back(std::move(vdata)); - } - - /** - * @brief Adds a free function with payload or a member function with an - * instance to the task list. - * @tparam Candidate Function or member to add to the task list. - * @tparam Req Additional requirements and/or override resource access mode. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - * @param name Optional name to associate with the task. - */ - template - void emplace(Type &value_or_instance, const char *name = nullptr) { - using resource_type = decltype(internal::constrained_function_to_resource_traits(Candidate)); - constexpr auto requires_registry = type_list_contains_v>; - - callback_type *callback = +[](const void *payload, basic_registry ®) { - Type *curr = static_cast(const_cast *>(payload)); - std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); - }; - - vertex_data vdata{ - resource_type::ro::size, - resource_type::rw::size, - name, - &value_or_instance, - callback, - +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, - +[](basic_registry ®) { void(to_args(reg, typename resource_type::args{})); }, - &type_id>()}; - - track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); - vertices.push_back(std::move(vdata)); - } - - /** - * @brief Adds an user defined function with optional payload to the task - * list. - * @tparam Req Additional requirements and/or override resource access mode. - * @param func Function to add to the task list. - * @param payload User defined arbitrary data. - * @param name Optional name to associate with the task. - */ - template - void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { - using resource_type = internal::resource_traits, type_list>; - track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); - - vertex_data vdata{ - resource_type::ro::size, - resource_type::rw::size, - name, - payload, - func, - +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, - nullptr, - &type_id()}; - - vertices.push_back(std::move(vdata)); - } - - /** - * @brief Generates a task graph for the current content. - * @return The adjacency list of the task graph. - */ - std::vector graph() { - const auto edges = adjacency_matrix(); - - // creates the adjacency list - std::vector adjacency_list{}; - adjacency_list.reserve(vertices.size()); - - for(std::size_t col{}, length = vertices.size(); col < length; ++col) { - std::vector reachable{}; - const auto row = col * length; - bool is_top_level = true; - - for(std::size_t next{}; next < length; ++next) { - if(edges[row + next]) { - reachable.push_back(next); - } - } - - for(std::size_t next{}; next < length && is_top_level; ++next) { - is_top_level = !edges[next * length + col]; - } - - adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable)); - } - - return adjacency_list; - } - - /*! @brief Erases all elements from a container. */ - void clear() { - dependencies.clear(); - vertices.clear(); - } - -private: - dense_map>, identity> dependencies; - std::vector vertices; -}; - -} // namespace entt - -#endif - -// #include "entity/registry.hpp" -#ifndef ENTT_ENTITY_REGISTRY_HPP -#define ENTT_ENTITY_REGISTRY_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../container/dense_map.hpp" - -// #include "../core/algorithm.hpp" - -// #include "../core/any.hpp" - -// #include "../core/fwd.hpp" - -// #include "../core/iterator.hpp" - -// #include "../core/type_info.hpp" - -// #include "../core/type_traits.hpp" - -// #include "../core/utility.hpp" - -// #include "component.hpp" - -// #include "entity.hpp" - -// #include "fwd.hpp" - -// #include "group.hpp" - -// #include "runtime_view.hpp" - -// #include "sparse_set.hpp" - -// #include "storage.hpp" - -// #include "utility.hpp" - -// #include "view.hpp" - - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -template -class storage_proxy_iterator final { - template - friend class storage_proxy_iterator; - - using mapped_type = std::remove_reference_t()->second)>; - -public: - using value_type = std::pair &>; - using pointer = input_iterator_pointer; - using reference = value_type; - using difference_type = std::ptrdiff_t; - using iterator_category = std::input_iterator_tag; - - storage_proxy_iterator() ENTT_NOEXCEPT - : it{} {} - - storage_proxy_iterator(const It iter) ENTT_NOEXCEPT - : it{iter} {} - - template && std::is_constructible_v>> - storage_proxy_iterator(const storage_proxy_iterator &other) ENTT_NOEXCEPT - : it{other.it} {} - - storage_proxy_iterator &operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - storage_proxy_iterator operator++(int) ENTT_NOEXCEPT { - storage_proxy_iterator orig = *this; - return ++(*this), orig; - } - - storage_proxy_iterator &operator--() ENTT_NOEXCEPT { - return --it, *this; - } - - storage_proxy_iterator operator--(int) ENTT_NOEXCEPT { - storage_proxy_iterator orig = *this; - return operator--(), orig; - } - - storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { - it += value; - return *this; - } - - storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { - storage_proxy_iterator copy = *this; - return (copy += value); - } - - storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { - return (*this += -value); - } - - storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { - return (*this + -value); - } - - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { - return {it[value].first, *it[value].second}; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return {it->first, *it->second}; - } - - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { - return operator*(); - } - - template - friend std::ptrdiff_t operator-(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; - - template - friend bool operator==(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; - - template - friend bool operator<(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; - -private: - It it; -}; - -template -[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it - rhs.it; -} - -template -[[nodiscard]] bool operator==(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it == rhs.it; -} - -template -[[nodiscard]] bool operator!=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -template -[[nodiscard]] bool operator<(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return lhs.it < rhs.it; -} - -template -[[nodiscard]] bool operator>(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return rhs < lhs; -} - -template -[[nodiscard]] bool operator<=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs > rhs); -} - -template -[[nodiscard]] bool operator>=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { - return !(lhs < rhs); -} - -struct registry_context { - template - Type &emplace_hint(const id_type id, Args &&...args) { - return any_cast(data.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); - } - - template - Type &emplace(Args &&...args) { - return emplace_hint(type_id().hash(), std::forward(args)...); - } - - template - bool erase(const id_type id = type_id().hash()) { - const auto it = data.find(id); - return it != data.end() && it->second.type() == type_id() ? (data.erase(it), true) : false; - } - - template - [[nodiscard]] std::add_const_t &at(const id_type id = type_id().hash()) const { - return any_cast &>(data.at(id)); - } - - template - [[nodiscard]] Type &at(const id_type id = type_id().hash()) { - return any_cast(data.at(id)); - } - - template - [[nodiscard]] std::add_const_t *find(const id_type id = type_id().hash()) const { - const auto it = data.find(id); - return it != data.cend() ? any_cast>(&it->second) : nullptr; - } - - template - [[nodiscard]] Type *find(const id_type id = type_id().hash()) { - const auto it = data.find(id); - return it != data.end() ? any_cast(&it->second) : nullptr; - } - - template - [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { - const auto it = data.find(id); - return it != data.end() && it->second.type() == type_id(); - } - -private: - dense_map, identity> data; -}; - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/** - * @brief Fast and reliable entity-component system. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -class basic_registry { - using entity_traits = entt_traits; - using basic_common_type = basic_sparse_set; - - template - using storage_type = typename storage_traits::storage_type; - - template - struct group_handler; - - 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"); - std::conditional_t current{}; - - template - void maybe_valid_if(basic_registry &owner, const Entity entt) { - [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); - - 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 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), ...); - } - } - } - - 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), ...); - } - } - } - }; - - struct group_data { - std::size_t size; - std::unique_ptr group; - bool (*owned)(const id_type) ENTT_NOEXCEPT; - bool (*get)(const id_type) ENTT_NOEXCEPT; - bool (*exclude)(const id_type) ENTT_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.reset(new storage_type{}); - cpool->bind(forward_as_any(*this)); - } - - 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_type placeholder{}; - return placeholder; - } - - auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { - ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available"); - return entity_traits::combine(static_cast(pos), {}); - } - - auto recycle_identifier() ENTT_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(entities[curr]), tombstone); - return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr]))); - } - - auto release_entity(const Entity entity, const typename entity_traits::version_type version) { - const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone)); - entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); - free_list = entity_traits::combine(entity_traits::to_integral(entity), tombstone); - return vers; - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Underlying version type. */ - using version_type = typename entity_traits::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; - /*! @brief Context type. */ - using context = internal::registry_context; - - /*! @brief Default constructor. */ - basic_registry() - : pools{}, - groups{}, - entities{}, - free_list{tombstone}, - vars{} {} - - /** - * @brief Allocates enough memory upon construction to store `count` pools. - * @param count The number of pools to allocate memory for. - */ - basic_registry(const size_type count) - : pools{}, - groups{}, - entities{}, - free_list{tombstone}, - vars{} { - pools.reserve(count); - } - - /** - * @brief Move constructor. - * @param other The instance to move from. - */ - basic_registry(basic_registry &&other) - : pools{std::move(other.pools)}, - groups{std::move(other.groups)}, - entities{std::move(other.entities)}, - free_list{other.free_list}, - vars{std::move(other.vars)} { - for(auto &&curr: pools) { - curr.second->bind(forward_as_any(*this)); - } - } - - /** - * @brief Move assignment operator. - * @param other The instance to move from. - * @return This registry. - */ - basic_registry &operator=(basic_registry &&other) { - pools = std::move(other.pools); - groups = std::move(other.groups); - entities = std::move(other.entities); - free_list = other.free_list; - vars = std::move(other.vars); - - for(auto &&curr: pools) { - curr.second->bind(forward_as_any(*this)); - } - - return *this; - } - - /** - * @brief Returns an iterable object to use to _visit_ a registry. - * - * The iterable object returns a pair that contains the name and a reference - * to the current storage. - * - * @return An iterable object to use to _visit_ the registry. - */ - [[nodiscard]] auto storage() ENTT_NOEXCEPT { - return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}}; - } - - /*! @copydoc storage */ - [[nodiscard]] auto storage() const ENTT_NOEXCEPT { - return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}}; - } - - /** - * @brief Finds the storage associated with a given name, if any. - * @param id Name used to map the storage within the registry. - * @return An iterator to the given storage if it's found, past the end - * iterator otherwise. - */ - [[nodiscard]] auto storage(const id_type id) { - return internal::storage_proxy_iterator{pools.find(id)}; - } - - /** - * @brief Finds the storage associated with a given name, if any. - * @param id Name used to map the storage within the registry. - * @return An iterator to the given storage if it's found, past the end - * iterator otherwise. - */ - [[nodiscard]] auto storage(const id_type id) const { - return internal::storage_proxy_iterator{pools.find(id)}; - } - - /** - * @brief Returns the storage for a given component type. - * @tparam Component 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()) { - if constexpr(std::is_const_v) { - return std::as_const(*this).template storage>(id); - } else { - 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. - * - * @tparam Component 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 { - return assure>(id); - } - - /** - * @brief Returns the number of entities created so far. - * @return Number of entities created so far. - */ - [[nodiscard]] size_type size() const ENTT_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 = entities.size(); - - for(auto curr = free_list; curr != null; --sz) { - curr = entities[entity_traits::to_entity(curr)]; - } - - return sz; - } - - /** - * @brief Increases the capacity (number of entities) of the registry. - * @param cap Desired capacity. - */ - void reserve(const size_type cap) { - entities.reserve(cap); - } - - /** - * @brief Returns the number of entities that a registry has currently - * allocated space for. - * @return Capacity of the registry. - */ - [[nodiscard]] size_type capacity() const ENTT_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 { - return !alive(); - } - - /** - * @brief Direct access to the list of entities of a registry. - * - * The returned pointer is such that range `[data(), data() + size())` is - * always a valid range, even if the registry is empty. - * - * @warning - * This list contains both valid and destroyed entities and isn't suitable - * for direct use. - * - * @return A pointer to the array of entities. - */ - [[nodiscard]] const entity_type *data() const ENTT_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. - */ - [[nodiscard]] entity_type released() const ENTT_NOEXCEPT { - return free_list; - } - - /** - * @brief Checks if an identifier refers to a valid entity. - * @param entity An identifier, either valid or not. - * @return True if the identifier is valid, false otherwise. - */ - [[nodiscard]] bool valid(const entity_type entity) const { - const auto pos = size_type(entity_traits::to_entity(entity)); - return (pos < entities.size() && entities[pos] == entity); - } - - /** - * @brief Returns the actual version for an identifier. - * @param entity A valid identifier. - * @return The version for the given identifier if valid, the tombstone - * version otherwise. - */ - [[nodiscard]] version_type current(const entity_type entity) const { - const auto pos = size_type(entity_traits::to_entity(entity)); - return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone); - } - - /** - * @brief Creates a new entity or recycles a destroyed one. - * @return A valid identifier. - */ - [[nodiscard]] entity_type create() { - return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier(); - } - - /** - * @copybrief create - * - * If the requested entity isn't in use, the suggested identifier is used. - * Otherwise, a new identifier is generated. - * - * @param hint Required identifier. - * @return A valid identifier. - */ - [[nodiscard]] entity_type create(const entity_type hint) { - const auto length = entities.size(); - - if(hint == null || hint == tombstone) { - return create(); - } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { - entities.resize(size_type(req) + 1u, null); - - for(auto pos = length; pos < req; ++pos) { - release_entity(generate_identifier(pos), {}); - } - - return (entities[req] = hint); - } else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) { - return create(); - } else { - auto *it = &free_list; - for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {} - *it = entity_traits::combine(curr, entity_traits::to_integral(*it)); - return (entities[req] = hint); - } - } - - /** - * @brief Assigns each element in a range an identifier. - * - * @sa create - * - * @tparam It Type of 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 create(It first, It last) { - for(; free_list != null && first != last; ++first) { - *first = recycle_identifier(); - } - - const auto length = entities.size(); - entities.resize(length + std::distance(first, last), null); - - for(auto pos = length; first != last; ++first, ++pos) { - *first = entities[pos] = generate_identifier(pos); - } - } - - /** - * @brief Assigns identifiers to an empty registry. - * - * This function is intended for use in conjunction with `data`, `size` and - * `destroyed`.
- * Don't try to inject ranges of randomly generated entities nor the _wrong_ - * head for the list of destroyed entities. There is no guarantee that a - * registry will continue to work properly in this case. - * - * @warning - * There must be no entities still alive for this to work properly. - * - * @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. - */ - template - void assign(It first, It last, const entity_type destroyed) { - ENTT_ASSERT(!alive(), "Entities still alive"); - entities.assign(first, last); - free_list = destroyed; - } - - /** - * @brief Releases an identifier. - * - * 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 entity A valid identifier. - * @return The version of the recycled entity. - */ - version_type release(const entity_type entity) { - return release(entity, static_cast(entity_traits::to_version(entity) + 1u)); - } - - /** - * @brief Releases an identifier. - * - * The suggested version or the valid version closest to the suggested one - * is used instead of the implicitly generated version. - * - * @sa release - * - * @param entity A valid identifier. - * @param version A desired version upon destruction. - * @return The version actually assigned to the entity. - */ - version_type release(const entity_type entity, const version_type version) { - ENTT_ASSERT(orphan(entity), "Non-orphan entity"); - return release_entity(entity, version); - } - - /** - * @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); - } - } - - /** - * @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. - * - * @param entity A valid identifier. - * @return The version of the recycled entity. - */ - version_type destroy(const entity_type entity) { - return destroy(entity, static_cast(entity_traits::to_version(entity) + 1u)); - } - - /** - * @brief Destroys an entity and releases its identifier. - * - * The suggested version or the valid version closest to the suggested one - * is used instead of the implicitly generated version. - * - * @sa destroy - * - * @param entity A valid identifier. - * @param version A desired version upon destruction. - * @return The version actually assigned to the entity. - */ - version_type destroy(const entity_type entity, const version_type version) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - - for(size_type pos = pools.size(); pos; --pos) { - pools.begin()[pos - 1u].second->remove(entity); - } - - return release_entity(entity, version); - } - - /** - * @brief Destroys all entities in a range and releases their identifiers. - * - * @sa destroy - * - * @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 destroy(It first, It last) { - for(; first != last; ++first) { - destroy(*first); - } - } - - /** - * @brief Assigns the given component to an entity. - * - * The component must have a proper constructor or be of aggregate type. - * - * @warning - * Attempting to use an invalid entity or to assign a component to an entity - * that already owns it results in undefined behavior. - * - * @tparam Component Type of component to create. - * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. - * @param args Parameters to use to initialize the component. - * @return A reference to the newly created component. - */ - template - decltype(auto) emplace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure().emplace(entity, std::forward(args)...); - } - - /** - * @brief Assigns each entity in a range the given component. - * - * @sa emplace - * - * @tparam Component Type of component to create. - * @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 component to assign. - */ - template - void insert(It first, It last, const Component &value = {}) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure().insert(first, last, value); - } - - /** - * @brief Assigns each entity in a range the given components. - * - * @sa emplace - * - * @tparam Component Type of component to create. - * @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 components. - */ - template::value_type, Component>>> - void insert(EIt first, EIt last, CIt from) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure().insert(first, last, from); - } - - /** - * @brief Assigns or replaces the given component for an entity. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Type of component to assign or replace. - * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. - * @param args Parameters to use to initialize the component. - * @return A reference to the newly created component. - */ - template - decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - auto &cpool = assure(); - - return cpool.contains(entity) - ? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }) - : cpool.emplace(entity, std::forward(args)...); - } - - /** - * @brief Patches the given component for an entity. - * - * The signature of the function should be equivalent to the following: - * - * @code{.cpp} - * void(Component &); - * @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 use an invalid entity or to patch a component of an entity - * that doesn't own it results in undefined behavior. - * - * @tparam Component Type of component to patch. - * @tparam Func Types of the function objects to invoke. - * @param entity A valid identifier. - * @param func Valid function objects. - * @return A reference to the patched component. - */ - template - decltype(auto) patch(const entity_type entity, Func &&...func) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure().patch(entity, std::forward(func)...); - } - - /** - * @brief Replaces the given component for an entity. - * - * The component must have a proper constructor or be of aggregate type. - * - * @warning - * Attempting to use an invalid entity or to replace a component of an - * entity that doesn't own it results in undefined behavior. - * - * @tparam Component Type of component to replace. - * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. - * @param args Parameters to use to initialize the component. - * @return A reference to the component being replaced. - */ - template - decltype(auto) replace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return assure().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }); - } - - /** - * @brief Removes the given components from an entity. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Type of component to remove. - * @tparam Other Other types of components to remove. - * @param entity A valid identifier. - * @return The number of components actually removed. - */ - template - size_type remove(const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return (assure().remove(entity) + ... + assure().remove(entity)); - } - - /** - * @brief Removes the given components from all the entities in a range. - * - * @sa remove - * - * @tparam Component Type of component to remove. - * @tparam Other Other types of components to remove. - * @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. - * @return The number of components actually removed. - */ - template - size_type remove(It first, It last) { - if constexpr(sizeof...(Other) == 0u) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - return assure().remove(std::move(first), std::move(last)); - } else { - size_type count{}; - - for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { - ENTT_ASSERT(valid(*first), "Invalid entity"); - count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); - } - - return count; - } - } - - /** - * @brief Erases the given components from an entity. - * - * @warning - * Attempting to use an invalid entity or to erase a component from an - * entity that doesn't own it results in undefined behavior. - * - * @tparam Component Types of components to erase. - * @tparam Other Other types of components to erase. - * @param entity A valid identifier. - */ - template - void erase(const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - (assure().erase(entity), (assure().erase(entity), ...)); - } - - /** - * @brief Erases the given components from all the entities in a range. - * - * @sa erase - * - * @tparam Component Types of components to erase. - * @tparam Other Other types of components to erase. - * @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 erase(It first, It last) { - if constexpr(sizeof...(Other) == 0u) { - ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); - assure().erase(std::move(first), std::move(last)); - } else { - for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { - ENTT_ASSERT(valid(*first), "Invalid entity"); - std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); - } - } - } - - /** - * @brief Removes all tombstones from a registry or only the pools for the - * given components. - * @tparam Component Types of components for which to clear all tombstones. - */ - template void compact() { - if constexpr(sizeof...(Component) == 0) { + if constexpr(sizeof...(Type) == 0) { for(auto &&curr: pools) { curr.second->compact(); } } else { - (assure().compact(), ...); + (assure().compact(), ...); } } /** - * @brief Checks if an entity has all the given components. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Components for which to perform the check. - * @param entity A valid identifier. - * @return True if the entity has all the components, false otherwise. + * @brief Check if an entity is part of all the given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of all the storage, false otherwise. */ - template - [[nodiscard]] bool all_of(const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return (assure>().contains(entity) && ...); + template + [[nodiscard]] bool all_of(const entity_type entt) const { + return (assure>().contains(entt) && ...); } /** - * @brief Checks if an entity has at least one of the given components. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * - * @tparam Component Components for which to perform the check. - * @param entity A valid identifier. - * @return True if the entity has at least one of the given components, - * false otherwise. + * @brief Check if an entity is part of at least one given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of at least one storage, false + * otherwise. */ - template - [[nodiscard]] bool any_of(const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return (assure>().contains(entity) || ...); + template + [[nodiscard]] bool any_of(const entity_type entt) const { + return (assure>().contains(entt) || ...); } /** * @brief Returns references to the given components for an entity. * * @warning - * Attempting to use an invalid entity or to get a component from an entity - * that doesn't own it results in undefined behavior. + * Attempting to get a component from an entity that doesn't own it results + * in undefined behavior. * - * @tparam Component Types of components to get. - * @param entity A valid identifier. + * @tparam Type Types of components to get. + * @param entt A valid identifier. * @return References to the components owned by the entity. */ - template - [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return view().template get(entity); + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + return (assure>().get(entt), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } } /*! @copydoc get */ - template - [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return view().template get(entity); + 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)), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } } /** @@ -28767,71 +35502,68 @@ public: * In case the entity doesn't own the component, the parameters provided are * used to construct it. * - * @warning - * Attempting to use an invalid entity results in undefined behavior. + * @sa get + * @sa emplace * - * @tparam Component Type of component to get. + * @tparam Type Type of component to get. * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid identifier. + * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return Reference to the component owned by the entity. */ - template - [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) { - ENTT_ASSERT(valid(entity), "Invalid entity"); - auto &cpool = assure(); - return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward(args)...); + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) { + if(auto &cpool = assure(); cpool.contains(entt)) { + return cpool.get(entt); + } else { + return cpool.emplace(entt, std::forward(args)...); + } } /** * @brief Returns pointers to the given components for an entity. * - * @warning - * Attempting to use an invalid entity results in undefined behavior. - * * @note * The registry retains ownership of the pointed-to components. * - * @tparam Component Types of components to get. - * @param entity A valid identifier. + * @tparam Type Types of components to get. + * @param entt A valid identifier. * @return Pointers to the components owned by the entity. */ - template - [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - - if constexpr(sizeof...(Component) == 1) { - const auto &cpool = assure...>(); - return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr; + 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; } else { - return std::make_tuple(try_get(entity)...); + return std::make_tuple(try_get(entt)...); } } /*! @copydoc try_get */ - template - [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { - if constexpr(sizeof...(Component) == 1) { - return (const_cast(std::as_const(*this).template try_get(entity)), ...); + 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)), ...); } else { - return std::make_tuple(try_get(entity)...); + return std::make_tuple(try_get(entt)...); } } /** * @brief Clears a whole registry or the pools for the given components. - * @tparam Component Types of components to remove from their entities. + * @tparam Type Types of components to remove from their entities. */ - template + template void clear() { - if constexpr(sizeof...(Component) == 0) { + if constexpr(sizeof...(Type) == 0) { for(auto &&curr: pools) { curr.second->clear(); } each([this](const auto entity) { this->release(entity); }); } else { - (assure().clear(), ...); + (assure().clear(), ...); } } @@ -28852,12 +35584,12 @@ public: template void each(Func func) const { if(free_list == null) { - for(auto pos = entities.size(); pos; --pos) { - func(entities[pos - 1]); + for(auto pos = epool.size(); pos; --pos) { + func(epool[pos - 1]); } } else { - for(auto pos = entities.size(); pos; --pos) { - if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { + for(auto pos = epool.size(); pos; --pos) { + if(const auto entity = epool[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { func(entity); } } @@ -28866,12 +35598,11 @@ public: /** * @brief Checks if an entity has components assigned. - * @param entity A valid identifier. + * @param entt A valid identifier. * @return True if the entity has no components assigned, false otherwise. */ - [[nodiscard]] bool orphan(const entity_type entity) const { - ENTT_ASSERT(valid(entity), "Invalid entity"); - return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); }); + [[nodiscard]] bool orphan(const entity_type entt) const { + return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); }); } /** @@ -28889,12 +35620,12 @@ public: * * @sa sink * - * @tparam Component Type of component of which to get the sink. + * @tparam Type Type of component of which to get the sink. * @return A temporary sink object. */ - template + template [[nodiscard]] auto on_construct() { - return assure().on_construct(); + return assure().on_construct(); } /** @@ -28912,12 +35643,12 @@ public: * * @sa sink * - * @tparam Component Type of component of which to get the sink. + * @tparam Type Type of component of which to get the sink. * @return A temporary sink object. */ - template + template [[nodiscard]] auto on_update() { - return assure().on_update(); + return assure().on_update(); } /** @@ -28935,12 +35666,12 @@ public: * * @sa sink * - * @tparam Component Type of component of which to get the sink. + * @tparam Type Type of component of which to get the sink. * @return A temporary sink object. */ - template + template [[nodiscard]] auto on_destroy() { - return assure().on_destroy(); + return assure().on_destroy(); } /** @@ -28951,20 +35682,22 @@ public: * Creating and destroying a view is an incredibly cheap operation. As a * rule of thumb, storing a view should never be an option. * - * @tparam Component Type of component used to construct the view. + * @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. * @return A newly created view. */ - template - [[nodiscard]] basic_view, std::add_const_t...>, exclude_t> view(exclude_t = {}) const { - return {assure>(), assure>()..., assure()...}; + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = {}) const { + return {assure>(), assure>()..., assure>()...}; } /*! @copydoc view */ - template - [[nodiscard]] basic_view, exclude_t> view(exclude_t = {}) { - return {assure>(), assure>()..., assure()...}; + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = {}) { + return {assure>(), assure>()..., assure>()...}; } /** @@ -28986,13 +35719,14 @@ public: * The group takes the ownership of the pools and arrange components so as * to iterate them as fast as possible. * - * @tparam Owned Types of components owned by the group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components used to filter the group. + * @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 A newly created group. */ template - [[nodiscard]] basic_group, get_t, exclude_t> group(get_t, exclude_t = {}) { + [[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"); @@ -29006,7 +35740,7 @@ public: return gdata.size == size && (gdata.owned(type_hash>::value()) && ...) && (gdata.get(type_hash>::value()) && ...) - && (gdata.exclude(type_hash::value()) && ...); + && (gdata.exclude(type_hash>::value()) && ...); }); if(it != groups.cend()) { @@ -29014,10 +35748,10 @@ public: } else { group_data candidate = { size, - {new handler_type{}, [](void *instance) { delete static_cast(instance); }}, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, - []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash::value()) || ...); }, + 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()); @@ -29030,7 +35764,7 @@ public: } 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())); + const auto sz = overlapping + (0u + ... + gdata.get(type_hash>::value())) + (0u + ... + gdata.exclude(type_hash>::value())); return !overlapping || ((sz == size) || (sz == gdata.size)); }; @@ -29051,11 +35785,11 @@ public: (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), ...); + (on_destroy>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (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), ...); + (on_construct>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); if constexpr(sizeof...(Owned) == 0) { for(const auto entity: view(exclude)) { @@ -29069,17 +35803,18 @@ public: } } - return {handler->current, std::get> &>(cpools)..., std::get> &>(cpools)...}; + return {handler->current, std::get> &>(cpools)..., std::get> &>(cpools)...}; } /*! @copydoc group */ template - [[nodiscard]] basic_group...>, get_t...>, exclude_t> group_if_exists(get_t, exclude_t = {}) const { + [[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()) && ...); + && (gdata.exclude(type_hash>::value()) && ...); }); if(it == groups.cend()) { @@ -29090,40 +35825,28 @@ public: } } - /*! @copydoc group */ - template - [[nodiscard]] basic_group, get_t<>, exclude_t> group(exclude_t = {}) { - return group(get_t<>{}, exclude); - } - - /*! @copydoc group */ - template - [[nodiscard]] basic_group...>, get_t<>, exclude_t> group_if_exists(exclude_t = {}) const { - return group_if_exists...>(get_t<>{}, exclude); - } - /** * @brief Checks whether the given components belong to any group. * @tparam Component 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()) || ...); }); + 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 Types of components owned by the group. - * @tparam Get Types of components observed by the group. - * @tparam Exclude Types of components used to filter the group. + * @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> &) ENTT_NOEXCEPT { + [[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); }; + 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(); } @@ -29138,7 +35861,7 @@ public: * * @code{.cpp} * bool(const Entity, const Entity); - * bool(const Component &, const Component &); + * bool(const Type &, const Type &); * @endcode * * Moreover, it shall induce a _strict weak ordering_ on the values.
@@ -29154,7 +35877,7 @@ public: * @warning * Pools of components owned by a group cannot be sorted. * - * @tparam Component Type of components to sort. + * @tparam Type Type of components to sort. * @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. @@ -29162,10 +35885,10 @@ public: * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ - template + template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { - ENTT_ASSERT(!owned(), "Cannot sort owned storage"); - auto &cpool = assure(); + ENTT_ASSERT(!owned(), "Cannot sort owned storage"); + auto &cpool = assure(); if constexpr(std::is_invocable_v) { auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; @@ -29204,21 +35927,22 @@ public: * @brief Returns the context object, that is, a general purpose container. * @return The context object, that is, a general purpose container. */ - context &ctx() ENTT_NOEXCEPT { + context &ctx() noexcept { return vars; } /*! @copydoc ctx */ - const context &ctx() const ENTT_NOEXCEPT { + const context &ctx() const noexcept { return vars; } private: - dense_map, identity> pools; - std::vector groups; - std::vector entities; - entity_type free_list; 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; }; } // namespace entt @@ -29230,18 +35954,14 @@ private: #define ENTT_ENTITY_RUNTIME_VIEW_HPP #include +#include #include -#include #include #include -// #include "../config/config.h" - // #include "entity.hpp" // #include "fwd.hpp" -// #include "sparse_set.hpp" - namespace entt { @@ -29269,13 +35989,13 @@ public: using reference = typename iterator_type::reference; using iterator_category = std::bidirectional_iterator_tag; - runtime_view_iterator() ENTT_NOEXCEPT + constexpr runtime_view_iterator() noexcept : pools{}, filter{}, it{}, tombstone_check{} {} - runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) ENTT_NOEXCEPT + runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) noexcept : pools{&cpools}, filter{&ignore}, it{curr}, @@ -29305,25 +36025,25 @@ public: return operator--(), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] pointer operator->() const noexcept { return it.operator->(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] reference operator*() const noexcept { return *operator->(); } - [[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept { return it == other.it; } - [[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept { return !(*this == other); } private: - const std::vector *pools; - const std::vector *filter; + const std::vector *pools; + const std::vector *filter; iterator_type it; bool tombstone_check; }; @@ -29335,15 +36055,6 @@ private: * @endcond */ -/** - * @brief Runtime view implementation. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. - */ -template -struct basic_runtime_view; - /** * @brief Generic runtime view. * @@ -29380,31 +36091,105 @@ struct basic_runtime_view; * 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. * - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Common base type. * @tparam Allocator Type of allocator used to manage memory and elements. */ -template -struct basic_runtime_view> { +template +class basic_runtime_view { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = typename Type::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = basic_sparse_set; + using base_type = Type; /*! @brief Bidirectional iterator type. */ using iterator = internal::runtime_view_iterator; /*! @brief Default constructor to use to create empty, invalid views. */ - basic_runtime_view() ENTT_NOEXCEPT - : pools{}, - filter{} {} + basic_runtime_view() noexcept + : basic_runtime_view{allocator_type{}} {} + + /** + * @brief Constructs an empty, invalid view with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_runtime_view(const allocator_type &allocator) + : pools{allocator}, + filter{allocator} {} + + /*! @brief Default copy constructor. */ + basic_runtime_view(const basic_runtime_view &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator) + : pools{other.pools, allocator}, + filter{other.filter, allocator} {} + + /*! @brief Default move constructor. */ + basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator) + : pools{std::move(other.pools), allocator}, + filter{std::move(other.filter), allocator} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + basic_runtime_view &operator=(const basic_runtime_view &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Exchanges the contents with those of a given view. + * @param other View to exchange the content with. + */ + void swap(basic_runtime_view &other) { + using std::swap; + swap(pools, other.pools); + swap(filter, other.filter); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.get_allocator(); + } + + /*! @brief Clears the view. */ + void clear() { + pools.clear(); + filter.clear(); + } /** * @brief Appends an opaque storage object to a runtime view. * @param base An opaque reference to a storage object. * @return This runtime view. */ - basic_runtime_view &iterate(const base_type &base) { + basic_runtime_view &iterate(base_type &base) { if(pools.empty() || !(base.size() < pools[0u]->size())) { pools.push_back(&base); } else { @@ -29419,7 +36204,7 @@ struct basic_runtime_view> { * @param base An opaque reference to a storage object. * @return This runtime view. */ - basic_runtime_view &exclude(const base_type &base) { + basic_runtime_view &exclude(base_type &base) { filter.push_back(&base); return *this; } @@ -29495,191 +36280,8 @@ struct basic_runtime_view> { } private: - std::vector pools; - std::vector filter; -}; - -} // namespace entt - -#endif - -// #include "entity/sigh_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 { - -/** - * @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_iterator = typename Type::basic_iterator; - - template - void notify_destruction(basic_iterator first, basic_iterator last, Func func) { - ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); - - for(; first != last; ++first) { - const auto entt = *first; - destruction.publish(*owner, entt); - const auto it = Type::find(entt); - func(it, it + 1u); - } - } - - void swap_and_pop(basic_iterator first, basic_iterator last) final { - notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); }); - } - - void in_place_pop(basic_iterator first, basic_iterator last) final { - notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); }); - } - - basic_iterator try_emplace(const typename 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); - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = typename Type::entity_type; - - /*! @brief Inherited constructors. */ - using Type::Type; - - /** - * @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() ENTT_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() ENTT_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() ENTT_NOEXCEPT { - return sink{destruction}; - } - - /** - * @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 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 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)...); - - for(auto it = construction.empty() ? last : first; it != last; ++it) { - construction.publish(*owner, *it); - } - } - - /** - * @brief Forwards variables to mixins, if any. - * @param value A variable wrapped in an opaque container. - */ - void bind(any value) ENTT_NOEXCEPT final { - auto *reg = any_cast>(&value); - owner = reg ? reg : owner; - Type::bind(std::move(value)); - } - -private: - sigh &, const entity_type)> construction{}; - sigh &, const entity_type)> destruction{}; - sigh &, const entity_type)> update{}; - basic_registry *owner{}; + container_type pools; + container_type filter; }; } // namespace entt @@ -29709,7 +36311,7 @@ private: // #include "fwd.hpp" -// #include "registry.hpp" +// #include "view.hpp" namespace entt { @@ -29722,15 +36324,15 @@ namespace entt { * This type can be used in both cases if provided with a correctly configured * output archive. * - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Registry Basic registry type. */ -template +template class basic_snapshot { - using entity_traits = entt_traits; + 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>(); + const auto view = reg->template view(); archive(typename entity_traits::entity_type(sz)); while(first != last) { @@ -29756,21 +36358,23 @@ class basic_snapshot { } public: + /*! Basic registry type. */ + using registry_type = Registry; /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = typename registry_type::entity_type; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ - basic_snapshot(const basic_registry &source) ENTT_NOEXCEPT + basic_snapshot(const registry_type &source) noexcept : reg{&source} {} /*! @brief Default move constructor. */ - basic_snapshot(basic_snapshot &&) ENTT_NOEXCEPT = default; + basic_snapshot(basic_snapshot &&) noexcept = default; /*! @brief Default move assignment operator. @return This snapshot. */ - basic_snapshot &operator=(basic_snapshot &&) ENTT_NOEXCEPT = default; + basic_snapshot &operator=(basic_snapshot &&) noexcept = default; /** * @brief Puts aside all the entities from the underlying registry. @@ -29840,7 +36444,7 @@ public: } private: - const basic_registry *reg; + const registry_type *reg; }; /** @@ -29851,57 +36455,59 @@ private: * originally had.
* An example of use is the implementation of a save/restore utility. * - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Registry Basic registry type. */ -template +template class basic_snapshot_loader { - using entity_traits = entt_traits; + using entity_traits = entt_traits; - template + template void assign(Archive &archive) const { typename entity_traits::entity_type length{}; entity_type entt; archive(length); - if constexpr(ignore_as_empty_v) { + 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); + reg->template emplace(entt); } } else { - Type instance; + 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)); + reg->template emplace(entt, std::move(instance)); } } } public: + /*! Basic registry type. */ + using registry_type = Registry; /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = typename registry_type::entity_type; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ - basic_snapshot_loader(basic_registry &source) ENTT_NOEXCEPT + basic_snapshot_loader(registry_type &source) noexcept : reg{&source} { // restoring a snapshot as a whole requires a clean registry ENTT_ASSERT(reg->empty(), "Registry must be empty"); } /*! @brief Default move constructor. */ - basic_snapshot_loader(basic_snapshot_loader &&) ENTT_NOEXCEPT = default; + basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default; /*! @brief Default move assignment operator. @return This loader. */ - basic_snapshot_loader &operator=(basic_snapshot_loader &&) ENTT_NOEXCEPT = default; + basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; /** * @brief Restores entities that were in use during serialization. @@ -29969,7 +36575,7 @@ public: } private: - basic_registry *reg; + registry_type *reg; }; /** @@ -29986,13 +36592,13 @@ private: * the requirement of transferring somehow parts of the representation side to * side. * - * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Registry Basic registry type. */ -template +template class basic_continuous_loader { - using entity_traits = entt_traits; + using entity_traits = entt_traits; - void destroy(Entity entt) { + 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)); @@ -30000,7 +36606,7 @@ class basic_continuous_loader { } } - void restore(Entity entt) { + void restore(typename Registry::entity_type entt) { const auto it = remloc.find(entt); if(it == remloc.cend()) { @@ -30049,9 +36655,9 @@ class basic_continuous_loader { } } - template - void update([[maybe_unused]] Other &instance, [[maybe_unused]] Member Type::*member) { - if constexpr(!std::is_same_v) { + template + void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) { + if constexpr(!std::is_same_v) { return; } else if constexpr(std::is_same_v) { instance.*member = map(instance.*member); @@ -30072,40 +36678,42 @@ class basic_continuous_loader { } } - template - void assign(Archive &archive, [[maybe_unused]] Member Type::*...member) { + 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) { + if constexpr(ignore_as_empty_v) { while(length--) { archive(entt); restore(entt); - reg->template emplace_or_replace(map(entt)); + reg->template emplace_or_replace(map(entt)); } } else { - Other instance; + Component instance; while(length--) { archive(entt, instance); (update(instance, member), ...); restore(entt); - reg->template emplace_or_replace(map(entt), std::move(instance)); + reg->template emplace_or_replace(map(entt), std::move(instance)); } } } public: + /*! Basic registry type. */ + using registry_type = Registry; /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = typename registry_type::entity_type; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ - basic_continuous_loader(basic_registry &source) ENTT_NOEXCEPT + basic_continuous_loader(registry_type &source) noexcept : reg{&source} {} /*! @brief Default move constructor. */ @@ -30159,14 +36767,14 @@ public: * * @tparam Component Type of component to restore. * @tparam Archive Type of input archive. - * @tparam Type Types of components to update with local counterparts. + * @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 Type::*...member) { + template + basic_continuous_loader &component(Archive &archive, Member Other::*...member) { (remove_if_exists(), ...); (assign(archive, member...), ...); return *this; @@ -30227,7 +36835,7 @@ public: * @param entt A valid identifier. * @return True if `entity` is managed by the loader, false otherwise. */ - [[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] bool contains(entity_type entt) const noexcept { return (remloc.find(entt) != remloc.cend()); } @@ -30236,7 +36844,7 @@ public: * @param entt A valid identifier. * @return The local identifier if any, the null entity otherwise. */ - [[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] entity_type map(entity_type entt) const noexcept { const auto it = remloc.find(entt); entity_type other = null; @@ -30249,7 +36857,7 @@ public: private: dense_map> remloc; - basic_registry *reg; + registry_type *reg; }; } // namespace entt @@ -30298,63 +36906,63 @@ struct sparse_set_iterator final { using difference_type = typename Container::difference_type; using iterator_category = std::random_access_iterator_tag; - sparse_set_iterator() ENTT_NOEXCEPT + constexpr sparse_set_iterator() noexcept : packed{}, offset{} {} - sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept : packed{std::addressof(ref)}, offset{idx} {} - sparse_set_iterator &operator++() ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator++() noexcept { return --offset, *this; } - sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator++(int) noexcept { sparse_set_iterator orig = *this; return ++(*this), orig; } - sparse_set_iterator &operator--() ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator--() noexcept { return ++offset, *this; } - sparse_set_iterator operator--(int) ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator--(int) noexcept { sparse_set_iterator orig = *this; return operator--(), orig; } - sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } - sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { sparse_set_iterator copy = *this; return (copy += value); } - sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return packed->data()[index() - value]; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return packed->data() + index(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } - [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } @@ -30364,37 +36972,37 @@ private: }; template -[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template -[[nodiscard]] bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template -[[nodiscard]] bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template -[[nodiscard]] bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -30471,7 +37079,7 @@ class basic_sparse_set { } auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; - ENTT_ASSERT(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "Slot not available"); + ENTT_ASSERT(elem == null, "Slot not available"); return elem; } @@ -30500,31 +37108,47 @@ protected: using basic_iterator = internal::sparse_set_iterator; /** - * @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. + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. */ - virtual void swap_and_pop(basic_iterator first, basic_iterator last) { - for(; first != last; ++first) { - sparse_ref(packed.back()) = entity_traits::combine(static_cast(first.index()), entity_traits::to_integral(packed.back())); - const auto entt = std::exchange(packed[first.index()], packed.back()); - // unnecessary but it helps to detect nasty bugs - ENTT_ASSERT((packed.back() = tombstone, true), ""); - // lazy self-assignment guard - sparse_ref(entt) = null; - packed.pop_back(); - } + 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())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); } + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + 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)); + } + +protected: /** * @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. */ - virtual void in_place_pop(basic_iterator first, basic_iterator last) { - for(; first != last; ++first) { - sparse_ref(*first) = null; - packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast(first.index()), entity_traits::reserved)); + virtual void pop(basic_iterator first, basic_iterator last) { + if(mode == deletion_policy::swap_and_pop) { + for(; first != last; ++first) { + swap_and_pop(first); + } + } else { + for(; first != last; ++first) { + in_place_pop(first); + } } } @@ -30553,11 +37177,11 @@ public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = typename entity_traits::value_type; /*! @brief Underlying version type. */ using version_type = typename entity_traits::version_type; /*! @brief Unsigned integer type. */ - using size_type = typename packed_container_type::size_type; + using size_type = std::size_t; /*! @brief Pointer type to contained entities. */ using pointer = typename packed_container_type::const_pointer; /*! @brief Random access iterator type. */ @@ -30606,7 +37230,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT + basic_sparse_set(basic_sparse_set &&other) noexcept : sparse{std::move(other.sparse)}, packed{std::move(other.packed)}, info{other.info}, @@ -30618,7 +37242,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept : sparse{std::move(other.sparse), allocator}, packed{std::move(other.packed), allocator}, info{other.info}, @@ -30637,7 +37261,7 @@ public: * @param other The instance to move from. * @return This sparse set. */ - basic_sparse_set &operator=(basic_sparse_set &&other) ENTT_NOEXCEPT { + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); release_sparse_pages(); @@ -30666,7 +37290,7 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return packed.get_allocator(); } @@ -30674,7 +37298,7 @@ public: * @brief Returns the deletion policy of a sparse set. * @return The deletion policy of the sparse set. */ - [[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT { + [[nodiscard]] deletion_policy policy() const noexcept { return mode; } @@ -30695,7 +37319,7 @@ public: * allocated space for. * @return Capacity of the sparse set. */ - [[nodiscard]] virtual size_type capacity() const ENTT_NOEXCEPT { + [[nodiscard]] virtual size_type capacity() const noexcept { return packed.capacity(); } @@ -30714,7 +37338,7 @@ public: * * @return Extent of the sparse set. */ - [[nodiscard]] size_type extent() const ENTT_NOEXCEPT { + [[nodiscard]] size_type extent() const noexcept { return sparse.size() * entity_traits::page_size; } @@ -30728,7 +37352,7 @@ public: * * @return Number of elements. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.size(); } @@ -30736,7 +37360,7 @@ public: * @brief Checks whether a sparse set is empty. * @return True if the sparse set is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.empty(); } @@ -30744,7 +37368,7 @@ public: * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. */ - [[nodiscard]] pointer data() const ENTT_NOEXCEPT { + [[nodiscard]] pointer data() const noexcept { return packed.data(); } @@ -30757,13 +37381,13 @@ public: * * @return An iterator to the first entity of the sparse set. */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { const auto pos = static_cast(packed.size()); return iterator{packed, pos}; } /*! @copydoc begin */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } @@ -30777,12 +37401,12 @@ public: * @return An iterator to the element following the last entity of a sparse * set. */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] iterator end() const noexcept { return iterator{packed, {}}; } /*! @copydoc end */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return end(); } @@ -30796,12 +37420,12 @@ public: * @return An iterator to the first entity of the reversed internal packed * array. */ - [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return std::make_reverse_iterator(end()); } /*! @copydoc rbegin */ - [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); } @@ -30815,12 +37439,12 @@ public: * @return An iterator to the element following the last entity of the * reversed sparse set. */ - [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rend() const noexcept { return std::make_reverse_iterator(begin()); } /*! @copydoc rend */ - [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crend() const noexcept { return rend(); } @@ -30830,7 +37454,7 @@ public: * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? --(end() - index(entt)) : end(); } @@ -30839,7 +37463,7 @@ public: * @param entt A valid identifier. * @return True if the sparse set contains the entity, false otherwise. */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] bool contains(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); constexpr auto cap = entity_traits::to_entity(null); // testing versions permits to avoid accessing the packed array @@ -30852,7 +37476,7 @@ public: * @return The version for the given identifier if present, the tombstone * version otherwise. */ - [[nodiscard]] version_type current(const entity_type entt) const ENTT_NOEXCEPT { + [[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; @@ -30868,7 +37492,7 @@ public: * @param entt A valid identifier. * @return The position of the entity in the sparse set. */ - [[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT { + [[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))); } @@ -30878,7 +37502,7 @@ public: * @param pos The position for which to return the entity. * @return The entity at specified location if any, a null entity otherwise. */ - [[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT { + [[nodiscard]] entity_type at(const size_type pos) const noexcept { return pos < packed.size() ? packed[pos] : null; } @@ -30887,7 +37511,7 @@ public: * @param pos The position for which to return the entity. * @return The entity at specified location. */ - [[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { + [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); return packed[pos]; } @@ -30902,12 +37526,12 @@ public: * @param entt A valid identifier. * @return An opaque pointer to the element assigned to the entity, if any. */ - const void *get(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] const void *get(const entity_type entt) const noexcept { return get_at(index(entt)); } /*! @copydoc get */ - void *get(const entity_type entt) ENTT_NOEXCEPT { + [[nodiscard]] void *get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } @@ -30938,6 +37562,7 @@ public: */ 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; } @@ -30975,7 +37600,7 @@ public: */ void erase(const entity_type entt) { const auto it = --(end() - index(entt)); - (mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_pop(it, it + 1u); + pop(it, it + 1u); } /** @@ -30990,7 +37615,7 @@ public: template void erase(It first, It last) { if constexpr(std::is_same_v) { - (mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_pop(first, last); + pop(first, last); } else { for(; first != last; ++first) { erase(*first); @@ -31187,12 +37812,12 @@ public: /*! @brief Clears a sparse set. */ void clear() { if(const auto last = end(); free_list == null) { - in_place_pop(begin(), last); + pop(begin(), last); } else { for(auto &&entity: *this) { // tombstone filter on itself if(const auto it = find(entity); it != last) { - in_place_pop(it, it + 1u); + pop(it, it + 1u); } } } @@ -31205,12 +37830,12 @@ public: * @brief Returned value type, if any. * @return Returned value type, if any. */ - const type_info &type() const ENTT_NOEXCEPT { + const type_info &type() const noexcept { return *info; } - /*! @brief Forwards variables to mixins, if any. */ - virtual void bind(any) ENTT_NOEXCEPT {} + /*! @brief Forwards variables to derived classes, if any. */ + virtual void bind(any) noexcept {} private: sparse_container_type sparse; @@ -31251,10 +37876,10 @@ private: // #include "fwd.hpp" -// #include "sigh_storage_mixin.hpp" - // #include "sparse_set.hpp" +// #include "storage_mixin.hpp" + namespace entt { @@ -31271,7 +37896,7 @@ class storage_iterator final { using container_type = std::remove_const_t; using alloc_traits = std::allocator_traits; - using comp_traits = component_traits; + using comp_traits = component_traits>; using iterator_traits = std::iterator_traits, @@ -31285,68 +37910,67 @@ public: using difference_type = typename iterator_traits::difference_type; using iterator_category = std::random_access_iterator_tag; - storage_iterator() ENTT_NOEXCEPT = default; + constexpr storage_iterator() noexcept = default; - storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept : packed{ref}, offset{idx} {} template, typename = std::enable_if_t> - storage_iterator(const storage_iterator> &other) ENTT_NOEXCEPT - : packed{other.packed}, - offset{other.offset} {} + constexpr storage_iterator(const storage_iterator> &other) noexcept + : storage_iterator{other.packed, other.offset} {} - storage_iterator &operator++() ENTT_NOEXCEPT { + constexpr storage_iterator &operator++() noexcept { return --offset, *this; } - storage_iterator operator++(int) ENTT_NOEXCEPT { + constexpr storage_iterator operator++(int) noexcept { storage_iterator orig = *this; return ++(*this), orig; } - storage_iterator &operator--() ENTT_NOEXCEPT { + constexpr storage_iterator &operator--() noexcept { return ++offset, *this; } - storage_iterator operator--(int) ENTT_NOEXCEPT { + constexpr storage_iterator operator--(int) noexcept { storage_iterator orig = *this; return operator--(), orig; } - storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr storage_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } - storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr storage_iterator operator+(const difference_type value) const noexcept { storage_iterator copy = *this; return (copy += value); } - storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr storage_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr storage_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[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)]; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { const auto pos = index(); return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } - [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } @@ -31356,37 +37980,37 @@ private: }; template -[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template -[[nodiscard]] bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template -[[nodiscard]] bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template -[[nodiscard]] bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -31402,46 +38026,47 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - extended_storage_iterator() = default; + constexpr extended_storage_iterator() + : it{} {} - extended_storage_iterator(It base, Other... other) + constexpr extended_storage_iterator(It base, Other... other) : it{base, other...} {} template && ...) && (std::is_constructible_v && ...)>> - extended_storage_iterator(const extended_storage_iterator &other) + constexpr extended_storage_iterator(const extended_storage_iterator &other) : it{other.it} {} - extended_storage_iterator &operator++() ENTT_NOEXCEPT { + constexpr extended_storage_iterator &operator++() noexcept { return ++std::get(it), (++std::get(it), ...), *this; } - extended_storage_iterator operator++(int) ENTT_NOEXCEPT { + constexpr extended_storage_iterator operator++(int) noexcept { extended_storage_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {*std::get(it), *std::get(it)...}; } template - friend bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; private: std::tuple it; }; template -[[nodiscard]] bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_NOEXCEPT { +[[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]] bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -31463,20 +38088,20 @@ template * 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 Entity A valid entity type (see entt_traits for more details). * @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 +template class basic_storage: public basic_sparse_set::template rebind_alloc> { - static_assert(std::is_move_constructible_v && std::is_move_assignable_v, "The type must be at least move constructible/assignable"); - 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)]; } @@ -31512,12 +38137,7 @@ class basic_storage: public basic_sparse_set(args)...); } ENTT_CATCH { - if constexpr(comp_traits::in_place_delete) { - base_type::in_place_pop(it, it + 1u); - } else { - base_type::swap_and_pop(it, it + 1u); - } - + base_type::pop(it, it + 1u); ENTT_THROW; } @@ -31551,42 +38171,51 @@ private: return std::addressof(element_at(pos)); } - void swap_at(const std::size_t lhs, const std::size_t rhs) final { - using std::swap; - swap(element_at(lhs), element_at(rhs)); - } + 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"); - void move_element(const std::size_t from, const std::size_t to) final { - 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)); - } - -protected: - /** - * @brief Erases elements from a storage. - * @param first An iterator to the first element to erase. - * @param last An iterator past the last element to erase. - */ - void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { - for(; first != last; ++first) { - auto &elem = element_at(base_type::size() - 1u); - // destroying on exit allows reentrant destructors - [[maybe_unused]] auto unused = std::exchange(element_at(static_cast(first.index())), std::move(elem)); - std::destroy_at(std::addressof(elem)); - base_type::swap_and_pop(first, first + 1u); + if constexpr(!is_pinned_type_v) { + using std::swap; + swap(element_at(lhs), element_at(rhs)); } } + 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"); + + 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)); + } + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = typename underlying_type::basic_iterator; + /** - * @brief Erases elements from a storage. - * @param first An iterator to the first element to erase. - * @param last An iterator past the last element to erase. + * @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 in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { + void pop(basic_iterator first, basic_iterator last) override { for(; first != last; ++first) { - base_type::in_place_pop(first, first + 1u); - std::destroy_at(std::addressof(element_at(static_cast(first.index())))); + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + 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); + } } } @@ -31597,7 +38226,7 @@ protected: * @param force_back Force back insertion. * @return Iterator pointing to the emplaced element. */ - typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, const bool force_back, const void *value) override { + 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)); @@ -31657,7 +38286,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_storage(basic_storage &&other) ENTT_NOEXCEPT + basic_storage(basic_storage &&other) noexcept : base_type{std::move(other)}, packed{std::move(other.packed)} {} @@ -31666,7 +38295,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - basic_storage(basic_storage &&other, const allocator_type &allocator) ENTT_NOEXCEPT + 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"); @@ -31682,7 +38311,7 @@ public: * @param other The instance to move from. * @return This storage. */ - basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT { + 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"); shrink_to_size(0u); @@ -31707,7 +38336,7 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{packed.second()}; } @@ -31731,7 +38360,7 @@ public: * allocated space for. * @return Capacity of the storage. */ - [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT override { + [[nodiscard]] size_type capacity() const noexcept override { return packed.first().size() * comp_traits::page_size; } @@ -31745,12 +38374,12 @@ public: * @brief Direct access to the array of objects. * @return A pointer to the array of objects. */ - [[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT { + [[nodiscard]] const_pointer raw() const noexcept { return packed.first().data(); } /*! @copydoc raw */ - [[nodiscard]] pointer raw() ENTT_NOEXCEPT { + [[nodiscard]] pointer raw() noexcept { return packed.first().data(); } @@ -31762,18 +38391,18 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { const auto pos = static_cast(base_type::size()); return const_iterator{&packed.first(), pos}; } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { const auto pos = static_cast(base_type::size()); return iterator{&packed.first(), pos}; } @@ -31788,17 +38417,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return const_iterator{&packed.first(), {}}; } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return iterator{&packed.first(), {}}; } @@ -31811,17 +38440,17 @@ public: * * @return An iterator to the first instance of the reversed internal array. */ - [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return std::make_reverse_iterator(cend()); } /*! @copydoc crbegin */ - [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return crbegin(); } /*! @copydoc rbegin */ - [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rbegin() noexcept { return std::make_reverse_iterator(end()); } @@ -31835,17 +38464,17 @@ public: * @return An iterator to the element following the last instance of the * reversed internal array. */ - [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator crend() const noexcept { return std::make_reverse_iterator(cbegin()); } /*! @copydoc crend */ - [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { + [[nodiscard]] const_reverse_iterator rend() const noexcept { return crend(); } /*! @copydoc rend */ - [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { + [[nodiscard]] reverse_iterator rend() noexcept { return std::make_reverse_iterator(begin()); } @@ -31859,12 +38488,12 @@ public: * @param entt A valid identifier. * @return The object assigned to the entity. */ - [[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { return element_at(base_type::index(entt)); } /*! @copydoc get */ - [[nodiscard]] value_type &get(const entity_type entt) ENTT_NOEXCEPT { + [[nodiscard]] value_type &get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } @@ -31873,12 +38502,12 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { return std::forward_as_tuple(get(entt)); } /*! @copydoc get_as_tuple */ - [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) ENTT_NOEXCEPT { + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { return std::forward_as_tuple(get(entt)); } @@ -31967,12 +38596,12 @@ public: * * @return An iterable object to use to _visit_ the storage. */ - [[nodiscard]] iterable each() ENTT_NOEXCEPT { + [[nodiscard]] iterable each() noexcept { return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; } /*! @copydoc each */ - [[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; } @@ -31981,8 +38610,8 @@ private: }; /*! @copydoc basic_storage */ -template -class 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"); @@ -32020,14 +38649,14 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_storage(basic_storage &&other) ENTT_NOEXCEPT = default; + basic_storage(basic_storage &&other) noexcept = default; /** * @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) ENTT_NOEXCEPT + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept : base_type{std::move(other), allocator} {} /** @@ -32035,13 +38664,13 @@ public: * @param other The instance to move from. * @return This storage. */ - basic_storage &operator=(basic_storage &&other) ENTT_NOEXCEPT = default; + basic_storage &operator=(basic_storage &&other) noexcept = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{base_type::get_allocator()}; } @@ -32054,7 +38683,7 @@ public: * * @param entt A valid identifier. */ - void get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + void get([[maybe_unused]] const entity_type entt) const noexcept { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); } @@ -32068,7 +38697,7 @@ public: * @param entt A valid identifier. * @return Returns an empty tuple. */ - [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + [[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{}; } @@ -32121,81 +38750,295 @@ public: * * @return An iterable object to use to _visit_ the storage. */ - [[nodiscard]] iterable each() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; } }; /** - * @brief Provides a common way to access certain properties of storage types. + * @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 Type Type of objects managed by the storage class. + * @tparam Allocator Type of allocator used to manage memory and elements. */ -template -struct storage_traits { - /*! @brief Resulting type after component-to-storage conversion. */ - using storage_type = sigh_storage_mixin>; +template +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; + +/** + * 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>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_for_t = typename storage_for::type; + } // namespace entt #endif -// #include "entity/utility.hpp" -#ifndef ENTT_ENTITY_UTILITY_HPP -#define ENTT_ENTITY_UTILITY_HPP +// #include "entity/storage_mixin.hpp" +#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP +#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP -// #include "../core/type_traits.hpp" +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../signal/sigh.hpp" + +// #include "fwd.hpp" namespace entt { /** - * @brief Alias for exclusion lists. - * @tparam Type List of types. + * @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 -struct exclude_t: 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; -/** - * @brief Variable template for exclusion lists. - * @tparam Type List of types. - */ -template -inline constexpr exclude_t exclude{}; + void pop(basic_iterator first, basic_iterator last) override { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); -/** - * @brief Alias for lists of observed components. - * @tparam Type List of types. - */ -template -struct get_t: type_list {}; + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(*owner, entt); + const auto it = Type::find(entt); + Type::pop(it, it + 1u); + } + } -/** - * @brief Variable template for lists of observed components. - * @tparam Type List of types. - */ -template -inline constexpr get_t get{}; + 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 Alias for lists of owned components. - * @tparam Type List of types. - */ -template -struct owned_t: type_list {}; +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 Variable template for lists of owned components. - * @tparam Type List of types. - */ -template -inline constexpr owned_t owned{}; + /*! @brief Default constructor. */ + sigh_storage_mixin() + : sigh_storage_mixin{allocator_type{}} {} + + /** + * @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 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 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 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 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 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 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 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 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)...); + + for(auto it = construction.empty() ? last : first; it != last; ++it) { + construction.publish(*owner, *it); + } + } + + /** + * @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)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; } // namespace entt @@ -32205,7 +39048,6 @@ inline constexpr owned_t owned{}; #ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP -#include #include #include #include @@ -32227,8 +39069,6 @@ inline constexpr owned_t owned{}; // #include "storage.hpp" -// #include "utility.hpp" - namespace entt { @@ -32239,12 +39079,12 @@ namespace entt { namespace internal { -template +template class view_iterator final { using iterator_type = typename Type::const_iterator; - [[nodiscard]] bool valid() const ENTT_NOEXCEPT { - return ((Component != 0u) || (*it != tombstone)) + [[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); } @@ -32256,102 +39096,108 @@ public: using difference_type = typename iterator_type::difference_type; using iterator_category = std::forward_iterator_tag; - view_iterator() ENTT_NOEXCEPT = default; + constexpr view_iterator() noexcept + : it{}, + last{}, + pools{}, + filter{} {} - view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) ENTT_NOEXCEPT + 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} { - if(it != last && !valid()) { - ++(*this); + while(it != last && !valid()) { + ++it; } } - view_iterator &operator++() ENTT_NOEXCEPT { + view_iterator &operator++() noexcept { while(++it != last && !valid()) {} return *this; } - view_iterator operator++(int) ENTT_NOEXCEPT { + view_iterator operator++(int) noexcept { view_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] pointer operator->() const noexcept { return &*it; } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] reference operator*() const noexcept { return *operator->(); } template - friend bool operator==(const view_iterator &, const view_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; private: iterator_type it; iterator_type last; - std::array pools; + std::array pools; std::array filter; }; template -[[nodiscard]] bool operator==(const view_iterator &lhs, const view_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const view_iterator &lhs, const view_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { return !(lhs == rhs); } -template +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 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; - extended_view_iterator() = default; + constexpr extended_view_iterator() + : it{}, + pools{} {} - extended_view_iterator(It from, std::tuple storage) + extended_view_iterator(It from, std::tuple storage) : it{from}, pools{storage} {} - extended_view_iterator &operator++() ENTT_NOEXCEPT { + extended_view_iterator &operator++() noexcept { return ++it, *this; } - extended_view_iterator operator++(int) ENTT_NOEXCEPT { + extended_view_iterator operator++(int) noexcept { extended_view_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[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); } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] pointer operator->() const noexcept { return operator*(); } template - friend bool operator==(const extended_view_iterator &, const extended_view_iterator &) ENTT_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]] bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -32368,137 +39214,157 @@ template * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ -template +template class basic_view; /** * @brief Multi component view. * - * Multi component views iterate over those entities that have at least all the - * given components in their bags. 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 iterate. + * 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 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 other cases, modifying the pools iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Component Types of components iterated by the view. - * @tparam Exclude Types of components used to filter the view. + * @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> { - template +template +class basic_view, exclude_t> { + using underlying_type = std::common_type_t; + using basic_common_type = std::common_type_t; + + template friend class basic_view; - template - using storage_type = constness_as_t>::storage_type, Comp>; + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; - template - [[nodiscard]] auto pools_to_array(std::index_sequence) const ENTT_NOEXCEPT { - std::size_t pos{}; - std::array other{}; - (static_cast(std::get(pools) == view ? void() : void(other[pos++] = std::get(pools))), ...); + [[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; } - template - [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { - if constexpr(Comp == Other) { + [[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 std::get(pools)->get_as_tuple(std::get<0>(curr)); + return storage().get_as_tuple(std::get<0>(curr)); } } - template - void each(Func func, std::index_sequence) const { - for(const auto curr: std::get(pools)->each()) { - const auto entt = 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); + } - if(((sizeof...(Component) != 1u) || (entt != tombstone)) - && ((Comp == Index || std::get(pools)->contains(entt)) && ...) - && std::apply([entt](const auto *...cpool) { return (!cpool->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)) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { - std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); } else { - std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + 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(std::move(func), seq) : void()), ...); + void pick_and_each(Func &func, std::index_sequence seq) const { + ((&storage() == view ? each(func, seq) : void()), ...); } public: /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ - using base_type = std::common_type_t::base_type...>; + using base_type = basic_common_type; /*! @brief Bidirectional iterator type. */ - using iterator = internal::view_iterator; + using iterator = internal::view_iterator; /*! @brief Iterable view type. */ - using iterable = iterable_adaptor...>>; + using iterable = iterable_adaptor>; /*! @brief Default constructor to use to create empty, invalid views. */ - basic_view() ENTT_NOEXCEPT + basic_view() noexcept : pools{}, filter{}, view{} {} /** * @brief Constructs a multi-type view from a set of storage classes. - * @param component The storage for the types to iterate. - * @param epool The storage for the types used to filter the view. + * @param value The storage for the types to iterate. + * @param exclude The storage for the types used to filter the view. */ - basic_view(storage_type &...component, const storage_type &...epool) ENTT_NOEXCEPT - : pools{&component...}, - filter{&epool...}, - view{(std::min)({&static_cast(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {} + 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...)} {} + + /** + * @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. + */ + 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)} {} /** * @brief Creates a new view driven by a given component in its iterations. - * @tparam Comp Type of component used to drive the iteration. + * @tparam Type Type of component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ - template - [[nodiscard]] basic_view use() const ENTT_NOEXCEPT { - basic_view other{*this}; - other.view = std::get *>(pools); - return other; + template + [[nodiscard]] basic_view use() const noexcept { + return use>(); } /** * @brief Creates a new view driven by a given component in its iterations. - * @tparam Comp Index of the component used to drive the iteration. + * @tparam Index Index of the component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ - template - [[nodiscard]] basic_view use() const ENTT_NOEXCEPT { + template + [[nodiscard]] basic_view use() const noexcept { basic_view other{*this}; - other.view = std::get(pools); + other.view = &storage(); return other; } + /** + * @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 Returns the leading storage of a view. * @return The leading storage of the view. */ - const base_type &handle() const ENTT_NOEXCEPT { + [[nodiscard]] const base_type &handle() const noexcept { return *view; } @@ -32507,26 +39373,26 @@ public: * @tparam Comp Type of component of which to return the storage. * @return The storage for the given component type. */ - template - [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { - return *std::get *>(pools); + template + [[nodiscard]] decltype(auto) storage() const noexcept { + return storage>(); } /** - * @brief Returns the storage for a given component type. - * @tparam Comp Index of component of which to return the storage. - * @return The storage for the given component type. + * @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 ENTT_NOEXCEPT { - return *std::get(pools); + template + [[nodiscard]] decltype(auto) storage() const noexcept { + return *std::get(pools); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ - [[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size_hint() const noexcept { return view->size(); } @@ -32538,8 +39404,8 @@ public: * * @return An iterator to the first entity of the view. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; + [[nodiscard]] iterator begin() const noexcept { + return iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; } /** @@ -32551,8 +39417,8 @@ public: * * @return An iterator to the entity following the last entity of the view. */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; + [[nodiscard]] iterator end() const noexcept { + return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()}; } /** @@ -32560,7 +39426,7 @@ public: * @return The first entity of the view if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type front() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } @@ -32570,7 +39436,7 @@ public: * @return The last entity of the view if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type back() const ENTT_NOEXCEPT { + [[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; @@ -32582,8 +39448,8 @@ public: * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { - return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for{}), filter} : end(); + [[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(); } /** @@ -32592,14 +39458,14 @@ public: * @return The components assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { - return get(entt); + return get(entt); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return view != nullptr; } @@ -32608,7 +39474,7 @@ public: * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { + [[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); } @@ -32620,20 +39486,18 @@ public: * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * - * @tparam Comp Types of components to get. + * @tparam Type 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 { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { + 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...(Comp) == 1) { - return (std::get *>(pools)->get(entt), ...); + } else if constexpr(sizeof...(Type) == 1) { + return (storage>().get(entt), ...); } else { - return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)...); + return std::tuple_cat(storage>().get_as_tuple(entt)...); } } @@ -32651,12 +39515,10 @@ public: */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - if constexpr(sizeof...(Other) == 0) { - return std::get(pools)->get(entt); + return storage().get(entt); } else { - return std::tuple_cat(std::get(pools)->get_as_tuple(entt), std::get(pools)->get_as_tuple(entt)...); + return std::tuple_cat(storage().get_as_tuple(entt), storage().get_as_tuple(entt)...); } } @@ -32680,7 +39542,7 @@ public: */ template void each(Func func) const { - pick_and_each(std::move(func), std::index_sequence_for{}); + pick_and_each(func, std::index_sequence_for{}); } /** @@ -32692,30 +39554,26 @@ public: * * @return An iterable object to use to _visit_ the view. */ - [[nodiscard]] iterable each() const ENTT_NOEXCEPT { + [[nodiscard]] iterable each() const noexcept { return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; } /** * @brief Combines two views in a _more specific_ one (friend function). - * @tparam Get Component list of the view to combine with. - * @tparam Excl Filter list of the view to combine with. + * @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 ENTT_NOEXCEPT { - using view_type = basic_view, exclude_t>; - return std::make_from_tuple(std::tuple_cat( - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools), - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), - std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, filter), - std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); + 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))); } private: - std::tuple *...> pools; - std::array filter; + std::tuple pools; + std::tuple filter; const base_type *view; }; @@ -32730,96 +39588,99 @@ private: * * Iterators aren't invalidated if: * - * * New instances of the given component are created and assigned to entities. - * * The entity currently pointed is modified (as an example, the given - * component 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 other cases, modifying the pool iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Component Type of component iterated by the view. + * @tparam Get Type of storage iterated by the view. */ -template -class basic_view, exclude_t<>, std::void_t>::in_place_delete>>> { - template +template +class basic_view, exclude_t<>, std::void_t::in_place_delete>>> { + template friend class basic_view; - using storage_type = constness_as_t>::storage_type, Component>; - public: /*! @brief Underlying entity identifier. */ - using entity_type = Entity; + 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 storage_type::base_type; + 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()); + using iterable = decltype(std::declval().each()); /*! @brief Default constructor to use to create empty, invalid views. */ - basic_view() ENTT_NOEXCEPT + basic_view() noexcept : pools{}, - filter{}, - view{} {} + filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ - basic_view(storage_type &ref) ENTT_NOEXCEPT + basic_view(Get &ref) noexcept : pools{&ref}, - filter{}, - view{&ref} {} + filter{} {} + + /** + * @brief Constructs a single-type view from a storage class. + * @param ref The storage for the type to iterate. + */ + basic_view(std::tuple ref, std::tuple<> = {}) noexcept + : pools{&std::get<0>(ref)}, + filter{} {} /** * @brief Returns the leading storage of a view. * @return The leading storage of the view. */ - const base_type &handle() const ENTT_NOEXCEPT { - return *view; + [[nodiscard]] const base_type &handle() const noexcept { + return storage(); } /** * @brief Returns the storage for a given component type. - * @tparam Comp Type of component of which to return the storage. + * @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 ENTT_NOEXCEPT { - static_assert(std::is_same_v, "Invalid component type"); - return *std::get<0>(pools); + template + [[nodiscard]] decltype(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 component type. - * @tparam Comp Index of component of which to return the storage. - * @return The storage for the given component type. + * @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 ENTT_NOEXCEPT { - return *std::get(pools); + template + [[nodiscard]] decltype(auto) storage() const noexcept { + return *std::get(pools); } /** * @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 ENTT_NOEXCEPT { - return view->size(); + [[nodiscard]] size_type size() const noexcept { + return handle().size(); } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - return view->empty(); + [[nodiscard]] bool empty() const noexcept { + return handle().empty(); } /** @@ -32830,8 +39691,8 @@ public: * * @return An iterator to the first entity of the view. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return view->begin(); + [[nodiscard]] iterator begin() const noexcept { + return handle().begin(); } /** @@ -32843,8 +39704,8 @@ public: * * @return An iterator to the entity following the last entity of the view. */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return view->end(); + [[nodiscard]] iterator end() const noexcept { + return handle().end(); } /** @@ -32855,8 +39716,8 @@ public: * * @return An iterator to the first entity of the reversed view. */ - [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { - return view->rbegin(); + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return handle().rbegin(); } /** @@ -32870,8 +39731,8 @@ public: * @return An iterator to the entity following the last entity of the * reversed view. */ - [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { - return view->rend(); + [[nodiscard]] reverse_iterator rend() const noexcept { + return handle().rend(); } /** @@ -32879,7 +39740,7 @@ public: * @return The first entity of the view if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type front() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type front() const noexcept { return empty() ? null : *begin(); } @@ -32888,7 +39749,7 @@ public: * @return The last entity of the view if one exists, the null entity * otherwise. */ - [[nodiscard]] entity_type back() const ENTT_NOEXCEPT { + [[nodiscard]] entity_type back() const noexcept { return empty() ? null : *rbegin(); } @@ -32898,8 +39759,8 @@ public: * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { - return contains(entt) ? view->find(entt) : end(); + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? handle().find(entt) : end(); } /** @@ -32917,15 +39778,15 @@ public: * @return The component assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { - return get(entt); + return storage().get(entt); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return view != nullptr; + [[nodiscard]] explicit operator bool() const noexcept { + return std::get<0>(pools) != nullptr; } /** @@ -32933,8 +39794,8 @@ public: * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ - [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return view->contains(entt); + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return handle().contains(entt); } /** @@ -32944,27 +39805,24 @@ public: * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * - * @tparam Comp Type or index of the component to get. + * @tparam Type Type or index of the component to get. * @param entt A valid identifier. * @return The component assigned to the entity. */ - template + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - - if constexpr(sizeof...(Comp) == 0) { - return std::get<0>(pools)->get_as_tuple(entt); + if constexpr(sizeof...(Type) == 0) { + return storage().get_as_tuple(entt); } else { - static_assert(std::is_same_v, "Invalid component type"); - return std::get<0>(pools)->get(entt); + static_assert((std::is_same_v, typename Get::value_type> && ...), "Invalid component type"); + return storage().get(entt); } } /*! @copydoc get */ - template + template [[nodiscard]] decltype(auto) get(const entity_type entt) const { - ENTT_ASSERT(contains(entt), "View does not contain entity"); - return std::get<0>(pools)->get(entt); + return storage().get(entt); } /** @@ -32978,10 +39836,14 @@ public: * forms: * * @code{.cpp} - * void(const entity_type, Component &); - * void(Component &); + * void(const entity_type, Type &); + * void(typename 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. */ @@ -32991,18 +39853,14 @@ public: for(const auto pack: each()) { std::apply(func, pack); } - } else if constexpr(std::is_invocable_v) { - for(auto &&component: *std::get<0>(pools)) { - func(component); - } - } else if constexpr(std::is_invocable_v) { - for(auto entity: *view) { - func(entity); - } - } else { + } else if constexpr(ignore_as_empty_v) { for(size_type pos{}, last = size(); pos < last; ++pos) { func(); } + } else { + for(auto &&component: storage()) { + func(component); + } } } @@ -33015,50 +39873,58 @@ public: * * @return An iterable object to use to _visit_ the view. */ - [[nodiscard]] iterable each() const ENTT_NOEXCEPT { - return std::get<0>(pools)->each(); + [[nodiscard]] iterable each() const noexcept { + return storage().each(); } /** * @brief Combines two views in a _more specific_ one (friend function). - * @tparam Get Component list of the view to combine with. - * @tparam Excl Filter list of the view to combine with. + * @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 ENTT_NOEXCEPT { - using view_type = basic_view, exclude_t>; - return std::make_from_tuple(std::tuple_cat( - std::forward_as_tuple(*std::get<0>(pools)), - std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), - std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); + 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::array filter; - const base_type *view; + std::tuple pools; + std::tuple<> filter; }; /** * @brief Deduction guide. - * @tparam Storage Type of storage classes used to create the view. + * @tparam Type Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ -template -basic_view(Storage &...storage) -> basic_view, get_t...>, exclude_t<>>; +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 -// #include "locator/locator.hpp" -#ifndef ENTT_LOCATOR_LOCATOR_HPP -#define ENTT_LOCATOR_LOCATOR_HPP +// #include "graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#include +#include #include +#include #include +#include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H @@ -33078,7 +39944,7 @@ basic_view(Storage &...storage) -> basic_view basic_view basic_view -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# 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/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#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 + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -33163,165 +40350,457 @@ basic_view(Storage &...storage) -> basic_view - * Usually service locators are tightly bound to the services they expose and - * thus it's hard to define a general purpose class to do that. This tiny class - * tries to fill the gap and to get rid of the burden of defining a different - * specific locator for each application. - * - * @note - * Users shouldn't retain references to a service. The recommended way is to - * retrieve the service implementation currently set each and every time the - * need for it arises. The risk is to incur in unexpected behaviors otherwise. - * - * @tparam Service Service type. - */ -template -struct locator final { - /*! @brief Service type. */ - using type = Service; +template +class basic_any; - /*! @brief Default constructor, deleted on purpose. */ - locator() = delete; - /*! @brief Default destructor, deleted on purpose. */ - ~locator() = delete; +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; - /** - * @brief Checks whether a service locator contains a value. - * @return True if the service locator contains a value, false otherwise. - */ - [[nodiscard]] static bool has_value() ENTT_NOEXCEPT { - return (service != nullptr); - } - - /** - * @brief Returns a reference to a valid service, if any. - * - * @warning - * Invoking this function can result in undefined behavior if the service - * hasn't been set yet. - * - * @return A reference to the service currently set, if any. - */ - [[nodiscard]] static Service &value() ENTT_NOEXCEPT { - ENTT_ASSERT(has_value(), "Service not available"); - return *service; - } - - /** - * @brief Returns a service if available or sets it from a fallback type. - * - * Arguments are used only if a service doesn't already exist. In all other - * cases, they are discarded. - * - * @tparam Args Types of arguments to use to construct the fallback service. - * @tparam Impl Fallback service type. - * @param args Parameters to use to construct the fallback service. - * @return A reference to a valid service. - */ - template - [[nodiscard]] static Service &value_or(Args &&...args) { - return service ? *service : emplace(std::forward(args)...); - } - - /** - * @brief Sets or replaces a service. - * @tparam Impl 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 - static Service &emplace(Args &&...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 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)...); - return *service; - } - - /*! @brief Resets a service. */ - static void reset() ENTT_NOEXCEPT { - service.reset(); - } - -private: - // std::shared_ptr because of its type erased allocator which is pretty useful here - inline static std::shared_ptr service = nullptr; -}; +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; } // namespace entt #endif -// #include "meta/adl_pointer.hpp" -#ifndef ENTT_META_ADL_POINTER_HPP -#define ENTT_META_ADL_POINTER_HPP namespace entt { /** - * @brief ADL based lookup function for dereferencing meta pointer-like types. - * @tparam Type Element type. - * @param value A pointer-like object. - * @return The value returned from the dereferenced pointer. + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. */ -template -decltype(auto) dereference_meta_pointer_like(const Type &value) { - return *value; + +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; } +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal + /** - * @brief Fake ADL based lookup function for meta pointer-like types. - * @tparam Type Element type. + * Internal details not to be documented. + * @endcond */ -template -struct adl_meta_pointer_like { + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + /** - * @brief Uses the default ADL based lookup method to resolve the call. - * @param value A pointer-like object. - * @return The value returned from the dereferenced pointer. + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. */ - static decltype(auto) dereference(const Type &value) { - return dereference_meta_pointer_like(value); + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[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; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[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; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; }; } // namespace entt #endif -// #include "meta/container.hpp" -#ifndef ENTT_META_CONTAINER_HPP -#define ENTT_META_CONTAINER_HPP +// #include "graph/dot.hpp" +#ifndef ENTT_GRAPH_DOT_HPP +#define ENTT_GRAPH_DOT_HPP -#include -#include -#include +#include #include -#include -#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @tparam Writer Vertex decorator type. + * @param out A standard output stream. + * @param graph The graph to output. + * @param writer Vertex decorator object. + */ +template +void dot(std::ostream &out, const Graph &graph, Writer writer) { + static_assert(std::is_base_of_v, "Invalid graph category"); + + if constexpr(std::is_same_v) { + out << "graph{"; + } else { + out << "digraph{"; + } + + for(auto &&vertex: graph.vertices()) { + out << vertex << "["; + writer(out, vertex); + out << "];"; + } + + for(auto [lhs, rhs]: graph.edges()) { + if constexpr(std::is_same_v) { + out << lhs << "--" << rhs << ";"; + } else { + out << lhs << "->" << rhs << ";"; + } + } + + out << "}"; +} + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @param out A standard output stream. + * @param graph The graph to output. + */ +template +void dot(std::ostream &out, const Graph &graph) { + return dot(out, graph, [](auto &&...) {}); +} + +} // namespace entt + +#endif + +// #include "graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include +#include +#include #include +// #include "../config/config.h" + // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP -#include #include #include #include @@ -33351,7 +40830,7 @@ struct adl_meta_pointer_like { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -33362,14 +40841,12 @@ struct adl_meta_pointer_like { #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -33397,16 +40874,23 @@ struct adl_meta_pointer_like { #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -33441,6 +40925,14 @@ struct adl_meta_pointer_like { #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 @@ -33460,7 +40952,7 @@ struct adl_meta_pointer_like { #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -33471,14 +40963,12 @@ struct adl_meta_pointer_like { #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -33506,16 +40996,23 @@ struct adl_meta_pointer_like { #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -33542,28 +41039,17 @@ struct adl_meta_pointer_like { #endif -// #include "type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - -#include -#include -#include -#include -// #include "../config/config.h" - // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -33690,22 +41176,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -33716,6 +41202,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -33846,6 +41384,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -34045,7 +41606,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -34133,7 +41694,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -34175,7 +41736,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -34215,6 +41776,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -34235,22 +41828,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -34265,22 +41858,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -34323,7 +41916,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -34331,13 +41924,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -34347,7 +41940,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -34359,7 +41952,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -34368,25 +41961,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -34394,12 +41987,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -34407,7 +42000,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -34420,7 +42013,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -34431,7 +42024,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -34457,7 +42050,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -34497,9 +42090,8 @@ struct tuple_element>: conditional #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -34509,46 +42101,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -34564,22 +42234,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -34588,17 +42260,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -34626,69 +42298,12 @@ private: namespace entt { -/** - * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). - * @tparam Type Pointer type. - * @param ptr Fancy or raw pointer. - * @return A raw pointer that represents the address of the original pointer. - */ -template -[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { - if constexpr(std::is_pointer_v>>) { - return ptr; - } else { - return to_address(std::forward(ptr).operator->()); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { - lhs = rhs; - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { - lhs = std::move(rhs); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); - - if constexpr(std::allocator_traits::propagate_on_container_swap::value) { - using std::swap; - swap(lhs, rhs); - } -} - /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ -[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } @@ -34697,8 +42312,8 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ -[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { - ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { @@ -34714,11 +42329,68 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ -[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { - ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. @@ -34734,14 +42406,14 @@ struct allocation_deleter: private Allocator { * @brief Inherited constructors. * @param alloc The allocator to use. */ - allocation_deleter(const allocator_type &alloc) + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ - void operator()(pointer ptr) { + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); @@ -34758,7 +42430,7 @@ struct allocation_deleter: private Allocator { * @return A properly initialized unique pointer with a custom deleter. */ template -auto allocate_unique(Allocator &allocator, Args &&...args) { +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; @@ -34788,14 +42460,14 @@ namespace internal { template struct uses_allocator_construction { template - static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { - return std::tuple(std::allocator_arg, allocator, std::forward(params)...); + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); @@ -34809,7 +42481,7 @@ struct uses_allocator_construction> { using type = std::pair; template - static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), @@ -34817,22 +42489,22 @@ struct uses_allocator_construction> { } template - static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template - static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template - static constexpr auto args(const Allocator &allocator, const std::pair &value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template - static constexpr auto args(const Allocator &allocator, std::pair &&value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; @@ -34858,7 +42530,7 @@ struct uses_allocator_construction> { * @return The arguments needed to create an object of the given type. */ template -constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } @@ -34896,7 +42568,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { - return std::apply([&](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt @@ -35029,22 +42701,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -35055,6 +42727,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -35185,6 +42909,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -35384,7 +43131,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -35472,7 +43219,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -35514,7 +43261,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -35554,6 +43301,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -35639,109 +43418,109 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_iterator() ENTT_NOEXCEPT + constexpr dense_map_iterator() noexcept : it{} {} - dense_map_iterator(const It iter) ENTT_NOEXCEPT + constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} - dense_map_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } - dense_map_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } - dense_map_iterator &operator--() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } - dense_map_iterator operator--(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } - dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } - dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template - friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -35760,37 +43539,37 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_local_iterator() ENTT_NOEXCEPT + constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} - dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> - dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} - dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } - dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } - [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } @@ -35800,12 +43579,12 @@ private: }; template -[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -35841,7 +43620,7 @@ class dense_map { using packed_container_type = std::vector>; template - [[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(sparse.second()(key), bucket_count()); } @@ -35900,8 +43679,8 @@ class dense_map { void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { - packed.first()[pos] = std::move(packed.first().back()); size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } @@ -35941,7 +43720,7 @@ public: /*! @brief Default constructor. */ dense_map() - : dense_map(minimum_capacity) {} + : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. @@ -35953,35 +43732,35 @@ public: /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const allocator_type &allocator) - : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) - : dense_map{bucket_count, hash, key_equal{}, allocator} {} + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ - explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { - rehash(bucket_count); + rehash(cnt); } /*! @brief Default copy constructor. */ @@ -35998,7 +43777,7 @@ public: threshold{other.threshold} {} /*! @brief Default move constructor. */ - dense_map(dense_map &&) = default; + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. @@ -36020,13 +43799,13 @@ public: * @brief Default move assignment operator. * @return This container. */ - dense_map &operator=(dense_map &&) = default; + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } @@ -36038,17 +43817,17 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } @@ -36062,17 +43841,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return packed.first().end(); } @@ -36080,7 +43859,7 @@ public: * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } @@ -36088,12 +43867,20 @@ public: * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + /*! @brief Clears the container. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); @@ -36309,6 +44096,27 @@ public: return insert_or_do_nothing(std::move(key)).first->second; } + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. @@ -36326,7 +44134,7 @@ public: /** * @brief Finds an element with a key that compares _equivalent_ to a given - * value. + * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element @@ -36345,6 +44153,46 @@ public: return constrained_find(key, key_to_bucket(key)); } + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. @@ -36408,7 +44256,7 @@ public: * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ - [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { + [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } @@ -36484,15 +44332,19 @@ public: /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. - * @param count New number of buckets. + * @param cnt New number of buckets. */ - void rehash(const size_type count) { - auto value = (std::max)(count, minimum_capacity); - value = (std::max)(value, static_cast(size() / max_load_factor())); + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); - std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits::max)()); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); @@ -36504,11 +44356,11 @@ public: /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. - * @param count New number of elements. + * @param cnt New number of elements. */ - void reserve(const size_type count) { - packed.first().reserve(count); - rehash(static_cast(std::ceil(count / max_load_factor()))); + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** @@ -36559,7 +44411,6 @@ struct uses_allocator, Allocator> #ifndef ENTT_CONTAINER_DENSE_SET_HPP #define ENTT_CONTAINER_DENSE_SET_HPP -#include #include #include #include @@ -36602,109 +44453,109 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; - dense_set_iterator() ENTT_NOEXCEPT + constexpr dense_set_iterator() noexcept : it{} {} - dense_set_iterator(const It iter) ENTT_NOEXCEPT + constexpr dense_set_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - dense_set_iterator(const dense_set_iterator &other) ENTT_NOEXCEPT + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept : it{other.it} {} - dense_set_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator++() noexcept { return ++it, *this; } - dense_set_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_set_iterator operator++(int) noexcept { dense_set_iterator orig = *this; return ++(*this), orig; } - dense_set_iterator &operator--() ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator--() noexcept { return --it, *this; } - dense_set_iterator operator--(int) ENTT_NOEXCEPT { + constexpr dense_set_iterator operator--(int) noexcept { dense_set_iterator orig = *this; return operator--(), orig; } - dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { dense_set_iterator copy = *this; return (copy += value); } - dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return it[value].second; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it->second); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } template - friend std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; template - friend bool operator==(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; template - friend bool operator<(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -36720,37 +44571,37 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; - dense_set_local_iterator() ENTT_NOEXCEPT + constexpr dense_set_local_iterator() noexcept : it{}, offset{} {} - dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> - dense_set_local_iterator(const dense_set_local_iterator &other) ENTT_NOEXCEPT + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} - dense_set_local_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_set_local_iterator &operator++() noexcept { return offset = it[offset].first, *this; } - dense_set_local_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_set_local_iterator operator++(int) noexcept { dense_set_local_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it[offset].second); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } - [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } @@ -36760,12 +44611,12 @@ private: }; template -[[nodiscard]] bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -36800,7 +44651,7 @@ class dense_set { using packed_container_type = std::vector>; template - [[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT { + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { return fast_mod(sparse.second()(value), bucket_count()); } @@ -36843,8 +44694,8 @@ class dense_set { void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { - packed.first()[pos] = std::move(packed.first().back()); size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].first) {} *curr = pos; } @@ -36882,7 +44733,7 @@ public: /*! @brief Default constructor. */ dense_set() - : dense_set(minimum_capacity) {} + : dense_set{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. @@ -36894,35 +44745,35 @@ public: /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ - dense_set(const size_type bucket_count, const allocator_type &allocator) - : dense_set{bucket_count, hasher{}, key_equal{}, allocator} {} + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ - dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) - : dense_set{bucket_count, hash, key_equal{}, allocator} {} + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ - explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { - rehash(bucket_count); + rehash(cnt); } /*! @brief Default copy constructor. */ @@ -36939,7 +44790,7 @@ public: threshold{other.threshold} {} /*! @brief Default move constructor. */ - dense_set(dense_set &&) = default; + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. @@ -36961,13 +44812,13 @@ public: * @brief Default move assignment operator. * @return This container. */ - dense_set &operator=(dense_set &&) = default; + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } @@ -36979,17 +44830,17 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } @@ -37003,17 +44854,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return packed.first().end(); } @@ -37021,7 +44872,7 @@ public: * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } @@ -37029,12 +44880,20 @@ public: * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + /*! @brief Clears the container. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); @@ -37084,7 +44943,7 @@ public: */ template std::pair emplace(Args &&...args) { - if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v>, value_type>)) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); @@ -37158,6 +45017,27 @@ public: swap(threshold, other.threshold); } + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + /** * @brief Finds an element with a given value. * @param value Value of an element to search for. @@ -37193,6 +45073,46 @@ public: return constrained_find(value, value_to_bucket(value)); } + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + /** * @brief Checks if the container contains an element with a given value. * @param value Value of an element to search for. @@ -37256,7 +45176,7 @@ public: * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ - [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { + [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } @@ -37332,15 +45252,19 @@ public: /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. - * @param count New number of buckets. + * @param cnt New number of buckets. */ - void rehash(const size_type count) { - auto value = (std::max)(count, minimum_capacity); - value = (std::max)(value, static_cast(size() / max_load_factor())); + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); - std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits::max)()); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = value_to_bucket(packed.first()[pos].second); @@ -37352,11 +45276,11 @@ public: /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. - * @param count New number of elements. + * @param cnt New number of elements. */ - void reserve(const size_type count) { - packed.first().reserve(count); - rehash(static_cast(std::ceil(count / max_load_factor()))); + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** @@ -37385,6 +45309,6942 @@ private: #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" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @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; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +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 {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @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 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 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 Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @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> {}; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 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 Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @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 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 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 Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @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>>; +}; + +/*! @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 Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @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; + +/** + * @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...> {}; + +/** + * @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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @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 Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 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 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 value. */ + static constexpr auto value = Value; +}; + +/** + * @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 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 {}; +} + +/*! @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<>; +}; + +/** + * @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; +}; + +/** + * @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; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @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 {}; + +/** + * @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 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; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_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 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 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 {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>, void>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @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 Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @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 {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (is_equality_comparable>::value && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { + return true; +} + +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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::bool_constant(choice<2>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @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; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @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 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"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @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 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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal + +/** + * 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; + +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; + + /** + * @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{} {} + + /** + * @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; + + /** + * @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; + + /** + * @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)} {} + + /** + * @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{}} {} + + /** + * @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; + + /** + * @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; + + /** + * @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(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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()); + } + + /** + * @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(); + } + } + + /*! @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(); + } + } +}; + +/** + * @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>; + +/** + * @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); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @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 {}; + +/** + * @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"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#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; + + /** + * @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); + } +}; + +/** + * @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; +} + +/** + * @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; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +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 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)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[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; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[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; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + 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>>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{}, + deps{}, + sync_on{} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[pos]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = sync_on = {}; + vertices.clear(); + deps.clear(); + } + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @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(); + + 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); + } + } + } + } + } + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on; +}; + +} // namespace entt + +#endif + +// #include "locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#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 + +#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 + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Impl Fallback service type. + * @param args Parameters to use to construct the fallback service. + * @return A reference to a valid service. + */ + template + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @tparam Impl 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 + static Service &emplace(Args &&...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 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)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + inline static std::shared_ptr service{}; +}; + +} // namespace entt + +#endif + +// #include "meta/adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template +decltype(auto) dereference_meta_pointer_like(const Type &value) { + return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + +} // namespace entt + +#endif + +// #include "meta/container.hpp" +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#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 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 + +#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 + +#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 "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @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; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +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 {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @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 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 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 Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @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> {}; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 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 Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @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 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 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 Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @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>>; +}; + +/*! @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 Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @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; + +/** + * @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...> {}; + +/** + * @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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @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 Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 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 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 value. */ + static constexpr auto value = Value; +}; + +/** + * @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 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 {}; +} + +/*! @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<>; +}; + +/** + * @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; +}; + +/** + * @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; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @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 {}; + +/** + * @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 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; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_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 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 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 {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>, void>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @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 Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @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 {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (is_equality_comparable>::value && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { + return true; +} + +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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::bool_constant(choice<2>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @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; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @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 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"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @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 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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal + +/** + * 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; + +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; + + /** + * @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{} {} + + /** + * @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; + + /** + * @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; + + /** + * @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)} {} + + /** + * @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{}} {} + + /** + * @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; + + /** + * @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; + + /** + * @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(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @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()); + } + + /** + * @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(); + } + } + + /*! @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(); + } + } +}; + +/** + * @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>; + +/** + * @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); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @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 {}; + +/** + * @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"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not. + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value. + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Args Types of arguments to use to construct the object. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @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; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @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 */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @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; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +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 {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @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 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 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 Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @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> {}; + +/** + * @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 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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 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 Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @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 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 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 Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @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>>; +}; + +/*! @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 Helper type. + * @tparam Type A type list. + */ +template +using type_list_unique_t = typename type_list_unique::type; + +/** + * @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; + +/** + * @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...> {}; + +/** + * @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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @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 Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 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 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 value. */ + static constexpr auto value = Value; +}; + +/** + * @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 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 {}; +} + +/*! @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<>; +}; + +/** + * @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; +}; + +/** + * @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; + +/*! @brief Same as std::is_invocable, but with tuples. */ +template +struct is_applicable: std::false_type {}; + +/** + * @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 {}; + +/** + * @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 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; + +/*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: std::false_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 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 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 {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>, void>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @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 Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @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 {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/** + * @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 {}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (is_equality_comparable>::value && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { + return true; +} + +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 + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>> + : std::bool_constant(choice<2>)> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @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; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @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 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"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @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 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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to, + typename = std::allocator> +class dense_set; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + 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 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 { + return lhs.it - rhs.it; +} + +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 { + return !(lhs == rhs); +} + +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 { + return rhs < lhs; +} + +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 { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +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 { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = internal::dense_map_node; + using alloc_traits = typename 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()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @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. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @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 packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std + +/** + * Internal details not to be documented. + * @endcond + */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + 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; + +private: + It it; +}; + +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 { + return lhs.it == rhs.it; +} + +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 { + return lhs.it < rhs.it; +} + +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 { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +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 { + return !(lhs == rhs); +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + 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 value_to_bucket(const Other &value) const noexcept { + return fast_mod(sparse.second()(value), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @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. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @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 packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "context.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#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 + +#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 + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#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; + + /** + * @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); + } +}; + +/** + * @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; +} + +/** + * @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; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +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 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)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +class meta_ctx; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +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); +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + /*! @brief Attorney idiom like model to access the base class. */ + friend struct internal::meta_context; +}; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +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) { + return ctx; +} + +/** + * Internal details not to be documented. + * @endcond + */ + +} // namespace entt + +#endif + // #include "meta.hpp" #ifndef ENTT_META_META_HPP #define ENTT_META_META_HPP @@ -37413,7 +52273,7 @@ private: #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -37424,14 +52284,12 @@ private: #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -37459,16 +52317,23 @@ private: #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -37504,212 +52369,13 @@ private: #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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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 - namespace entt { @@ -37725,7 +52391,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -37738,7 +52404,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -37749,7 +52415,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -37779,7 +52445,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -37789,13 +52455,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -37811,14 +52477,13 @@ private: #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -37880,8 +52545,6 @@ using any = basic_any<>; #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -37951,12 +52614,14 @@ class basic_hashed_string: internal::basic_hashed_string { struct const_wrapper { // non-explicit constructor on purpose - constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} + 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { @@ -37967,7 +52632,7 @@ 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { @@ -37991,7 +52656,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } @@ -38002,7 +52667,7 @@ public: * @return The numeric representation of the string. */ template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } @@ -38011,12 +52676,12 @@ public: * @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) ENTT_NOEXCEPT { + [[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() ENTT_NOEXCEPT + constexpr basic_hashed_string() noexcept : base_type{} {} /** @@ -38024,7 +52689,7 @@ public: * @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) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** @@ -38033,7 +52698,7 @@ public: * @param str Human-readable identifier. */ template - constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** @@ -38045,14 +52710,14 @@ public: * * @param wrapper Helps achieving the purpose by relying on overloading. */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + 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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } @@ -38060,7 +52725,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } @@ -38068,12 +52733,12 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } @@ -38081,7 +52746,7 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; @@ -38112,7 +52777,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; * @return True if the two hashed strings are identical, false otherwise. */ template -[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } @@ -38124,7 +52789,7 @@ template * @return True if the two hashed strings differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } @@ -38136,7 +52801,7 @@ template * @return True if the first element is less than the second, false otherwise. */ template -[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } @@ -38149,7 +52814,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } @@ -38162,7 +52827,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } @@ -38175,7 +52840,7 @@ template * false otherwise. */ template -[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } @@ -38192,7 +52857,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } @@ -38201,7 +52866,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } @@ -38222,14 +52887,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -38241,26 +52906,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -38284,13 +52949,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -38306,16 +52971,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -38330,12 +52995,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -38347,7 +53012,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -38356,7 +53021,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -38364,7 +53029,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -38372,7 +53037,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -38388,7 +53053,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -38398,7 +53063,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -38408,7 +53073,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -38419,7 +53084,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -38430,7 +53095,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -38441,7 +53106,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -38457,7 +53122,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -38468,7 +53133,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -38602,22 +53267,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -38628,6 +53293,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -38758,6 +53475,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -38957,7 +53697,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -39045,7 +53785,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -39087,7 +53827,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -39127,6 +53867,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -39134,6 +53906,36 @@ using member_class_t = typename member_class::type; namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +enum class any_policy : std::uint8_t { + owner, + ref, + cref +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. @@ -39141,30 +53943,19 @@ namespace entt { */ template class basic_any { - enum class operation : std::uint8_t { - copy, - move, - transfer, - assign, - destroy, - compare, - get - }; - - enum class policy : std::uint8_t { - owner, - ref, - cref - }; - - using storage_type = std::aligned_storage_t; + using operation = internal::any_operation; + using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); - template - static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; template - static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { + 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) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; @@ -39211,7 +54002,7 @@ class basic_any { break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { - return *static_cast(element) == *static_cast(other) ? other : nullptr; + return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } @@ -39224,31 +54015,32 @@ class basic_any { template void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + if constexpr(!std::is_void_v) { - info = &type_id>>(); vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "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) { - new(&storage) Type{std::forward(args)...}; + } else if constexpr(in_situ>>) { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { - new(&storage) Type(std::forward(args)...); + new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { - instance = new Type{std::forward(args)...}; + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + instance = new std::remove_cv_t>{std::forward(args)...}; } else { - instance = new Type(std::forward(args)...); + instance = new std::remove_cv_t>(std::forward(args)...); } } } } - basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, @@ -39261,11 +54053,8 @@ public: static constexpr auto alignment = Align; /*! @brief Default constructor. */ - constexpr basic_any() ENTT_NOEXCEPT - : instance{}, - info{&type_id()}, - vtable{}, - mode{policy::owner} {} + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. @@ -39275,7 +54064,10 @@ public: */ template explicit basic_any(std::in_place_type_t, Args &&...args) - : basic_any{} { + : instance{}, + info{}, + vtable{}, + mode{policy::owner} { initialize(std::forward(args)...); } @@ -39286,9 +54078,7 @@ public: */ template, basic_any>>> basic_any(Type &&value) - : basic_any{} { - initialize>(std::forward(value)); - } + : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. @@ -39305,7 +54095,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_any(basic_any &&other) ENTT_NOEXCEPT + basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, @@ -39342,7 +54132,7 @@ public: * @param other The instance to move from. * @return This any object. */ - basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { + basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { @@ -39372,7 +54162,7 @@ public: * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ - [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { + [[nodiscard]] const type_info &type() const noexcept { return *info; } @@ -39380,7 +54170,7 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } @@ -39389,7 +54179,7 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { + [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } @@ -39397,8 +54187,8 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data() ENTT_NOEXCEPT { - return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + [[nodiscard]] void *data() noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** @@ -39406,8 +54196,8 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { - return *info == req ? data() : nullptr; + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** @@ -39427,7 +54217,7 @@ public: * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ - bool assign(const any &other) { + bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } @@ -39436,7 +54226,7 @@ public: } /*! @copydoc assign */ - bool assign(any &&other) { + bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); @@ -39454,6 +54244,8 @@ public: vtable(operation::destroy, *this, nullptr); } + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; @@ -39463,7 +54255,7 @@ public: * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } @@ -39472,7 +54264,7 @@ public: * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ - bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } @@ -39480,16 +54272,25 @@ public: return (!vtable && !other.vtable); } + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ - [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ - [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } @@ -39497,7 +54298,7 @@ public: * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ - [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } @@ -39511,19 +54312,6 @@ private: policy mode; }; -/** - * @brief Checks if two wrappers differ in their content. - * @tparam Len Size of the storage reserved for the small buffer optimization. - * @tparam Align Alignment requirement. - * @param lhs A wrapper, either empty or not. - * @param rhs A wrapper, either empty or not. - * @return True if the two wrappers differ in their content, false otherwise. - */ -template -[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. @@ -39533,7 +54321,7 @@ template * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) ENTT_NOEXCEPT { +Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -39541,7 +54329,7 @@ Type any_cast(const basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) ENTT_NOEXCEPT { +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"); @@ -39550,7 +54338,7 @@ Type any_cast(basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) ENTT_NOEXCEPT { +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)); @@ -39566,17 +54354,21 @@ Type any_cast(basic_any &&data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) ENTT_NOEXCEPT { - const auto &info = type_id>>(); +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) ENTT_NOEXCEPT { - const auto &info = type_id>>(); - // last attempt to make wrappers for const references return their values - return static_cast(static_cast, Type> *>(data)->data(info)); +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)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } } /** @@ -39603,7 +54395,7 @@ basic_any make_any(Args &&...args) { */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { - return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; + return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt @@ -39611,28 +54403,6 @@ basic_any forward_as_any(Type &&value) { #endif // #include "../core/fwd.hpp" -#ifndef ENTT_CORE_FWD_HPP -#define ENTT_CORE_FWD_HPP - -#include -#include -// #include "../config/config.h" - - -namespace entt { - -template)> -class basic_any; - -/*! @brief Alias declaration for type identifiers. */ -using id_type = ENTT_ID_TYPE; - -/*! @brief Alias declaration for the most common use case. */ -using any = basic_any<>; - -} // namespace entt - -#endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP @@ -39640,9 +54410,8 @@ using any = basic_any<>; #include #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -39652,46 +54421,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -39707,22 +54554,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -39731,17 +54580,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -39780,14 +54629,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -39799,26 +54648,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -39842,13 +54691,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -39864,16 +54713,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -39888,12 +54737,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -39905,7 +54754,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -39914,7 +54763,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -39922,7 +54771,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -39930,7 +54779,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -39946,7 +54795,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -39956,7 +54805,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -39966,7 +54815,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -39977,7 +54826,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -39988,7 +54837,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -39999,7 +54848,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -40015,7 +54864,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -40026,7 +54875,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -40160,22 +55009,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -40186,6 +55035,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -40316,6 +55217,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -40515,7 +55439,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -40603,7 +55527,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -40645,7 +55569,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -40685,108 +55609,280 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif // #include "../core/utility.hpp" -#ifndef ENTT_CORE_UTILITY_HPP -#define ENTT_CORE_UTILITY_HPP +// #include "../locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#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 + +#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 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; +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; /** - * @brief Returns its argument unchanged. - * @tparam Type Type of the argument. - * @param value The actual argument. - * @return The submitted value as-is. + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. */ - template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { - return std::forward(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) ENTT_NOEXCEPT { - return member; -} - -/** - * @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) ENTT_NOEXCEPT { - return func; -} - -/** - * @brief Helper type for visitors. - * @tparam Func Types of function objects. - */ -template -struct overloaded: Func... { - using Func::operator()...; -}; - -/** - * @brief Deduction guide. - * @tparam Func Types of function objects. - */ -template -overloaded(Func...) -> overloaded; - -/** - * @brief Basic implementation of a y-combinator. - * @tparam Func Type of a potentially recursive function. - */ -template -struct y_combinator { - /** - * @brief Constructs a y-combinator from a given function. - * @param recursive A potentially recursive function. - */ - y_combinator(Func recursive) - : func{std::move(recursive)} {} - - /** - * @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 - decltype(auto) operator()(Args &&...args) const { - return func(*this, std::forward(args)...); + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); } - /*! @copydoc operator()() */ - template - decltype(auto) operator()(Args &&...args) { - return func(*this, std::forward(args)...); + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Impl Fallback service type. + * @param args Parameters to use to construct the fallback service. + * @return A reference to a valid service. + */ + template + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @tparam Impl 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 + static Service &emplace(Args &&...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 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)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; } private: - Func func; + // std::shared_ptr because of its type erased allocator which is useful here + inline static std::shared_ptr service{}; }; } // namespace entt @@ -40830,12 +55926,46 @@ struct adl_meta_pointer_like { #endif -// #include "ctx.hpp" -#ifndef ENTT_META_CTX_HPP -#define ENTT_META_CTX_HPP +// #include "context.hpp" +// #include "fwd.hpp" +#ifndef ENTT_META_FWD_HPP +#define ENTT_META_FWD_HPP + +namespace entt { + +class meta_sequence_container; + +class meta_associative_container; + +class meta_any; + +struct meta_handle; + +struct meta_prop; + +struct meta_data; + +struct meta_func; + +class meta_type; + +} // namespace entt + +#endif + +// #include "node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +#include // #include "../config/config.h" +// #include "../container/dense_map.hpp" + // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H @@ -40868,103 +55998,11 @@ struct adl_meta_pointer_like { #endif - -namespace entt { - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - -namespace internal { - -struct meta_type_node; - -struct ENTT_API meta_context { - // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ - // inline static meta_type_node *local = nullptr; - // inline static meta_type_node **global = &local; - - [[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT { - static meta_type_node *chain = nullptr; - return chain; - } - - [[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT { - static meta_type_node **chain = &local(); - return chain; - } -}; - -} // namespace internal - -/** - * Internal details not to be documented. - * @endcond - */ - -/*! @brief Opaque container for a meta context. */ -struct meta_ctx { - /** - * @brief Binds the meta system to a given context. - * @param other A valid context to which to bind. - */ - static void bind(meta_ctx other) ENTT_NOEXCEPT { - internal::meta_context::global() = other.ctx; - } - -private: - internal::meta_type_node **ctx{&internal::meta_context::local()}; -}; - -} // namespace entt - -#endif - -// #include "fwd.hpp" -#ifndef ENTT_META_FWD_HPP -#define ENTT_META_FWD_HPP - -namespace entt { - -class meta_sequence_container; - -class meta_associative_container; - -class meta_any; - -struct meta_handle; - -struct meta_prop; - -struct meta_data; - -struct meta_func; - -class meta_type; - -} // namespace entt - -#endif - -// #include "node.hpp" -#ifndef ENTT_META_NODE_HPP -#define ENTT_META_NODE_HPP - -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/attribute.h" - // #include "../core/enum.hpp" #ifndef ENTT_CORE_ENUM_HPP #define ENTT_CORE_ENUM_HPP #include -// #include "../config/config.h" - namespace entt { @@ -40998,21 +56036,21 @@ inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator|(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +operator|(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) | static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator&(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +operator&(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) & static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT { +operator^(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } @@ -41025,35 +56063,35 @@ operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT { */ template [[nodiscard]] constexpr std::enable_if_t, Type> -operator~(const Type value) ENTT_NOEXCEPT { +operator~(const Type value) noexcept { return static_cast(~static_cast>(value)); } /*! @copydoc operator~ */ template [[nodiscard]] constexpr std::enable_if_t, bool> -operator!(const Type value) ENTT_NOEXCEPT { +operator!(const Type value) noexcept { return !static_cast>(value); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> -operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +operator|=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs | rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> -operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +operator&=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs & rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> -operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { +operator^=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs ^ rhs)); } @@ -41065,6 +56103,10 @@ operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { // #include "../core/type_traits.hpp" +// #include "../core/utility.hpp" + +// #include "context.hpp" + // #include "type_traits.hpp" #ifndef ENTT_META_TYPE_TRAITS_HPP #define ENTT_META_TYPE_TRAITS_HPP @@ -41141,199 +56183,196 @@ enum class meta_traits : std::uint32_t { is_const = 0x0001, is_static = 0x0002, is_arithmetic = 0x0004, - is_array = 0x0008, - is_enum = 0x0010, - is_class = 0x0020, - is_pointer = 0x0040, - is_meta_pointer_like = 0x0080, - is_meta_sequence_container = 0x0100, - is_meta_associative_container = 0x0200, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_meta_pointer_like = 0x0100, + is_meta_sequence_container = 0x0200, + is_meta_associative_container = 0x0400, _entt_enum_as_bitmask }; struct meta_type_node; struct meta_prop_node { - meta_prop_node *next; - const meta_any &id; - meta_any &value; + meta_type_node (*type)(const meta_context &) noexcept {}; + std::shared_ptr value{}; }; struct meta_base_node { - meta_base_node *next; - meta_type_node *const type; - meta_any (*const cast)(meta_any) ENTT_NOEXCEPT; + meta_type_node (*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; }; struct meta_conv_node { - meta_conv_node *next; - meta_type_node *const type; - meta_any (*const conv)(const meta_any &); + meta_any (*conv)(const meta_ctx &, const void *){}; }; struct meta_ctor_node { using size_type = std::size_t; - meta_ctor_node *next; - const size_type arity; - meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; - meta_any (*const invoke)(meta_any *const); + + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_dtor_node { + void (*dtor)(void *){}; }; struct meta_data_node { using size_type = std::size_t; - id_type id; - const meta_traits traits; - meta_data_node *next; - meta_prop_node *prop; - const size_type arity; - meta_type_node *const type; - meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; - bool (*const set)(meta_handle, meta_any); - meta_any (*const get)(meta_handle); + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(const meta_ctx &, meta_handle){}; + dense_map prop{}; }; struct meta_func_node { using size_type = std::size_t; - id_type id; - const meta_traits traits; - meta_func_node *next; - meta_prop_node *prop; - const size_type arity; - meta_type_node *const ret; - meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; - meta_any (*const invoke)(meta_handle, meta_any *const); + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; + std::shared_ptr next{}; + dense_map prop{}; }; struct meta_template_node { using size_type = std::size_t; - const size_type arity; - meta_type_node *const type; - meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT; + + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + dense_map ctor{}; + dense_map base{}; + dense_map conv{}; + dense_map data{}; + dense_map func{}; + dense_map prop{}; }; struct meta_type_node { using size_type = std::size_t; - const type_info *info; - id_type id; - const meta_traits traits; - meta_type_node *next; - meta_prop_node *prop; - const size_type size_of; - meta_any (*const default_constructor)(); - double (*const conversion_helper)(void *, const void *); - const meta_template_node *const templ; - meta_ctor_node *ctor{nullptr}; - meta_base_node *base{nullptr}; - meta_conv_node *conv{nullptr}; - meta_data_node *data{nullptr}; - meta_func_node *func{nullptr}; - void (*dtor)(void *){nullptr}; -}; -template -meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; + const type_info *info{}; + id_type id{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + meta_type_node (*resolve)(const meta_context &) noexcept {}; + meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_dtor_node dtor{}; + std::shared_ptr details{}; +}; template -class ENTT_API meta_node { - static_assert(std::is_same_v>>, "Invalid type"); - - [[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT { - if constexpr(std::is_default_constructible_v) { - return +[]() { return meta_any{std::in_place_type}; }; - } else { - return static_cast>(nullptr); - } - } - - [[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT { - if constexpr(std::is_arithmetic_v) { - return +[](void *bin, const void *value) { - return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); - }; - } else if constexpr(std::is_enum_v) { - return +[](void *bin, const void *value) { - return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); - }; - } else { - return static_cast>(nullptr); - } - } - - [[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT { - if constexpr(is_complete_v>) { - static meta_template_node node{ - meta_template_traits::args_type::size, - meta_node::class_type>::resolve(), - [](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits::args_type{}, index); } - // tricks clang-format - }; - - return &node; - } else { - return nullptr; - } - } - -public: - [[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT { - static meta_type_node node{ - &type_id(), - {}, - internal::meta_traits::is_none - | (std::is_arithmetic_v ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none) - | (std::is_array_v ? internal::meta_traits::is_array : internal::meta_traits::is_none) - | (std::is_enum_v ? internal::meta_traits::is_enum : internal::meta_traits::is_none) - | (std::is_class_v ? internal::meta_traits::is_class : internal::meta_traits::is_none) - | (std::is_pointer_v ? internal::meta_traits::is_pointer : internal::meta_traits::is_none) - | (is_meta_pointer_like_v ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none) - | (is_complete_v> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none) - | (is_complete_v> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none), - nullptr, - nullptr, - size_of_v, - meta_default_constructor(), - meta_conversion_helper(), - meta_template_info() - // tricks clang-format - }; - - return &node; - } -}; +meta_type_node resolve(const meta_context &) noexcept; template -[[nodiscard]] meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { - meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node>>::resolve()...}; - return args[index + 1u]; +[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { + 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"); + return value(context); } -template -[[nodiscard]] static std::decay_t().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT { - for(auto *curr = node->*Member; curr; curr = curr->next) { - if constexpr(std::is_same_v) { - if(*curr->type->info == info_or_id) { - return curr; - } - } else if constexpr(std::is_same_v) { - if(curr->type->id == info_or_id) { - return curr; - } - } else { - if(curr->id == info_or_id) { - return curr; - } - } +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { + if(from.info && to.info && *from.info == *to.info) { + return instance; } - for(auto *curr = node->base; curr; curr = curr->next) { - if(auto *ret = find_by(info_or_id, curr->type); ret) { - return ret; + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { + return elem; + } } } return nullptr; } +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return it != context.value.end() ? &it->second : nullptr; +} + +template +[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + + if(auto *elem = try_resolve(context, type_id()); elem) { + return *elem; + } + + meta_type_node node{ + &type_id(), + type_id().hash(), + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), + size_of_v, + &resolve, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } + + if constexpr(!std::is_same_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { + if(element) { + return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; + } + + return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + } // namespace internal /** @@ -41351,8 +56390,13 @@ template #include #include +#include +// #include "../core/fwd.hpp" + // #include "../core/iterator.hpp" +// #include "context.hpp" + namespace entt { @@ -41363,50 +56407,119 @@ namespace entt { namespace internal { -template +template struct meta_range_iterator final { using difference_type = std::ptrdiff_t; - using value_type = Type; + using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; - using node_type = Node; - meta_range_iterator() ENTT_NOEXCEPT - : it{} {} + meta_range_iterator() noexcept + : it{}, + ctx{} {} - meta_range_iterator(node_type *head) ENTT_NOEXCEPT - : it{head} {} + meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} - meta_range_iterator &operator++() ENTT_NOEXCEPT { - return (it = it->next), *this; + meta_range_iterator &operator++() noexcept { + return ++it, *this; } - meta_range_iterator operator++(int) ENTT_NOEXCEPT { + meta_range_iterator operator++(int) noexcept { meta_range_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return it; + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + constexpr meta_range_iterator operator--(int) noexcept { + meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, Type{*ctx, it[value].second}}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT { - return it == other.it; + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, Type{*ctx, it->second}}; } - [[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; private: - node_type *it; + It it; + const meta_ctx *ctx; }; +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + } // namespace internal /** @@ -41417,57 +56530,10 @@ private: /** * @brief Iterable range to use to iterate all types of meta objects. * @tparam Type Type of meta objects returned. - * @tparam Node Type of meta nodes iterated. + * @tparam It Type of forward iterator. */ -template -struct meta_range final { - /*! @brief Node type. */ - using node_type = Node; - /*! @brief Input iterator type. */ - using iterator = internal::meta_range_iterator; - /*! @brief Constant input iterator type. */ - using const_iterator = iterator; - - /*! @brief Default constructor. */ - meta_range() ENTT_NOEXCEPT = default; - - /** - * @brief Constructs a meta range from a given node. - * @param head The underlying node with which to construct the range. - */ - meta_range(node_type *head) ENTT_NOEXCEPT - : node{head} {} - - /** - * @brief Returns an iterator to the beginning. - * @return An iterator to the first meta object of the range. - */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { - return iterator{node}; - } - - /*! @copydoc cbegin */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return cbegin(); - } - - /** - * @brief Returns an iterator to the end. - * @return An iterator to the element following the last meta object of the - * range. - */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { - return iterator{}; - } - - /*! @copydoc cend */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return cend(); - } - -private: - node_type *node{nullptr}; -}; +template +using meta_range = iterable_adaptor>; } // namespace entt @@ -41491,26 +56557,30 @@ public: /*! @brief Meta iterator type. */ using iterator = meta_iterator; - /*! @brief Default constructor. */ - meta_sequence_container() ENTT_NOEXCEPT = default; + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_sequence_container(const meta_ctx &area = locator::value_or()) noexcept + : ctx{&area} {} /** - * @brief Construct a proxy object for sequence containers. + * @brief Rebinds a proxy object to a sequence container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template - meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT - : value_type_node{internal::meta_node>>::resolve()}, - size_fn{&meta_sequence_container_traits::size}, - resize_fn{&meta_sequence_container_traits::resize}, - iter_fn{&meta_sequence_container_traits::iter}, - insert_fn{&meta_sequence_container_traits::insert}, - erase_fn{&meta_sequence_container_traits::erase}, - storage{std::move(instance)} {} + void rebind(any instance) noexcept { + value_type_node = &internal::resolve; + size_fn = &meta_sequence_container_traits::size; + resize_fn = &meta_sequence_container_traits::resize; + iter_fn = &meta_sequence_container_traits::iter; + insert_or_erase_fn = &meta_sequence_container_traits::insert_or_erase; + storage = std::move(instance); + } - [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; inline bool resize(const size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); @@ -41518,15 +56588,15 @@ public: inline iterator insert(iterator, meta_any); inline iterator erase(iterator); [[nodiscard]] inline meta_any operator[](const size_type); - [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + [[nodiscard]] inline explicit operator bool() const noexcept; private: - internal::meta_type_node *value_type_node = nullptr; - size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; - bool (*resize_fn)(any &, size_type) = nullptr; - iterator (*iter_fn)(any &, const bool) = nullptr; - iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr; - iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr; + const meta_ctx *ctx{}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const any &) noexcept {}; + bool (*resize_fn)(any &, size_type){}; + iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; + iterator (*insert_or_erase_fn)(const meta_ctx &, any &, const any &, meta_any &){}; any storage{}; }; @@ -41540,56 +56610,60 @@ public: /*! @brief Meta iterator type. */ using iterator = meta_iterator; - /*! @brief Default constructor. */ - meta_associative_container() ENTT_NOEXCEPT = default; + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_associative_container(const meta_ctx &area = locator::value_or()) noexcept + : ctx{&area} {} /** - * @brief Construct a proxy object for associative containers. + * @brief Rebinds a proxy object to an associative container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template - meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT - : key_only_container{meta_associative_container_traits::key_only}, - key_type_node{internal::meta_node>>::resolve()}, - mapped_type_node{nullptr}, - value_type_node{internal::meta_node>>::resolve()}, - size_fn{&meta_associative_container_traits::size}, - clear_fn{&meta_associative_container_traits::clear}, - iter_fn{&meta_associative_container_traits::iter}, - insert_fn{&meta_associative_container_traits::insert}, - erase_fn{&meta_associative_container_traits::erase}, - find_fn{&meta_associative_container_traits::find}, - storage{std::move(instance)} { + void rebind(any instance) noexcept { if constexpr(!meta_associative_container_traits::key_only) { - mapped_type_node = internal::meta_node>>::resolve(); + mapped_type_node = &internal::resolve; } + + key_only_container = meta_associative_container_traits::key_only; + key_type_node = &internal::resolve; + value_type_node = &internal::resolve; + size_fn = &meta_associative_container_traits::size; + clear_fn = &meta_associative_container_traits::clear; + iter_fn = &meta_associative_container_traits::iter; + insert_or_erase_fn = &meta_associative_container_traits::insert_or_erase; + find_fn = &meta_associative_container_traits::find; + storage = std::move(instance); } - [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; - [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + [[nodiscard]] inline bool key_only() const noexcept; + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); + inline bool insert(meta_any); inline bool insert(meta_any, meta_any); - inline bool erase(meta_any); + inline size_type erase(meta_any); [[nodiscard]] inline iterator find(meta_any); - [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + [[nodiscard]] inline explicit operator bool() const noexcept; private: + const meta_ctx *ctx{}; bool key_only_container{}; - internal::meta_type_node *key_type_node = nullptr; - internal::meta_type_node *mapped_type_node = nullptr; - internal::meta_type_node *value_type_node = nullptr; - size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; - bool (*clear_fn)(any &) = nullptr; - iterator (*iter_fn)(any &, const bool) = nullptr; - bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr; - bool (*erase_fn)(any &, meta_any &) = nullptr; - iterator (*find_fn)(any &, meta_any &) = nullptr; + internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const any &) noexcept {}; + bool (*clear_fn)(any &){}; + iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; + size_type (*insert_or_erase_fn)(any &, meta_any &, meta_any &){}; + iterator (*find_fn)(const meta_ctx &, any &, meta_any &){}; any storage{}; }; @@ -41611,8 +56685,8 @@ class meta_any { switch(op) { case operation::deref: if constexpr(is_meta_pointer_like_v) { - if constexpr(std::is_function_v::element_type>>) { - *static_cast(other) = any_cast(value); + if constexpr(std::is_function_v::element_type>) { + static_cast(other)->emplace(any_cast(value)); } else if constexpr(!std::is_same_v::element_type>, void>) { using in_place_type = decltype(adl_meta_pointer_like::dereference(any_cast(value))); @@ -41628,12 +56702,12 @@ class meta_any { break; case operation::seq: if constexpr(is_complete_v>) { - *static_cast(other) = {std::in_place_type, std::move(const_cast(value))}; + static_cast(other)->rebind(std::move(const_cast(value))); } break; case operation::assoc: if constexpr(is_complete_v>) { - *static_cast(other) = {std::in_place_type, std::move(const_cast(value))}; + static_cast(other)->rebind(std::move(const_cast(value))); } break; } @@ -41641,20 +56715,29 @@ class meta_any { } void release() { - if(node && node->dtor && storage.owner()) { - node->dtor(storage.data()); + if(node.dtor.dtor && owner()) { + node.dtor.dtor(storage.data()); } } - meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT + meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept : storage{std::move(ref)}, - node{storage ? other.node : nullptr}, + ctx{&area}, + node{storage ? other.node : internal::meta_type_node{}}, vtable{storage ? other.vtable : &basic_vtable} {} public: - /*! @brief Default constructor. */ - meta_any() ENTT_NOEXCEPT + /*! Default constructor. */ + meta_any() noexcept + : meta_any{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept : storage{}, + ctx{&area}, node{}, vtable{&basic_vtable} {} @@ -41666,8 +56749,20 @@ public: */ template explicit meta_any(std::in_place_type_t, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) : storage{std::in_place_type, std::forward(args)...}, - node{internal::meta_node>>::resolve()}, + ctx{&area}, + node{internal::resolve>>(internal::meta_context::from(*ctx))}, vtable{&basic_vtable>>} {} /** @@ -41677,7 +56772,39 @@ public: */ template, meta_any>>> meta_any(Type &&value) - : meta_any{std::in_place_type>>, std::forward(value)} {} + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : meta_any{other} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : meta_any{std::move(other)} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } /** * @brief Copy constructor. @@ -41689,9 +56816,10 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - meta_any(meta_any &&other) ENTT_NOEXCEPT + meta_any(meta_any &&other) noexcept : storage{std::move(other.storage)}, - node{std::exchange(other.node, nullptr)}, + ctx{other.ctx}, + node{std::exchange(other.node, internal::meta_type_node{})}, vtable{std::exchange(other.vtable, &basic_vtable)} {} /*! @brief Frees the internal storage, whatever it means. */ @@ -41706,9 +56834,10 @@ public: */ meta_any &operator=(const meta_any &other) { release(); - vtable = other.vtable; storage = other.storage; + ctx = other.ctx; node = other.node; + vtable = other.vtable; return *this; } @@ -41717,11 +56846,12 @@ public: * @param other The instance to move from. * @return This meta any object. */ - meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT { + meta_any &operator=(meta_any &&other) noexcept { release(); - vtable = std::exchange(other.vtable, &basic_vtable); storage = std::move(other.storage); - node = std::exchange(other.node, nullptr); + ctx = other.ctx; + node = std::exchange(other.node, internal::meta_type_node{}); + vtable = std::exchange(other.vtable, &basic_vtable); return *this; } @@ -41739,23 +56869,20 @@ public: } /*! @copydoc any::type */ - [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type type() const noexcept; /*! @copydoc any::data */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return storage.data(); } /*! @copydoc any::data */ - [[nodiscard]] void *data() ENTT_NOEXCEPT { + [[nodiscard]] void *data() noexcept { return storage.data(); } /** * @brief Invokes the underlying function, if possible. - * - * @sa meta_func::invoke - * * @tparam Args Types of arguments to use to invoke the function. * @param id Unique identifier. * @param args Parameters to use to invoke the function. @@ -41770,10 +56897,6 @@ public: /** * @brief Sets the value of a given variable. - * - * The type of the value is such that a cast or conversion to the type of - * the variable is possible. Otherwise, invoking the setter does nothing. - * * @tparam Type Type of value to assign. * @param id Unique identifier. * @param value Parameter to use to set the underlying variable. @@ -41799,42 +56922,24 @@ public: */ template [[nodiscard]] const Type *try_cast() const { - if(const auto &info = type_id(); node && *node->info == info) { - return any_cast(&storage); - } else if(node) { - for(auto *it = node->base; it; it = it->next) { - const auto as_const = it->cast(as_ref()); - - if(const Type *base = as_const.template try_cast(); base) { - return base; - } - } - } - - return nullptr; + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); } /*! @copydoc try_cast */ template [[nodiscard]] Type *try_cast() { - if(const auto &info = type_id(); node && *node->info == info) { - return any_cast(&storage); - } else if(node) { - for(auto *it = node->base; it; it = it->next) { - if(Type *base = it->cast(as_ref()).template try_cast(); base) { - return base; - } - } + if constexpr(std::is_const_v) { + return std::as_const(*this).try_cast>(); + } else { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); } - - return nullptr; } /** * @brief Tries to cast an instance to a given type. * - * The type of the instance must be such that the cast is possible. - * * @warning * Attempting to perform an invalid cast results is undefined behavior. * @@ -41872,7 +56977,7 @@ public: */ [[nodiscard]] bool allow_cast(const meta_type &type) { if(auto other = std::as_const(*this).allow_cast(type); other) { - if(other.storage.owner()) { + if(other.owner()) { std::swap(*this, other); } @@ -41890,12 +56995,11 @@ public: */ template [[nodiscard]] meta_any allow_cast() const { - const auto other = allow_cast(internal::meta_node>>::resolve()); - if constexpr(std::is_reference_v && !std::is_const_v>) { - return other.storage.owner() ? other : meta_any{}; + return meta_any{meta_ctx_arg, *ctx}; } else { - return other; + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}); } } @@ -41906,25 +57010,17 @@ public: */ template bool allow_cast() { - if(auto other = std::as_const(*this).allow_cast(internal::meta_node>>::resolve()); other) { - if(other.storage.owner()) { - std::swap(*this, other); - return true; - } - - return (static_cast> &>(storage).data() != nullptr); - } - - return false; + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); } /*! @copydoc any::emplace */ template void emplace(Args &&...args) { release(); - vtable = &basic_vtable>>; storage.emplace(std::forward(args)...); - node = internal::meta_node>>::resolve(); + node = internal::resolve>>(internal::meta_context::from(*ctx)); + vtable = &basic_vtable>>; } /*! @copydoc any::assign */ @@ -41936,26 +57032,26 @@ public: /*! @copydoc any::reset */ void reset() { release(); - vtable = &basic_vtable; storage.reset(); - node = nullptr; + node = {}; + vtable = &basic_vtable; } /** * @brief Returns a sequence container proxy. * @return A sequence container proxy for the underlying object. */ - [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { any detached = storage.as_ref(); - meta_sequence_container proxy; + meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } /*! @copydoc as_sequence_container */ - [[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { any detached = storage.as_ref(); - meta_sequence_container proxy; + meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } @@ -41964,17 +57060,17 @@ public: * @brief Returns an associative container proxy. * @return An associative container proxy for the underlying object. */ - [[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT { + [[nodiscard]] meta_associative_container as_associative_container() noexcept { any detached = storage.as_ref(); - meta_associative_container proxy; + meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } /*! @copydoc as_associative_container */ - [[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { any detached = storage.as_ref(); - meta_associative_container proxy; + meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } @@ -41984,8 +57080,8 @@ public: * @return A wrapper that shares a reference to an unmanaged object if the * wrapped element is dereferenceable, an invalid meta any otherwise. */ - [[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT { - meta_any ret{}; + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; vtable(operation::deref, storage, &ret); return ret; } @@ -41994,56 +57090,52 @@ public: * @brief Returns false if a wrapper is invalid, true otherwise. * @return False if the wrapper is invalid, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return !(node.info == nullptr); } /*! @copydoc any::operator== */ - [[nodiscard]] bool operator==(const meta_any &other) const { - return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage); + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); } /*! @copydoc any::as_ref */ - [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { - return meta_any{*this, storage.as_ref()}; + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::as_ref */ - [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { - return meta_any{*this, storage.as_ref()}; + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::owner */ - [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + [[nodiscard]] bool owner() const noexcept { return storage.owner(); } private: any storage; - internal::meta_type_node *node; + const meta_ctx *ctx; + internal::meta_type_node node; vtable_type *vtable; }; /** - * @brief Checks if two wrappers differ in their content. - * @param lhs A wrapper, either empty or not. - * @param rhs A wrapper, either empty or not. - * @return True if the two wrappers differ in their content, false otherwise. + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. */ -[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -/** - * @brief Constructs a wrapper from a given type, passing it all arguments. - * @tparam Type Type of object to use to initialize the wrapper. - * @tparam Args Types of arguments to use to construct the new instance. - * @param args Parameters to use to construct the instance. - * @return A properly initialized wrapper for an object of the given type. - */ -template -meta_any make_meta(Args &&...args) { - return meta_any{std::in_place_type, std::forward(args)...}; +template +meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; } /** @@ -42054,7 +57146,7 @@ meta_any make_meta(Args &&...args) { */ template meta_any forward_as_meta(Type &&value) { - return meta_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; + return forward_as_meta(locator::value_or(), std::forward(value)); } /** @@ -42065,8 +57157,65 @@ meta_any forward_as_meta(Type &&value) { * Handles are used to generate references to actual objects when needed. */ struct meta_handle { - /*! @brief Default constructor. */ - meta_handle() = default; + /*! Default constructor. */ + meta_handle() noexcept + : meta_handle{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept + : any{meta_ctx_arg, area} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) noexcept + : any{ctx, std::in_place_type, value} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) noexcept + : meta_handle{locator::value_or(), value} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_handle(const meta_ctx &area, const meta_handle &other) + : any{area, other.any} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} /*! @brief Default copy constructor, deleted on purpose. */ meta_handle(const meta_handle &) = delete; @@ -42086,26 +57235,11 @@ struct meta_handle { */ meta_handle &operator=(meta_handle &&) = default; - /** - * @brief Creates a handle that points to an unmanaged object. - * @tparam Type Type of object to use to initialize the handle. - * @param value An instance of an object to use to initialize the handle. - */ - template, meta_handle>>> - meta_handle(Type &value) ENTT_NOEXCEPT - : meta_handle{} { - if constexpr(std::is_same_v, meta_any>) { - any = value.as_ref(); - } else { - any.emplace(value); - } - } - /** * @brief Returns false if a handle is invalid, true otherwise. * @return False if the handle is invalid, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(any); } @@ -42128,65 +57262,65 @@ private: /*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { - /*! @brief Node type. */ - using node_type = internal::meta_prop_node; + /*! @brief Default constructor. */ + meta_prop() noexcept + : node{}, + ctx{} {} /** - * @brief Constructs an instance from a given node. + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ - meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} - - /** - * @brief Returns the stored key as a const reference. - * @return A wrapper containing the key stored with the property. - */ - [[nodiscard]] meta_any key() const { - return node->id.as_ref(); - } + meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept + : node{&curr}, + ctx{&area} {} /** * @brief Returns the stored value by copy. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { - return node->value; + 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 true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); } private: - const node_type *node; + const internal::meta_prop_node *node; + const meta_ctx *ctx; }; /*! @brief Opaque wrapper for data members. */ struct meta_data { - /*! @brief Node type. */ - using node_type = internal::meta_data_node; /*! @brief Unsigned integer type. */ - using size_type = typename node_type::size_type; + using size_type = typename internal::meta_data_node::size_type; - /*! @copydoc meta_prop::meta_prop */ - meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} + /*! @brief Default constructor. */ + meta_data() noexcept + : node{}, + ctx{} {} - /*! @copydoc meta_type::id */ - [[nodiscard]] id_type id() const ENTT_NOEXCEPT { - return node->id; - } + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} /** * @brief Returns the number of setters available. * @return The number of setters available. */ - [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + [[nodiscard]] size_type arity() const noexcept { return node->arity; } @@ -42194,29 +57328,23 @@ struct meta_data { * @brief Indicates whether a data member is constant or not. * @return True if the data member is constant, false otherwise. */ - [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_const); + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a data member is static or not. * @return True if the data member is static, false otherwise. */ - [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_static); + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); } /*! @copydoc meta_any::type */ - [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type type() const noexcept; /** * @brief Sets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member.
- * The type of the value is such that a cast or conversion to the type of - * the variable is possible. Otherwise, invoking the setter does nothing. - * * @tparam Type Type of value to assign. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. @@ -42224,20 +57352,16 @@ struct meta_data { */ template bool set(meta_handle instance, Type &&value) const { - return node->set && node->set(std::move(instance), std::forward(value)); + return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); } /** * @brief Gets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member. - * * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { - return node->get(std::move(instance)); + return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); } /** @@ -42245,64 +57369,63 @@ struct meta_data { * @param index Index of the setter of which to return the accepted type. * @return The type accepted by the i-th setter. */ - [[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Returns a range to visit registered meta properties. * @return An iterable range to visit registered meta properties. */ - [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { - return node->prop; + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** - * @brief Lookup function for registered meta properties. + * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ - [[nodiscard]] meta_prop prop(meta_any key) const { - for(auto curr: prop()) { - if(curr.key() == key) { - return curr; - } - } - - return nullptr; + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); } private: - const node_type *node; + const internal::meta_data_node *node; + const meta_ctx *ctx; }; /*! @brief Opaque wrapper for member functions. */ struct meta_func { - /*! @brief Node type. */ - using node_type = internal::meta_func_node; /*! @brief Unsigned integer type. */ - using size_type = typename node_type::size_type; + using size_type = typename internal::meta_func_node::size_type; - /*! @copydoc meta_prop::meta_prop */ - meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} + /*! @brief Default constructor. */ + meta_func() noexcept + : node{}, + ctx{} {} - /*! @copydoc meta_type::id */ - [[nodiscard]] id_type id() const ENTT_NOEXCEPT { - return node->id; - } + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} /** * @brief Returns the number of arguments accepted by a member function. * @return The number of arguments accepted by the member function. */ - [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + [[nodiscard]] size_type arity() const noexcept { return node->arity; } @@ -42310,39 +57433,37 @@ struct meta_func { * @brief Indicates whether a member function is constant or not. * @return True if the member function is constant, false otherwise. */ - [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_const); + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a member function is static or not. * @return True if the member function is static, false otherwise. */ - [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_static); + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); } /** * @brief Returns the return type of a member function. * @return The return type of the member function. */ - [[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type ret() const noexcept; /** * @brief Returns the type of the i-th argument of a member function. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a member function. */ - [[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Invokes the underlying function, if possible. * - * To invoke a member function, the parameters must be such that a cast or - * conversion to the required types is possible. Otherwise, an empty and - * thus invalid wrapper is returned.
- * It must be possible to cast the instance to the parent type of the member - * function. + * @warning + * The context of the arguments is **not** changed.
+ * It's up to the caller to bind them to the right context(s). * * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. @@ -42350,139 +57471,159 @@ struct meta_func { * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { - return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; + return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke - * - * @sa invoke - * * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. - * @return A wrapper containing the new instance, if any. + * @return A wrapper containing the returned value, if any. */ template meta_any invoke(meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; - return invoke(std::move(instance), arguments, sizeof...(Args)); + meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; + return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /*! @copydoc meta_data::prop */ - [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { - return node->prop; + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** - * @brief Lookup function for registered meta properties. + * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ - [[nodiscard]] meta_prop prop(meta_any key) const { - for(auto curr: prop()) { - if(curr.key() == key) { - return curr; - } - } + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } - return nullptr; + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return node->next ? meta_func{*ctx, *node->next} : meta_func{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); } private: - const node_type *node; + const internal::meta_func_node *node; + const meta_ctx *ctx; }; /*! @brief Opaque wrapper for types. */ class meta_type { - template - [[nodiscard]] std::decay_t().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const { - std::decay_t*Member)> candidate{}; - size_type extent{sz + 1u}; + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; bool ambiguous{}; - for(auto *curr = (node->*Member); curr; curr = curr->next) { - if(pred(curr) && curr->arity == sz) { - size_type direct{}; - size_type ext{}; + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } - for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) { - const auto type = args[next].type(); - const auto other = curr->arg(next); + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); if(const auto &info = other.info(); info == type.info()) { - ++direct; - } else { - ext += internal::find_by<&node_type::base>(info, type.node) - || internal::find_by<&node_type::conv>(info, type.node) - || (type.node->conversion_helper && other.node->conversion_helper); + ++match; + } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { + break; } } - if((direct + ext) == sz) { - if(ext < extent) { + if(pos == sz) { + if(!candidate || match > same) { candidate = curr; - extent = ext; + same = match; ambiguous = false; - } else if(ext == extent) { + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { + candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + ambiguous = true; } } } } - return (candidate && !ambiguous) ? candidate : decltype(candidate){}; + return ambiguous ? nullptr : candidate; } public: - /*! @brief Node type. */ - using node_type = internal::meta_type_node; - /*! @brief Node type. */ - using base_node_type = internal::meta_base_node; /*! @brief Unsigned integer type. */ - using size_type = typename node_type::size_type; + using size_type = typename internal::meta_type_node::size_type; - /*! @copydoc meta_prop::meta_prop */ - meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} + /*! @brief Default constructor. */ + meta_type() noexcept + : node{}, + ctx{} {} /** - * @brief Constructs an instance from a given base node. - * @param curr The base node with which to construct the instance. + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. */ - meta_type(const base_node_type *curr) ENTT_NOEXCEPT - : node{curr ? curr->type : nullptr} {} + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.type(internal::meta_context::from(area))} {} /** * @brief Returns the type info object of the underlying type. * @return The type info object of the underlying type. */ - [[nodiscard]] const type_info &info() const ENTT_NOEXCEPT { - return *node->info; + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; } /** * @brief Returns the identifier assigned to a type. * @return The identifier assigned to the type. */ - [[nodiscard]] id_type id() const ENTT_NOEXCEPT { - return node->id; + [[nodiscard]] id_type id() const noexcept { + return node.id; } /** * @brief Returns the size of the underlying type if known. * @return The size of the underlying type if known, 0 otherwise. */ - [[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { - return node->size_of; + [[nodiscard]] size_type size_of() const noexcept { + return node.size_of; } /** @@ -42490,40 +57631,65 @@ public: * @return True if the underlying type is an arithmetic type, false * otherwise. */ - [[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_arithmetic); + [[nodiscard]] bool is_arithmetic() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_signed); } /** * @brief Checks whether a type refers to an array type or not. * @return True if the underlying type is an array type, false otherwise. */ - [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_array); + [[nodiscard]] bool is_array() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_array); } /** * @brief Checks whether a type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. */ - [[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_enum); + [[nodiscard]] bool is_enum() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_enum); } /** * @brief Checks whether a type refers to a class or not. * @return True if the underlying type is a class, false otherwise. */ - [[nodiscard]] bool is_class() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_class); + [[nodiscard]] bool is_class() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_class); } /** * @brief Checks whether a type refers to a pointer or not. * @return True if the underlying type is a pointer, false otherwise. */ - [[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_pointer); + [[nodiscard]] bool is_pointer() const noexcept { + return node.info && (node.info->hash() != remove_pointer().info().hash()); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const noexcept { + return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; } /** @@ -42531,24 +57697,24 @@ public: * @return True if the underlying type is a pointer-like one, false * otherwise. */ - [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_meta_pointer_like); + [[nodiscard]] bool is_pointer_like() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); } /** * @brief Checks whether a type refers to a sequence container or not. * @return True if the type is a sequence container, false otherwise. */ - [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_meta_sequence_container); + [[nodiscard]] bool is_sequence_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); } /** * @brief Checks whether a type refers to an associative container or not. * @return True if the type is an associative container, false otherwise. */ - [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_meta_associative_container); + [[nodiscard]] bool is_associative_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); } /** @@ -42557,27 +57723,24 @@ public: * @return True if the type is a recognized class template specialization, * false otherwise. */ - [[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT { - return (node->templ != nullptr); + [[nodiscard]] bool is_template_specialization() const noexcept { + return (node.templ.arity != 0u); } /** * @brief Returns the number of template arguments. * @return The number of template arguments. */ - [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { - return node->templ ? node->templ->arity : size_type{}; + [[nodiscard]] size_type template_arity() const noexcept { + return node.templ.arity; } /** * @brief Returns a tag for the class template of the underlying type. - * - * @sa meta_class_template_tag - * * @return The tag for the class template of the underlying type. */ - [[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { - return node->templ ? node->templ->type : meta_type{}; + [[nodiscard]] inline meta_type template_type() const noexcept { + return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; } /** @@ -42585,107 +57748,142 @@ public: * @param index Index of the template argument of which to return the type. * @return The type of the i-th template argument of a type. */ - [[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT { - return index < template_arity() ? node->templ->arg(index) : meta_type{}; + [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; } /** * @brief Returns a range to visit registered top-level base meta types. * @return An iterable range to visit registered top-level base meta types. */ - [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { - return node->base; - } - - /** - * @brief Lookup function for registered base meta types. - * @param id Unique identifier. - * @return The registered base meta type for the given identifier, if any. - */ - [[nodiscard]] meta_type base(const id_type id) const { - return internal::find_by<&node_type::base>(id, node); + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; } /** * @brief Returns a range to visit registered top-level meta data. * @return An iterable range to visit registered top-level meta data. */ - [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { - return node->data; + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; } /** - * @brief Lookup function for registered meta data. - * - * Registered meta data of base classes will also be visited. - * + * @brief Lookup utility for meta data (bases are also visited). * @param id Unique identifier. * @return The registered meta data for the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { - return internal::find_by<&node_type::data>(id, node); + if(node.details) { + if(const auto it = node.details->data.find(id); it != node.details->data.cend()) { + return meta_data{*ctx, it->second}; + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.data(id); elem) { + return elem; + } + } + + return meta_data{}; } /** * @brief Returns a range to visit registered top-level functions. * @return An iterable range to visit registered top-level functions. */ - [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { - return node->func; + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; } /** - * @brief Lookup function for registered meta functions. + * @brief Lookup utility for meta functions (bases are also visited). * - * Registered meta functions of base classes will also be visited.
* In case of overloaded functions, the first one with the required - * identifier will be returned. + * identifier is returned. * * @param id Unique identifier. * @return The registered meta function for the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { - return internal::find_by<&node_type::func>(id, node); + if(node.details) { + if(const auto it = node.details->func.find(id); it != node.details->func.cend()) { + return meta_func{*ctx, it->second}; + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.func(id); elem) { + return elem; + } + } + + return meta_func{}; } /** * @brief Creates an instance of the underlying type, if possible. * - * Parameters are such that a cast or conversion to the required types is - * possible. Otherwise, an empty and thus invalid wrapper is returned.
* If suitable, the implicitly generated default constructor is used. * + * @warning + * The context of the arguments is **not** changed.
+ * It's up to the caller to bind them to the right context(s). + * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { - const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; }); - return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{}); + if(node.details) { + if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(sz == 0u && node.default_constructor) { + return node.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief construct - * - * @sa construct - * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any construct(Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return construct(arguments, sizeof...(Args)); } + /** + * @brief Wraps an opaque element of the underlying 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 { + 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 { + return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; + } + /** * @brief Invokes a function given an identifier, if possible. * - * It must be possible to cast the instance to the parent type of the member - * function. - * - * @sa meta_func::invoke + * @warning + * The context of the arguments is **not** changed.
+ * It's up to the caller to bind them to the right context(s). * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. @@ -42694,40 +57892,40 @@ public: * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { - const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); - - for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) { - candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + 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) { + return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); + } + } } - return candidate ? candidate->invoke(std::move(instance), args) : meta_any{}; + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke * - * @sa invoke - * * @param id Unique identifier. * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. - * @return A wrapper containing the new instance, if any. + * @return A wrapper containing the returned value, if any. */ template meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; - return invoke(id, std::move(instance), arguments, sizeof...(Args)); + meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; + return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /** * @brief Sets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member.
- * The type of the value is such that a cast or conversion to the type of - * the variable is possible. Otherwise, invoking the setter does nothing. - * * @tparam Type Type of value to assign. * @param id Unique identifier. * @param instance An opaque instance of the underlying type. @@ -42742,45 +57940,51 @@ public: /** * @brief Gets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member. - * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { const auto candidate = data(id); - return candidate ? candidate.get(std::move(instance)) : meta_any{}; + return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Returns a range to visit registered top-level meta properties. * @return An iterable range to visit registered top-level meta properties. */ - [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { - return node->prop; + [[nodiscard]] meta_range prop() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; } /** - * @brief Lookup function for meta properties. - * - * Properties of base classes are also visited. - * + * @brief Lookup utility for meta properties (bases are also visited). * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ - [[nodiscard]] meta_prop prop(meta_any key) const { - return internal::find_by<&internal::meta_type_node::prop>(key, node); + [[nodiscard]] meta_prop prop(const id_type key) const { + if(node.details) { + if(const auto it = node.details->prop.find(key); it != node.details->prop.cend()) { + return meta_prop{*ctx, it->second}; + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.prop(key); elem) { + return elem; + } + } + + return meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return !(ctx == nullptr); } /** @@ -42788,12 +57992,13 @@ public: * @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_type &other) const ENTT_NOEXCEPT { - return (!node && !other.node) || (node && other.node && *node->info == *other.node->info); + [[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)); } private: - const node_type *node; + internal::meta_type_node node; + const meta_ctx *ctx; }; /** @@ -42802,12 +58007,12 @@ private: * @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_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { return !(lhs == rhs); } -[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { - return node; +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return node.info ? meta_type{*ctx, node} : meta_type{}; } template @@ -42834,26 +58039,17 @@ bool meta_any::set(const id_type id, Type &&value) { } [[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { - if(const auto &info = type.info(); node && *node->info == info) { + if(node.info && *node.info == type.info()) { return as_ref(); - } else if(node) { - for(auto *it = node->conv; it; it = it->next) { - if(*it->type->info == info) { - return it->conv(*this); - } + } + + if(const auto *value = data(); node.details) { + if(auto it = node.details->conv.find(type.info().hash()); it != node.details->conv.cend()) { + return it->second.conv(*ctx, data()); } - if(node->conversion_helper && (type.is_arithmetic() || type.is_enum())) { - // exploits the fact that arithmetic types and enums are also default constructible - auto other = type.construct(); - ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found"); - const auto value = node->conversion_helper(nullptr, storage.data()); - other.node->conversion_helper(other.storage.data(), &value); - return other; - } - - for(auto *it = node->base; it; it = it->next) { - const auto as_const = it->cast(as_ref()); + for(auto &&curr: node.details->base) { + const auto &as_const = curr.second.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.second.cast(value)); if(auto other = as_const.allow_cast(type); other) { return other; @@ -42861,36 +58057,45 @@ bool meta_any::set(const id_type id, Type &&value) { } } - return {}; + if(node.conversion_helper && (type.is_arithmetic() || type.is_enum())) { + // exploits the fact that arithmetic types and enums are also default constructible + auto other = type.construct(); + ENTT_ASSERT(other.node.conversion_helper, "Conversion helper not found"); + const auto value = node.conversion_helper(nullptr, storage.data()); + other.node.conversion_helper(other.storage.data(), &value); + return other; + } + + return meta_any{meta_ctx_arg, *ctx}; } inline bool meta_any::assign(const meta_any &other) { - auto value = other.allow_cast(node); + auto value = other.allow_cast({*ctx, node}); return value && storage.assign(std::move(value.storage)); } inline bool meta_any::assign(meta_any &&other) { - if(*node->info == *other.node->info) { + if(*node.info == *other.node.info) { return storage.assign(std::move(other.storage)); } return assign(std::as_const(other)); } -[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { - return node->type; +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; } -[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { - return node->ret; +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; } -[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT { - return index < arity() ? node->arg(index) : meta_type{}; +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; } -[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT { - return index < arity() ? node->arg(index) : meta_type{}; +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; } /** @@ -42901,11 +58106,25 @@ inline bool meta_any::assign(meta_any &&other) { class meta_sequence_container::meta_iterator final { friend class meta_sequence_container; - using deref_fn_type = meta_any(const any &, const std::ptrdiff_t); + enum class operation : std::uint8_t { + incr, + deref + }; + + using vtable_type = void(const operation, const any &, const std::ptrdiff_t, meta_any *); template - static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) { - return meta_any{std::in_place_type::reference>, any_cast(value)[pos]}; + static void basic_vtable(const operation op, const any &value, const std::ptrdiff_t offset, meta_any *other) { + switch(op) { + case operation::incr: { + auto &it = any_cast(const_cast(value)); + it = std::next(it, offset); + } break; + case operation::deref: { + const auto &it = any_cast(value); + other->emplace(*it); + } break; + } } public: @@ -42915,60 +58134,64 @@ public: using reference = value_type; using iterator_category = std::input_iterator_tag; - meta_iterator() ENTT_NOEXCEPT - : deref{}, - offset{}, + constexpr meta_iterator() noexcept + : ctx{}, + vtable{}, handle{} {} template - explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT - : deref{&deref_fn}, - offset{init}, + explicit meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, handle{std::move(iter)} {} - meta_iterator &operator++() ENTT_NOEXCEPT { - return ++offset, *this; + meta_iterator &operator++() noexcept { + vtable(operation::incr, handle, 1, nullptr); + return *this; } - meta_iterator operator++(int value) ENTT_NOEXCEPT { + meta_iterator operator++(int value) noexcept { meta_iterator orig = *this; - offset += ++value; + vtable(operation::incr, handle, ++value, nullptr); return orig; } - meta_iterator &operator--() ENTT_NOEXCEPT { - return --offset, *this; + meta_iterator &operator--() noexcept { + vtable(operation::incr, handle, -1, nullptr); + return *this; } - meta_iterator operator--(int value) ENTT_NOEXCEPT { + meta_iterator operator--(int value) noexcept { meta_iterator orig = *this; - offset -= ++value; + vtable(operation::incr, handle, --value, nullptr); return orig; } [[nodiscard]] reference operator*() const { - return deref(handle, offset); + reference other{meta_ctx_arg, *ctx}; + vtable(operation::deref, handle, 0, &other); + return other; } [[nodiscard]] pointer operator->() const { return operator*(); } - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } - [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { - return offset == other.offset; + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; } - [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: - deref_fn_type *deref; - difference_type offset; + const meta_ctx *ctx; + vtable_type *vtable; any handle; }; @@ -43005,27 +58228,29 @@ public: using reference = value_type; using iterator_category = std::input_iterator_tag; - meta_iterator() ENTT_NOEXCEPT - : vtable{}, + constexpr meta_iterator() noexcept + : ctx{}, + vtable{}, handle{} {} template - meta_iterator(std::integral_constant, It iter) ENTT_NOEXCEPT - : vtable{&basic_vtable}, + meta_iterator(const meta_ctx &area, std::integral_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, handle{std::move(iter)} {} - meta_iterator &operator++() ENTT_NOEXCEPT { + meta_iterator &operator++() noexcept { vtable(operation::incr, handle, nullptr); return *this; } - meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator operator++(int) noexcept { meta_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const { - reference other; + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; vtable(operation::deref, handle, &other); return other; } @@ -43034,19 +58259,20 @@ public: return operator*(); } - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } - [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { return handle == other.handle; } - [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: + const meta_ctx *ctx; vtable_type *vtable; any handle; }; @@ -43060,15 +58286,15 @@ private: * @brief Returns the meta value type of a container. * @return The meta value type of the container. */ -[[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { - return value_type_node; +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the size of a container. * @return The size of the container. */ -[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { return size_fn(storage); } @@ -43094,7 +58320,7 @@ inline bool meta_sequence_container::clear() { * @return An iterator to the first element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { - return iter_fn(storage, false); + return iter_fn(*ctx, storage, false); } /** @@ -43102,7 +58328,7 @@ inline bool meta_sequence_container::clear() { * @return An iterator that is past the last element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { - return iter_fn(storage, true); + return iter_fn(*ctx, storage, true); } /** @@ -43112,7 +58338,7 @@ inline bool meta_sequence_container::clear() { * @return A possibly invalid iterator to the inserted element. */ inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { - return insert_fn(storage, it.offset, value); + return insert_or_erase_fn(*ctx, storage, it.handle, value); } /** @@ -43121,7 +58347,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 erase_fn(storage, it.offset); + return insert(std::move(it), {}); } /** @@ -43140,7 +58366,7 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ -[[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { return static_cast(storage); } @@ -43148,7 +58374,7 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator * @brief Returns true if a container is also key-only, false otherwise. * @return True if the associative container is also key-only, false otherwise. */ -[[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { +[[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { return key_only_container; } @@ -43156,25 +58382,25 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator * @brief Returns the meta key type of a container. * @return The meta key type of the a container. */ -[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { - return key_type_node; +[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { + return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the meta mapped type of a container. * @return The meta mapped type of the a container. */ -[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { - return mapped_type_node; +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { + return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::value_type */ -[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { - return value_type_node; +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::size */ -[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { return size_fn(storage); } @@ -43185,22 +58411,32 @@ inline bool meta_associative_container::clear() { /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { - return iter_fn(storage, false); + return iter_fn(*ctx, storage, false); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { - return iter_fn(storage, true); + return iter_fn(*ctx, storage, true); } /** - * @brief Inserts an element (a key/value pair) into a container. + * @brief Inserts a key only element into a container. + * @param key The key of the element to insert. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key) { + meta_any value{*ctx, std::in_place_type}; + return (insert_or_erase_fn(storage, key, value) != 0u); +} + +/** + * @brief Inserts a key/value element into a container. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ -inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { - return insert_fn(storage, key, value); +inline bool meta_associative_container::insert(meta_any key, meta_any value) { + return (insert_or_erase_fn(storage, key, value) != 0u); } /** @@ -43208,8 +58444,8 @@ inline bool meta_associative_container::insert(meta_any key, meta_any value = {} * @param key The key of the element to remove. * @return A bool denoting whether the removal took place. */ -inline bool meta_associative_container::erase(meta_any key) { - return erase_fn(storage, key); +inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return insert(std::move(key), meta_any{meta_ctx_arg, *ctx}); } /** @@ -43218,14 +58454,14 @@ inline bool meta_associative_container::erase(meta_any key) { * @return An iterator to the element with the given key, if any. */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { - return find_fn(storage, key); + return find_fn(*ctx, storage, key); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ -[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_associative_container::operator bool() const noexcept { return static_cast(storage); } @@ -43249,7 +58485,7 @@ template struct is_dynamic_sequence_container: std::false_type {}; template -struct is_dynamic_sequence_container>: std::true_type {}; +struct is_dynamic_sequence_container>: std::true_type {}; template struct is_key_only_meta_associative_container: std::true_type {}; @@ -43262,7 +58498,7 @@ struct basic_meta_sequence_container_traits { using iterator = meta_sequence_container::iterator; using size_type = std::size_t; - [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { + [[nodiscard]] static size_type size(const any &container) noexcept { return any_cast(container).size(); } @@ -43277,43 +58513,39 @@ struct basic_meta_sequence_container_traits { return false; } - [[nodiscard]] static iterator iter(any &container, const bool as_end) { - using std::begin; - + [[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) { if(auto *const cont = any_cast(&container); cont) { - return iterator{begin(*cont), static_cast(as_end * cont->size())}; + return iterator{ctx, as_end ? cont->end() : cont->begin()}; } const Type &as_const = any_cast(container); - return iterator{begin(as_const), static_cast(as_end * as_const.size())}; + return iterator{ctx, as_end ? as_const.end() : as_const.begin()}; } - [[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset, [[maybe_unused]] meta_any &value) { + [[nodiscard]] static iterator insert_or_erase([[maybe_unused]] const meta_ctx &ctx, [[maybe_unused]] any &container, [[maybe_unused]] const any &handle, [[maybe_unused]] meta_any &value) { if constexpr(is_dynamic_sequence_container::value) { if(auto *const cont = any_cast(&container); cont) { - // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector - if(value.allow_cast() || value.allow_cast()) { - using std::begin; - const auto *element = value.try_cast>(); - const auto curr = cont->insert(begin(*cont) + offset, element ? *element : value.cast()); - return iterator{curr, curr - begin(*cont)}; + typename Type::const_iterator it{}; + + if(auto *non_const = any_cast(&handle); non_const) { + it = *non_const; + } else { + it = any_cast(handle); + } + + if(value) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(value.allow_cast() || value.allow_cast()) { + const auto *element = value.try_cast>(); + return iterator{ctx, cont->insert(it, element ? *element : value.cast())}; + } + } else { + return iterator{ctx, cont->erase(it)}; } } } - return {}; - } - - [[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset) { - if constexpr(is_dynamic_sequence_container::value) { - if(auto *const cont = any_cast(&container); cont) { - using std::begin; - const auto curr = cont->erase(begin(*cont) + offset); - return iterator{curr, curr - begin(*cont)}; - } - } - - return {}; + return iterator{}; } }; @@ -43324,7 +58556,7 @@ struct basic_meta_associative_container_traits { static constexpr auto key_only = is_key_only_meta_associative_container::value; - [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { + [[nodiscard]] static size_type size(const any &container) noexcept { return any_cast(container).size(); } @@ -43337,46 +58569,41 @@ struct basic_meta_associative_container_traits { return false; } - [[nodiscard]] static iterator iter(any &container, const bool as_end) { - using std::begin; - using std::end; - + [[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) { if(auto *const cont = any_cast(&container); cont) { - return iterator{std::integral_constant{}, as_end ? end(*cont) : begin(*cont)}; + return iterator{ctx, std::bool_constant{}, as_end ? cont->end() : cont->begin()}; } const auto &as_const = any_cast(container); - return iterator{std::integral_constant{}, as_end ? end(as_const) : begin(as_const)}; + return iterator{ctx, std::bool_constant{}, as_end ? as_const.end() : as_const.begin()}; } - [[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) { - auto *const cont = any_cast(&container); - - if constexpr(is_key_only_meta_associative_container::value) { - return cont && key.allow_cast() - && cont->insert(key.cast()).second; - } else { - return cont && key.allow_cast() && value.allow_cast() - && cont->emplace(key.cast(), value.cast()).second; + [[nodiscard]] static size_type insert_or_erase(any &container, meta_any &key, meta_any &value) { + if(auto *const cont = any_cast(&container); cont && key.allow_cast()) { + if(value) { + if constexpr(key_only) { + return cont->insert(key.cast()).second; + } else { + return value.allow_cast() && cont->emplace(key.cast(), value.cast()).second; + } + } else { + return cont->erase(key.cast()); + } } + + return 0u; } - [[nodiscard]] static bool erase(any &container, meta_any &key) { - auto *const cont = any_cast(&container); - return cont && key.allow_cast() - && (cont->erase(key.cast()) != cont->size()); - } - - [[nodiscard]] static iterator find(any &container, meta_any &key) { + [[nodiscard]] static iterator find(const meta_ctx &ctx, any &container, meta_any &key) { if(key.allow_cast()) { if(auto *const cont = any_cast(&container); cont) { - return iterator{std::integral_constant{}, cont->find(key.cast())}; + return iterator{ctx, std::bool_constant{}, cont->find(key.cast())}; } - return iterator{std::integral_constant{}, any_cast(container).find(key.cast())}; + return iterator{ctx, std::bool_constant{}, any_cast(container).find(key.cast())}; } - return {}; + return iterator{}; } }; @@ -43389,96 +58616,106 @@ struct basic_meta_associative_container_traits { /** * @brief Meta sequence container traits for `std::vector`s of any type. - * @tparam Type The type of elements. - * @tparam Args Other arguments. + * @tparam Args Template arguments for the container. */ -template -struct meta_sequence_container_traits> - : internal::basic_meta_sequence_container_traits> {}; +template +struct meta_sequence_container_traits> + : internal::basic_meta_sequence_container_traits> {}; /** * @brief Meta sequence container traits for `std::array`s of any type. - * @tparam Type The type of elements. - * @tparam N The number of elements. + * @tparam Type Template arguments for the container. + * @tparam N Template arguments for the container. */ template struct meta_sequence_container_traits> : internal::basic_meta_sequence_container_traits> {}; /** - * @brief Meta associative container traits for `std::map`s of any type. - * @tparam Key The key type of elements. - * @tparam Value The value type of elements. - * @tparam Args Other arguments. + * @brief Meta sequence container traits for `std::list`s of any type. + * @tparam Args Template arguments for the container. */ -template -struct meta_associative_container_traits> - : internal::basic_meta_associative_container_traits> {}; +template +struct meta_sequence_container_traits> + : internal::basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::deque`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : internal::basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `std::unordered_map`s of any * type. - * @tparam Key The key type of elements. - * @tparam Value The value type of elements. - * @tparam Args Other arguments. + * @tparam Args Template arguments for the container. */ -template -struct meta_associative_container_traits> - : internal::basic_meta_associative_container_traits> {}; +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `std::set`s of any type. - * @tparam Key The type of elements. - * @tparam Args Other arguments. + * @tparam Args Template arguments for the container. */ -template -struct meta_associative_container_traits> - : internal::basic_meta_associative_container_traits> {}; +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `std::unordered_set`s of any * type. - * @tparam Key The type of elements. - * @tparam Args Other arguments. + * @tparam Args Template arguments for the container. */ -template -struct meta_associative_container_traits> - : internal::basic_meta_associative_container_traits> {}; +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `dense_map`s of any type. - * @tparam Key The key type of the elements. - * @tparam Type The value type of the elements. - * @tparam Args Other arguments. + * @tparam Args Template arguments for the container. */ -template -struct meta_associative_container_traits> - : internal::basic_meta_associative_container_traits> {}; +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `dense_set`s of any type. - * @tparam Type The value type of the elements. - * @tparam Args Other arguments. + * @tparam Args Template arguments for the container. */ -template -struct meta_associative_container_traits> - : internal::basic_meta_associative_container_traits> {}; +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; } // namespace entt #endif -// #include "meta/ctx.hpp" +// #include "meta/context.hpp" #ifndef ENTT_META_CTX_HPP #define ENTT_META_CTX_HPP -// #include "../config/config.h" +// #include "../container/dense_map.hpp" -// #include "../core/attribute.h" +// #include "../core/fwd.hpp" + +// #include "../core/utility.hpp" namespace entt { +class meta_ctx; + /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -43488,20 +58725,11 @@ namespace internal { struct meta_type_node; -struct ENTT_API meta_context { - // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ - // inline static meta_type_node *local = nullptr; - // inline static meta_type_node **global = &local; +struct meta_context { + dense_map value{}; - [[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT { - static meta_type_node *chain = nullptr; - return chain; - } - - [[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT { - static meta_type_node **chain = &local(); - return chain; - } + static inline meta_context &from(meta_ctx &ctx); + static inline const meta_context &from(const meta_ctx &ctx); }; } // namespace internal @@ -43511,20 +58739,36 @@ struct ENTT_API meta_context { * @endcond */ -/*! @brief Opaque container for a meta context. */ -struct meta_ctx { - /** - * @brief Binds the meta system to a given context. - * @param other A valid context to which to bind. - */ - static void bind(meta_ctx other) ENTT_NOEXCEPT { - internal::meta_context::global() = other.ctx; - } +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; -private: - internal::meta_type_node **ctx{&internal::meta_context::local()}; +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + /*! @brief Attorney idiom like model to access the base class. */ + friend struct internal::meta_context; }; +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +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) { + return ctx; +} + +/** + * Internal details not to be documented. + * @endcond + */ + } // namespace entt #endif @@ -43533,9 +58777,9 @@ private: #ifndef ENTT_META_FACTORY_HPP #define ENTT_META_FACTORY_HPP -#include #include #include +#include #include #include #include @@ -43547,6 +58791,10 @@ private: // #include "../core/type_traits.hpp" +// #include "../locator/locator.hpp" + +// #include "context.hpp" + // #include "meta.hpp" // #include "node.hpp" @@ -43560,7 +58808,7 @@ private: namespace entt { /*! @brief Empty class type used to request the _as ref_ policy. */ -struct as_ref_t { +struct as_ref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -43574,7 +58822,7 @@ struct as_ref_t { }; /*! @brief Empty class type used to request the _as cref_ policy. */ -struct as_cref_t { +struct as_cref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -43588,7 +58836,7 @@ struct as_cref_t { }; /*! @brief Empty class type used to request the _as-is_ policy. */ -struct as_is_t { +struct as_is_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -43602,7 +58850,7 @@ struct as_is_t { }; /*! @brief Empty class type used to request the _as void_ policy. */ -struct as_void_t { +struct as_void_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -43615,12 +58863,142 @@ struct as_void_t { */ }; +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::disjunction< + std::is_same, + std::is_same, + std::is_same, + std::is_same> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + } // namespace entt #endif // #include "range.hpp" +// #include "resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + auto &&context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return elem ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); +} + +} // namespace entt + +#endif + // #include "utility.hpp" #ifndef ENTT_META_UTILITY_HPP #define ENTT_META_UTILITY_HPP @@ -43629,10 +59007,10 @@ struct as_void_t { #include #include #include -// #include "../config/config.h" - // #include "../core/type_traits.hpp" +// #include "../locator/locator.hpp" + // #include "meta.hpp" // #include "node.hpp" @@ -43642,6 +59020,26 @@ struct as_void_t { namespace entt { +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + /*! @brief Primary template isn't defined on purpose. */ template struct meta_function_descriptor; @@ -43654,17 +59052,12 @@ struct meta_function_descriptor; * @tparam Args Function arguments. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = std::conditional_t, type_list, type_list>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = true; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; /** * @brief Meta function descriptor. @@ -43674,17 +59067,12 @@ struct meta_function_descriptor { * @tparam Args Function arguments. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = std::conditional_t, type_list, type_list>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = false; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; /** * @brief Meta function descriptor. @@ -43693,17 +59081,12 @@ struct meta_function_descriptor { * @tparam Ret Data member type. */ template -struct meta_function_descriptor { - /*! @brief Meta data return type. */ - using return_type = Ret &; - /*! @brief Meta data arguments. */ - using args_type = std::conditional_t, type_list<>, type_list>; - - /*! @brief True if the meta data is const, false otherwise. */ - static constexpr auto is_const = false; - /*! @brief True if the meta data is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; /** * @brief Meta function descriptor. @@ -43713,17 +59096,12 @@ struct meta_function_descriptor { * @tparam Args Other function arguments. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = std::conditional_t>, Type>, type_list, type_list>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = std::is_base_of_v>, Type> && std::is_const_v>; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v>, Type>; -}; +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>> {}; /** * @brief Meta function descriptor. @@ -43731,17 +59109,12 @@ struct meta_function_descriptor { * @tparam Ret Function return type. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = type_list<>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = false; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = true; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; /** * @brief Meta function helper. @@ -43782,6 +59155,33 @@ public: template using meta_function_helper_t = typename meta_function_helper::type; +/** + * @brief Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @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) { + if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + /** * @brief Wraps a value depending on the given policy. * @tparam Policy Optional policy (no policy set by default). @@ -43790,28 +59190,32 @@ using meta_function_helper_t = typename meta_function_helper::t * @return A meta any containing the returned value, if any. */ template -meta_any meta_dispatch([[maybe_unused]] Type &&value) { - if constexpr(std::is_same_v) { - return meta_any{std::in_place_type}; - } else if constexpr(std::is_same_v) { - return meta_any{std::in_place_type, std::forward(value)}; - } else if constexpr(std::is_same_v) { - static_assert(std::is_lvalue_reference_v, "Invalid type"); - return meta_any{std::in_place_type &>, std::as_const(value)}; - } else { - static_assert(std::is_same_v, "Policy not supported"); - return meta_any{std::forward(value)}; - } +std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template -[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT { - return internal::meta_arg_node(Type{}, index); +[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); } /** @@ -43857,6 +59261,49 @@ template return false; } +/** + * @brief Gets the value of a given variable. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(ctx, std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(ctx, std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, ctx}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, ctx}; + } else { + return meta_dispatch(ctx, *Data); + } + } else { + return meta_dispatch(ctx, Data); + } +} + /** * @brief Gets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. @@ -43866,32 +59313,8 @@ template * @return A meta any containing the value of the underlying variable. */ template -[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { - if constexpr(std::is_member_pointer_v || std::is_function_v>>) { - if constexpr(!std::is_array_v>>>) { - if constexpr(std::is_invocable_v) { - if(auto *clazz = instance->try_cast(); clazz) { - return meta_dispatch(std::invoke(Data, *clazz)); - } - } - - if constexpr(std::is_invocable_v) { - if(auto *fallback = instance->try_cast(); fallback) { - return meta_dispatch(std::invoke(Data, *fallback)); - } - } - } - - return meta_any{}; - } else if constexpr(std::is_pointer_v) { - if constexpr(std::is_array_v>) { - return meta_any{}; - } else { - return meta_dispatch(*Data); - } - } else { - return meta_dispatch(Data); - } +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + return meta_getter(locator::value_or(), std::move(instance)); } /** @@ -43901,44 +59324,44 @@ template namespace internal { -template -[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) { - if constexpr(std::is_same_v, void>) { - std::invoke(candidate, args...); - return meta_any{std::in_place_type}; +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_same_v(candidate), args...)), void>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; } else { - return meta_dispatch(std::invoke(candidate, args...)); + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); } } template -[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { +[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { using descriptor = meta_function_helper_t>; if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { - return meta_invoke_with_args(std::forward(candidate), *clazz, (args + Index)->cast>()...); + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { - return meta_invoke_with_args(std::forward(candidate), *clazz, (args + Index)->cast>()...); + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else { if(((args + Index)->allow_cast>() && ...)) { - return meta_invoke_with_args(std::forward(candidate), (args + Index)->cast>()...); + return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); } } - return meta_any{}; + return meta_any{meta_ctx_arg, ctx}; } template -[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence) { +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { if(((args + Index)->allow_cast() && ...)) { - return meta_any{std::in_place_type, (args + Index)->cast()...}; + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; } - return meta_any{}; + return meta_any{meta_ctx_arg, ctx}; } } // namespace internal @@ -43948,6 +59371,27 @@ template * @endcond */ +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + /** * @brief Tries to _invoke_ an object given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. @@ -43959,8 +59403,28 @@ template * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) { - return internal::meta_invoke(std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); } /** @@ -43973,8 +59437,26 @@ template * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) { - return internal::meta_invoke(std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); } /** @@ -43986,7 +59468,31 @@ template */ template [[nodiscard]] meta_any meta_construct(meta_any *const args) { - return internal::meta_construct(args, std::index_sequence_for{}); + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } } /** @@ -43994,17 +59500,32 @@ template * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. - * @param args Parameters to use to _invoke_ the object. * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) { - if constexpr(meta_function_helper_t::is_static) { - return internal::meta_invoke({}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); - } else { - return internal::meta_invoke(*args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); - } +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); } /** @@ -44016,8 +59537,8 @@ template * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_construct(meta_any *const args) { - return meta_construct(Candidate, args); +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); } } // namespace entt @@ -44028,210 +59549,126 @@ template namespace entt { /** - * @brief Meta factory to be used for reflection purposes. - * - * The meta factory is an utility class used to reflect types, data members and - * functions of all sorts. This class ensures that the underlying web of types - * is built correctly and performs some checks in debug mode to ensure that - * there are no subtle errors at runtime. + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. */ -template -class meta_factory; + +namespace internal { + +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; +} + +inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) { + if(auto it = parent.details->func.find(id); it != parent.details->func.end()) { + for(auto *curr = &it->second; curr; curr = curr->next.get()) { + if(curr->invoke == node.invoke) { + node.next = std::move(curr->next); + *curr = std::move(node); + return *curr; + } + } + + // locally overloaded function + node.next = std::make_shared(std::move(parent.details->func[id])); + } + + 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 /** - * @brief Extended meta factory to be used for reflection purposes. - * @tparam Type Reflected type for which the factory was created. - * @tparam Spec Property specialization pack used to disambiguate overloads. + * Internal details not to be documented. + * @endcond */ -template -class meta_factory: public meta_factory { - void link_prop_if_required(internal::meta_prop_node &node) ENTT_NOEXCEPT { - if(meta_range range{*ref}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { - ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [&node](const auto *curr) { return curr->id == node.id; }) == range.cend(), "Duplicate identifier"); - node.next = *ref; - *ref = &node; - } - } - - template - void unroll(choice_t<2>, std::tuple property, Other &&...other) ENTT_NOEXCEPT { - std::apply([this](auto &&...curr) { (this->unroll(choice<2>, std::forward(curr)...)); }, property); - unroll(choice<2>, std::forward(other)...); - } - - template - void unroll(choice_t<1>, std::pair property, Other &&...other) ENTT_NOEXCEPT { - assign(std::move(property.first), std::move(property.second)); - unroll(choice<2>, std::forward(other)...); - } - - template - void unroll(choice_t<0>, Property &&property, Other &&...other) ENTT_NOEXCEPT { - assign(std::forward(property)); - unroll(choice<2>, std::forward(other)...); - } - - template - void unroll(choice_t<0>) ENTT_NOEXCEPT {} - - template - void assign(meta_any key, meta_any value = {}) { - static meta_any property[2u]{}; - - static internal::meta_prop_node node{ - nullptr, - property[0u], - property[1u] - // tricks clang-format - }; - - property[0u] = std::move(key); - property[1u] = std::move(value); - - link_prop_if_required(node); - } - -public: - /** - * @brief Constructs an extended factory from a given node. - * @param target The underlying node to which to assign the properties. - */ - meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT - : ref{target} {} - - /** - * @brief Assigns a property to the last meta object created. - * - * Both the key and the value (if any) must be at least copy constructible. - * - * @tparam PropertyOrKey Type of the property or property key. - * @tparam Value Optional type of the property value. - * @param property_or_key Property or property key. - * @param value Optional property value. - * @return A meta factory for the parent type. - */ - template - meta_factory prop(PropertyOrKey &&property_or_key, Value &&...value) { - if constexpr(sizeof...(Value) == 0) { - unroll(choice<2>, std::forward(property_or_key)); - } else { - assign(std::forward(property_or_key), std::forward(value)...); - } - - return {}; - } - - /** - * @brief Assigns properties to the last meta object created. - * - * Both key and value (if any) must be at least copy constructible. - * - * @tparam Property Types of the properties. - * @param property Properties to assign to the last meta object created. - * @return A meta factory for the parent type. - */ - template - meta_factory props(Property... property) { - unroll(choice<2>, std::forward(property)...); - return {}; - } - -private: - internal::meta_prop_node **ref; -}; /** * @brief Basic meta factory to be used for reflection purposes. * @tparam Type Reflected type for which the factory was created. */ template -class meta_factory { - void link_base_if_required(internal::meta_base_node &node) ENTT_NOEXCEPT { - if(meta_range range{owner->base}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { - node.next = owner->base; - owner->base = &node; - } - } - - void link_conv_if_required(internal::meta_conv_node &node) ENTT_NOEXCEPT { - if(meta_range range{owner->conv}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { - node.next = owner->conv; - owner->conv = &node; - } - } - - void link_ctor_if_required(internal::meta_ctor_node &node) ENTT_NOEXCEPT { - if(meta_range range{owner->ctor}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { - node.next = owner->ctor; - owner->ctor = &node; - } - } - - void link_data_if_required(const id_type id, internal::meta_data_node &node) ENTT_NOEXCEPT { - meta_range range{owner->data}; - ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, &node](const auto *curr) { return curr != &node && curr->id == id; }) == range.cend(), "Duplicate identifier"); - node.id = id; - - if(std::find(range.cbegin(), range.cend(), &node) == range.cend()) { - node.next = owner->data; - owner->data = &node; - } - } - - void link_func_if_required(const id_type id, internal::meta_func_node &node) ENTT_NOEXCEPT { - node.id = id; - - if(meta_range range{owner->func}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { - node.next = owner->func; - owner->func = &node; - } - } - +class meta_factory { template - auto data(const id_type id, std::index_sequence) ENTT_NOEXCEPT { + void data(const id_type id, std::index_sequence) noexcept { using data_type = std::invoke_result_t; using args_type = type_list)>::args_type...>; static_assert(Policy::template value, "Invalid return type for the given policy"); - static internal::meta_data_node node{ - {}, - /* this is never static */ - (std::is_member_object_pointer_v)> && ... && std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none, - nullptr, - nullptr, - Setter::size, - internal::meta_node>>::resolve(), - &meta_arg::size != 1u, type_list_element_t>...>>, - [](meta_handle instance, meta_any value) -> bool { return (meta_setter>(*instance.operator->(), value.as_ref()) || ...); }, - &meta_getter - // tricks clang-format - }; + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + (std::is_member_object_pointer_v)> && ... && std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none, + Setter::size, + &internal::resolve>>, + &meta_arg::size != 1u, type_list_element_t>...>>, + +[](meta_handle instance, meta_any value) { return (meta_setter>(*instance.operator->(), value.as_ref()) || ...); }, + &meta_getter}); - link_data_if_required(id, node); - return meta_factory>{&node.prop}; + bucket = &elem.prop; } public: /*! @brief Default constructor. */ - meta_factory() ENTT_NOEXCEPT - : owner{internal::meta_node::resolve()} {} + meta_factory() noexcept + : meta_factory{locator::value_or()} {} /** - * @brief Makes a meta type _searchable_. - * @param id Optional unique identifier. - * @return An extended meta factory for the given type. + * @brief Context aware constructor. + * @param area The context into which to construct meta types. */ - auto type(const id_type id = type_hash::value()) ENTT_NOEXCEPT { - meta_range range{*internal::meta_context::global()}; - ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, this](const auto *curr) { return curr != owner && curr->id == id; }) == range.cend(), "Duplicate identifier"); - owner->id = id; + meta_factory(meta_ctx &area) noexcept + : ctx{&area}, + bucket{}, + info{&type_id()} { + auto &&elem = internal::owner(*ctx, *info); - if(std::find(range.cbegin(), range.cend(), owner) == range.cend()) { - owner->next = *internal::meta_context::global(); - *internal::meta_context::global() = owner; + if(!elem.details) { + elem.details = std::make_shared(); } - return meta_factory{&owner->prop}; + bucket = &elem.details->prop; + } + + /** + * @brief Assigns a custom unique identifier to a meta type. + * @param id A custom unique identifier. + * @return An extended meta factory for the given type. + */ + auto type(const id_type id) noexcept { + auto &&elem = internal::owner(*ctx, *info); + ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier"); + bucket = &elem.details->prop; + elem.id = id; + return *this; } /** @@ -44243,24 +59680,20 @@ public: * @return A meta factory for the parent type. */ template - auto base() ENTT_NOEXCEPT { + auto base() noexcept { static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); - static internal::meta_base_node node{ - nullptr, - internal::meta_node::resolve(), - [](meta_any other) ENTT_NOEXCEPT -> meta_any { - if(auto *ptr = other.data(); ptr) { - return forward_as_meta(*static_cast(static_cast(ptr))); - } + 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))); + }}); - return forward_as_meta(*static_cast(static_cast(std::as_const(other).data()))); - } - // tricks clang-format - }; - - link_base_if_required(node); - return meta_factory{}; + bucket = nullptr; + return *this; } /** @@ -44276,18 +59709,19 @@ public: * @return A meta factory for the parent type. */ template - auto conv() ENTT_NOEXCEPT { - static internal::meta_conv_node node{ - nullptr, - internal::meta_node>>>::resolve(), - [](const meta_any &instance) -> meta_any { - return forward_as_meta(std::invoke(Candidate, *static_cast(instance.data()))); - } - // tricks clang-format - }; + auto conv() noexcept { + using conv_type = std::remove_cv_t>>; - link_conv_if_required(node); - return meta_factory{}; + 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))); + }}); + + bucket = nullptr; + return *this; } /** @@ -44300,16 +59734,19 @@ public: * @return A meta factory for the parent type. */ template - auto conv() ENTT_NOEXCEPT { - static internal::meta_conv_node node{ - nullptr, - internal::meta_node>>::resolve(), - [](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast(*static_cast(instance.data()))); } - // tricks clang-format - }; + auto conv() noexcept { + using conv_type = std::remove_cv_t>; - link_conv_if_required(node); - return meta_factory{}; + 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))); + }}); + + bucket = nullptr; + return *this; } /** @@ -44326,21 +59763,21 @@ public: * @return An extended meta factory for the parent type. */ template - auto ctor() ENTT_NOEXCEPT { + auto ctor() noexcept { 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"); - static internal::meta_ctor_node node{ - nullptr, - descriptor::args_type::size, - &meta_arg, - &meta_construct - // tricks clang-format - }; + internal::meta_extend( + internal::owner(*ctx, *info), + type_id().hash(), + internal::meta_ctor_node{ + descriptor::args_type::size, + &meta_arg, + &meta_construct}); - link_ctor_if_required(node); - return meta_factory{}; + bucket = nullptr; + return *this; } /** @@ -44354,19 +59791,22 @@ public: * @return An extended meta factory for the parent type. */ template - auto ctor() ENTT_NOEXCEPT { - using descriptor = meta_function_helper_t; + auto ctor() noexcept { + // default constructor is already implicitly generated, no need for redundancy + if constexpr(sizeof...(Args) != 0u) { + using descriptor = meta_function_helper_t; - static internal::meta_ctor_node node{ - nullptr, - descriptor::args_type::size, - &meta_arg, - &meta_construct - // tricks clang-format - }; + internal::meta_extend( + internal::owner(*ctx, *info), + type_id().hash(), + internal::meta_ctor_node{ + descriptor::args_type::size, + &meta_arg, + &meta_construct}); + } - link_ctor_if_required(node); - return meta_factory{}; + bucket = nullptr; + return *this; } /** @@ -44388,10 +59828,16 @@ public: * @return A meta factory for the parent type. */ template - auto dtor() ENTT_NOEXCEPT { + auto dtor() noexcept { static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); - owner->dtor = [](void *instance) { std::invoke(Func, *static_cast(instance)); }; - return meta_factory{}; + + internal::meta_extend( + internal::owner(*ctx, *info), + internal::meta_dtor_node{ + +[](void *instance) { std::invoke(Func, *static_cast(instance)); }}); + + bucket = nullptr; + return *this; } /** @@ -44408,45 +59854,41 @@ public: * @return An extended meta factory for the parent type. */ template - auto data(const id_type id) ENTT_NOEXCEPT { + auto data(const id_type id) noexcept { if constexpr(std::is_member_object_pointer_v) { using data_type = std::remove_reference_t>; - static internal::meta_data_node node{ - {}, - /* this is never static */ - std::is_const_v ? internal::meta_traits::is_const : internal::meta_traits::is_none, - nullptr, - nullptr, - 1u, - internal::meta_node>::resolve(), - &meta_arg>>, - &meta_setter, - &meta_getter - // tricks clang-format - }; + 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, + 1u, + &internal::resolve>, + &meta_arg>>, + &meta_setter, + &meta_getter}); - link_data_if_required(id, node); - return meta_factory, std::integral_constant>{&node.prop}; + bucket = &elem.prop; } else { using data_type = std::remove_reference_t>; - static internal::meta_data_node node{ - {}, - ((std::is_same_v> || std::is_const_v) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, - nullptr, - nullptr, - 1u, - internal::meta_node>::resolve(), - &meta_arg>>, - &meta_setter, - &meta_getter - // tricks clang-format - }; + 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, + 1u, + &internal::resolve>, + &meta_arg>>, + &meta_setter, + &meta_getter}); - link_data_if_required(id, node); - return meta_factory>{&node.prop}; + bucket = &elem.prop; } + + return *this; } /** @@ -44470,47 +59912,43 @@ public: * @return An extended meta factory for the parent type. */ template - auto data(const id_type id) ENTT_NOEXCEPT { + auto data(const id_type id) noexcept { using data_type = std::invoke_result_t; static_assert(Policy::template value, "Invalid return type for the given policy"); if constexpr(std::is_same_v) { - static internal::meta_data_node node{ - {}, - /* this is never static */ - internal::meta_traits::is_const, - nullptr, - nullptr, - 0u, - internal::meta_node>>::resolve(), - &meta_arg>, - &meta_setter, - &meta_getter - // tricks clang-format - }; + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + internal::meta_traits::is_const, + 0u, + &internal::resolve>>, + &meta_arg>, + &meta_setter, + &meta_getter}); - link_data_if_required(id, node); - return meta_factory, std::integral_constant>{&node.prop}; + bucket = &elem.prop; } else { using args_type = typename meta_function_helper_t::args_type; - static internal::meta_data_node node{ - {}, - /* this is never static nor const */ - internal::meta_traits::is_none, - nullptr, - nullptr, - 1u, - internal::meta_node>>::resolve(), - &meta_arg>>, - &meta_setter, - &meta_getter - // tricks clang-format - }; + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static nor const */ + internal::meta_traits::is_none, + 1u, + &internal::resolve>>, + &meta_arg>>, + &meta_setter, + &meta_getter}); - link_data_if_required(id, node); - return meta_factory, std::integral_constant>{&node.prop}; + bucket = &elem.prop; } + + return *this; } /** @@ -44531,8 +59969,9 @@ public: * @return An extended meta factory for the parent type. */ template - auto data(const id_type id) ENTT_NOEXCEPT { - return data(id, std::make_index_sequence{}); + auto data(const id_type id) noexcept { + data(id, std::make_index_sequence{}); + return *this; } /** @@ -44549,30 +59988,82 @@ public: * @return An extended meta factory for the parent type. */ template - auto func(const id_type id) ENTT_NOEXCEPT { + auto func(const id_type id) noexcept { using descriptor = meta_function_helper_t; static_assert(Policy::template value, "Invalid return type for the given policy"); - static internal::meta_func_node node{ - {}, - (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), - nullptr, - nullptr, - descriptor::args_type::size, - internal::meta_node, void, std::remove_cv_t>>>::resolve(), - &meta_arg, - &meta_invoke - // tricks clang-format - }; + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_func_node{ + (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), + descriptor::args_type::size, + &internal::resolve, void, std::remove_cv_t>>>, + &meta_arg, + &meta_invoke}); - link_func_if_required(id, node); - return meta_factory>{&node.prop}; + bucket = &elem.prop; + return *this; + } + + /** + * @brief Assigns a property to the last meta object created. + * + * Both the key and the value (if any) must be at least copy constructible. + * + * @tparam Value Optional type of the property value. + * @param id Property key. + * @param value Optional property value. + * @return An extended meta factory for the given type. + */ + template + meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) { + 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}); + } else { + internal::meta_extend( + *bucket, + id, + internal::meta_prop_node{ + &internal::resolve>..., + std::make_shared>(std::forward(value))...}); + } + + return *this; } private: - internal::meta_type_node *owner; + meta_ctx *ctx; + dense_map *bucket; + const type_info *info; }; +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.
+ * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @param ctx The context into which to construct meta types. + * @return A meta factory for the given type. + */ +template +[[nodiscard]] auto meta(meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + // make sure the type exists in the context before returning a factory + context.value.try_emplace(type_id().hash(), internal::resolve(context)); + return meta_factory{ctx}; +} + /** * @brief Utility function to use for reflection. * @@ -44585,10 +60076,8 @@ private: * @return A meta factory for the given type. */ template -[[nodiscard]] auto meta() ENTT_NOEXCEPT { - auto *const node = internal::meta_node::resolve(); - // extended meta factory to allow assigning properties to opaque meta types - return meta_factory{&node->prop}; +[[nodiscard]] auto meta() noexcept { + return meta(locator::value_or()); } /** @@ -44598,36 +60087,49 @@ template * well as its constructors, destructors and conversion functions if any.
* Base classes aren't reset but the link between the two types is removed. * - * The type is also removed from the list of searchable types. + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept { + auto &&context = internal::meta_context::from(ctx); + + for(auto it = context.value.begin(); it != context.value.end();) { + if(it->second.id == id) { + it = context.value.erase(it); + } else { + ++it; + } + } +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.
+ * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the set of searchable types. * * @param id Unique identifier. */ -inline void meta_reset(const id_type id) ENTT_NOEXCEPT { - auto clear_chain = [](auto **curr, auto... member) { - for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) { - if constexpr(sizeof...(member) != 0u) { - static_assert(sizeof...(member) == 1u, "Assert in defense of the future me"); - for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {} - } - } - }; +inline void meta_reset(const id_type id) noexcept { + meta_reset(locator::value_or(), id); +} - for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) { - if(auto *node = *it; node->id == id) { - clear_chain(&node->prop); - clear_chain(&node->base); - clear_chain(&node->conv); - clear_chain(&node->ctor); - clear_chain(&node->data, &internal::meta_data_node::prop); - clear_chain(&node->func, &internal::meta_func_node::prop); - - node->id = {}; - node->dtor = nullptr; - *it = std::exchange(node->next, nullptr); - - break; - } - } +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + * @param ctx The context from which to reset meta types. + */ +template +void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.erase(type_id().hash()); } /** @@ -44638,19 +60140,28 @@ inline void meta_reset(const id_type id) ENTT_NOEXCEPT { * @tparam Type Type to reset. */ template -void meta_reset() ENTT_NOEXCEPT { - meta_reset(internal::meta_node::resolve()->id); +void meta_reset() noexcept { + meta_reset(locator::value_or()); } /** - * @brief Resets all searchable types. + * @brief Resets all meta types. + * + * @sa meta_reset + * + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.clear(); +} + +/** + * @brief Resets all meta types. * * @sa meta_reset */ -inline void meta_reset() ENTT_NOEXCEPT { - while(*internal::meta_context::global()) { - meta_reset((*internal::meta_context::global())->id); - } +inline void meta_reset() noexcept { + meta_reset(locator::value_or()); } } // namespace entt @@ -44680,9 +60191,11 @@ inline void meta_reset() ENTT_NOEXCEPT { // #include "../core/utility.hpp" +// #include "../locator/locator.hpp" + // #include "adl_pointer.hpp" -// #include "ctx.hpp" +// #include "context.hpp" // #include "fwd.hpp" @@ -44708,26 +60221,30 @@ public: /*! @brief Meta iterator type. */ using iterator = meta_iterator; - /*! @brief Default constructor. */ - meta_sequence_container() ENTT_NOEXCEPT = default; + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_sequence_container(const meta_ctx &area = locator::value_or()) noexcept + : ctx{&area} {} /** - * @brief Construct a proxy object for sequence containers. + * @brief Rebinds a proxy object to a sequence container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template - meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT - : value_type_node{internal::meta_node>>::resolve()}, - size_fn{&meta_sequence_container_traits::size}, - resize_fn{&meta_sequence_container_traits::resize}, - iter_fn{&meta_sequence_container_traits::iter}, - insert_fn{&meta_sequence_container_traits::insert}, - erase_fn{&meta_sequence_container_traits::erase}, - storage{std::move(instance)} {} + void rebind(any instance) noexcept { + value_type_node = &internal::resolve; + size_fn = &meta_sequence_container_traits::size; + resize_fn = &meta_sequence_container_traits::resize; + iter_fn = &meta_sequence_container_traits::iter; + insert_or_erase_fn = &meta_sequence_container_traits::insert_or_erase; + storage = std::move(instance); + } - [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; inline bool resize(const size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); @@ -44735,15 +60252,15 @@ public: inline iterator insert(iterator, meta_any); inline iterator erase(iterator); [[nodiscard]] inline meta_any operator[](const size_type); - [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + [[nodiscard]] inline explicit operator bool() const noexcept; private: - internal::meta_type_node *value_type_node = nullptr; - size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; - bool (*resize_fn)(any &, size_type) = nullptr; - iterator (*iter_fn)(any &, const bool) = nullptr; - iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr; - iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr; + const meta_ctx *ctx{}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const any &) noexcept {}; + bool (*resize_fn)(any &, size_type){}; + iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; + iterator (*insert_or_erase_fn)(const meta_ctx &, any &, const any &, meta_any &){}; any storage{}; }; @@ -44757,56 +60274,60 @@ public: /*! @brief Meta iterator type. */ using iterator = meta_iterator; - /*! @brief Default constructor. */ - meta_associative_container() ENTT_NOEXCEPT = default; + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_associative_container(const meta_ctx &area = locator::value_or()) noexcept + : ctx{&area} {} /** - * @brief Construct a proxy object for associative containers. + * @brief Rebinds a proxy object to an associative container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template - meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT - : key_only_container{meta_associative_container_traits::key_only}, - key_type_node{internal::meta_node>>::resolve()}, - mapped_type_node{nullptr}, - value_type_node{internal::meta_node>>::resolve()}, - size_fn{&meta_associative_container_traits::size}, - clear_fn{&meta_associative_container_traits::clear}, - iter_fn{&meta_associative_container_traits::iter}, - insert_fn{&meta_associative_container_traits::insert}, - erase_fn{&meta_associative_container_traits::erase}, - find_fn{&meta_associative_container_traits::find}, - storage{std::move(instance)} { + void rebind(any instance) noexcept { if constexpr(!meta_associative_container_traits::key_only) { - mapped_type_node = internal::meta_node>>::resolve(); + mapped_type_node = &internal::resolve; } + + key_only_container = meta_associative_container_traits::key_only; + key_type_node = &internal::resolve; + value_type_node = &internal::resolve; + size_fn = &meta_associative_container_traits::size; + clear_fn = &meta_associative_container_traits::clear; + iter_fn = &meta_associative_container_traits::iter; + insert_or_erase_fn = &meta_associative_container_traits::insert_or_erase; + find_fn = &meta_associative_container_traits::find; + storage = std::move(instance); } - [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; - [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; - [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + [[nodiscard]] inline bool key_only() const noexcept; + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); + inline bool insert(meta_any); inline bool insert(meta_any, meta_any); - inline bool erase(meta_any); + inline size_type erase(meta_any); [[nodiscard]] inline iterator find(meta_any); - [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + [[nodiscard]] inline explicit operator bool() const noexcept; private: + const meta_ctx *ctx{}; bool key_only_container{}; - internal::meta_type_node *key_type_node = nullptr; - internal::meta_type_node *mapped_type_node = nullptr; - internal::meta_type_node *value_type_node = nullptr; - size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; - bool (*clear_fn)(any &) = nullptr; - iterator (*iter_fn)(any &, const bool) = nullptr; - bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr; - bool (*erase_fn)(any &, meta_any &) = nullptr; - iterator (*find_fn)(any &, meta_any &) = nullptr; + internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const any &) noexcept {}; + bool (*clear_fn)(any &){}; + iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; + size_type (*insert_or_erase_fn)(any &, meta_any &, meta_any &){}; + iterator (*find_fn)(const meta_ctx &, any &, meta_any &){}; any storage{}; }; @@ -44828,8 +60349,8 @@ class meta_any { switch(op) { case operation::deref: if constexpr(is_meta_pointer_like_v) { - if constexpr(std::is_function_v::element_type>>) { - *static_cast(other) = any_cast(value); + if constexpr(std::is_function_v::element_type>) { + static_cast(other)->emplace(any_cast(value)); } else if constexpr(!std::is_same_v::element_type>, void>) { using in_place_type = decltype(adl_meta_pointer_like::dereference(any_cast(value))); @@ -44845,12 +60366,12 @@ class meta_any { break; case operation::seq: if constexpr(is_complete_v>) { - *static_cast(other) = {std::in_place_type, std::move(const_cast(value))}; + static_cast(other)->rebind(std::move(const_cast(value))); } break; case operation::assoc: if constexpr(is_complete_v>) { - *static_cast(other) = {std::in_place_type, std::move(const_cast(value))}; + static_cast(other)->rebind(std::move(const_cast(value))); } break; } @@ -44858,20 +60379,29 @@ class meta_any { } void release() { - if(node && node->dtor && storage.owner()) { - node->dtor(storage.data()); + if(node.dtor.dtor && owner()) { + node.dtor.dtor(storage.data()); } } - meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT + meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept : storage{std::move(ref)}, - node{storage ? other.node : nullptr}, + ctx{&area}, + node{storage ? other.node : internal::meta_type_node{}}, vtable{storage ? other.vtable : &basic_vtable} {} public: - /*! @brief Default constructor. */ - meta_any() ENTT_NOEXCEPT + /*! Default constructor. */ + meta_any() noexcept + : meta_any{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept : storage{}, + ctx{&area}, node{}, vtable{&basic_vtable} {} @@ -44883,8 +60413,20 @@ public: */ template explicit meta_any(std::in_place_type_t, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) : storage{std::in_place_type, std::forward(args)...}, - node{internal::meta_node>>::resolve()}, + ctx{&area}, + node{internal::resolve>>(internal::meta_context::from(*ctx))}, vtable{&basic_vtable>>} {} /** @@ -44894,7 +60436,39 @@ public: */ template, meta_any>>> meta_any(Type &&value) - : meta_any{std::in_place_type>>, std::forward(value)} {} + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : meta_any{other} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : meta_any{std::move(other)} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } /** * @brief Copy constructor. @@ -44906,9 +60480,10 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - meta_any(meta_any &&other) ENTT_NOEXCEPT + meta_any(meta_any &&other) noexcept : storage{std::move(other.storage)}, - node{std::exchange(other.node, nullptr)}, + ctx{other.ctx}, + node{std::exchange(other.node, internal::meta_type_node{})}, vtable{std::exchange(other.vtable, &basic_vtable)} {} /*! @brief Frees the internal storage, whatever it means. */ @@ -44923,9 +60498,10 @@ public: */ meta_any &operator=(const meta_any &other) { release(); - vtable = other.vtable; storage = other.storage; + ctx = other.ctx; node = other.node; + vtable = other.vtable; return *this; } @@ -44934,11 +60510,12 @@ public: * @param other The instance to move from. * @return This meta any object. */ - meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT { + meta_any &operator=(meta_any &&other) noexcept { release(); - vtable = std::exchange(other.vtable, &basic_vtable); storage = std::move(other.storage); - node = std::exchange(other.node, nullptr); + ctx = other.ctx; + node = std::exchange(other.node, internal::meta_type_node{}); + vtable = std::exchange(other.vtable, &basic_vtable); return *this; } @@ -44956,23 +60533,20 @@ public: } /*! @copydoc any::type */ - [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type type() const noexcept; /*! @copydoc any::data */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return storage.data(); } /*! @copydoc any::data */ - [[nodiscard]] void *data() ENTT_NOEXCEPT { + [[nodiscard]] void *data() noexcept { return storage.data(); } /** * @brief Invokes the underlying function, if possible. - * - * @sa meta_func::invoke - * * @tparam Args Types of arguments to use to invoke the function. * @param id Unique identifier. * @param args Parameters to use to invoke the function. @@ -44987,10 +60561,6 @@ public: /** * @brief Sets the value of a given variable. - * - * The type of the value is such that a cast or conversion to the type of - * the variable is possible. Otherwise, invoking the setter does nothing. - * * @tparam Type Type of value to assign. * @param id Unique identifier. * @param value Parameter to use to set the underlying variable. @@ -45016,42 +60586,24 @@ public: */ template [[nodiscard]] const Type *try_cast() const { - if(const auto &info = type_id(); node && *node->info == info) { - return any_cast(&storage); - } else if(node) { - for(auto *it = node->base; it; it = it->next) { - const auto as_const = it->cast(as_ref()); - - if(const Type *base = as_const.template try_cast(); base) { - return base; - } - } - } - - return nullptr; + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); } /*! @copydoc try_cast */ template [[nodiscard]] Type *try_cast() { - if(const auto &info = type_id(); node && *node->info == info) { - return any_cast(&storage); - } else if(node) { - for(auto *it = node->base; it; it = it->next) { - if(Type *base = it->cast(as_ref()).template try_cast(); base) { - return base; - } - } + if constexpr(std::is_const_v) { + return std::as_const(*this).try_cast>(); + } else { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); } - - return nullptr; } /** * @brief Tries to cast an instance to a given type. * - * The type of the instance must be such that the cast is possible. - * * @warning * Attempting to perform an invalid cast results is undefined behavior. * @@ -45089,7 +60641,7 @@ public: */ [[nodiscard]] bool allow_cast(const meta_type &type) { if(auto other = std::as_const(*this).allow_cast(type); other) { - if(other.storage.owner()) { + if(other.owner()) { std::swap(*this, other); } @@ -45107,12 +60659,11 @@ public: */ template [[nodiscard]] meta_any allow_cast() const { - const auto other = allow_cast(internal::meta_node>>::resolve()); - if constexpr(std::is_reference_v && !std::is_const_v>) { - return other.storage.owner() ? other : meta_any{}; + return meta_any{meta_ctx_arg, *ctx}; } else { - return other; + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}); } } @@ -45123,25 +60674,17 @@ public: */ template bool allow_cast() { - if(auto other = std::as_const(*this).allow_cast(internal::meta_node>>::resolve()); other) { - if(other.storage.owner()) { - std::swap(*this, other); - return true; - } - - return (static_cast> &>(storage).data() != nullptr); - } - - return false; + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); } /*! @copydoc any::emplace */ template void emplace(Args &&...args) { release(); - vtable = &basic_vtable>>; storage.emplace(std::forward(args)...); - node = internal::meta_node>>::resolve(); + node = internal::resolve>>(internal::meta_context::from(*ctx)); + vtable = &basic_vtable>>; } /*! @copydoc any::assign */ @@ -45153,26 +60696,26 @@ public: /*! @copydoc any::reset */ void reset() { release(); - vtable = &basic_vtable; storage.reset(); - node = nullptr; + node = {}; + vtable = &basic_vtable; } /** * @brief Returns a sequence container proxy. * @return A sequence container proxy for the underlying object. */ - [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { any detached = storage.as_ref(); - meta_sequence_container proxy; + meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } /*! @copydoc as_sequence_container */ - [[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { any detached = storage.as_ref(); - meta_sequence_container proxy; + meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } @@ -45181,17 +60724,17 @@ public: * @brief Returns an associative container proxy. * @return An associative container proxy for the underlying object. */ - [[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT { + [[nodiscard]] meta_associative_container as_associative_container() noexcept { any detached = storage.as_ref(); - meta_associative_container proxy; + meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } /*! @copydoc as_associative_container */ - [[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { any detached = storage.as_ref(); - meta_associative_container proxy; + meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } @@ -45201,8 +60744,8 @@ public: * @return A wrapper that shares a reference to an unmanaged object if the * wrapped element is dereferenceable, an invalid meta any otherwise. */ - [[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT { - meta_any ret{}; + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; vtable(operation::deref, storage, &ret); return ret; } @@ -45211,56 +60754,52 @@ public: * @brief Returns false if a wrapper is invalid, true otherwise. * @return False if the wrapper is invalid, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return !(node.info == nullptr); } /*! @copydoc any::operator== */ - [[nodiscard]] bool operator==(const meta_any &other) const { - return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage); + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); } /*! @copydoc any::as_ref */ - [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { - return meta_any{*this, storage.as_ref()}; + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::as_ref */ - [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { - return meta_any{*this, storage.as_ref()}; + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::owner */ - [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + [[nodiscard]] bool owner() const noexcept { return storage.owner(); } private: any storage; - internal::meta_type_node *node; + const meta_ctx *ctx; + internal::meta_type_node node; vtable_type *vtable; }; /** - * @brief Checks if two wrappers differ in their content. - * @param lhs A wrapper, either empty or not. - * @param rhs A wrapper, either empty or not. - * @return True if the two wrappers differ in their content, false otherwise. + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. */ -[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - -/** - * @brief Constructs a wrapper from a given type, passing it all arguments. - * @tparam Type Type of object to use to initialize the wrapper. - * @tparam Args Types of arguments to use to construct the new instance. - * @param args Parameters to use to construct the instance. - * @return A properly initialized wrapper for an object of the given type. - */ -template -meta_any make_meta(Args &&...args) { - return meta_any{std::in_place_type, std::forward(args)...}; +template +meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; } /** @@ -45271,7 +60810,7 @@ meta_any make_meta(Args &&...args) { */ template meta_any forward_as_meta(Type &&value) { - return meta_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; + return forward_as_meta(locator::value_or(), std::forward(value)); } /** @@ -45282,8 +60821,65 @@ meta_any forward_as_meta(Type &&value) { * Handles are used to generate references to actual objects when needed. */ struct meta_handle { - /*! @brief Default constructor. */ - meta_handle() = default; + /*! Default constructor. */ + meta_handle() noexcept + : meta_handle{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept + : any{meta_ctx_arg, area} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) noexcept + : any{ctx, std::in_place_type, value} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) noexcept + : meta_handle{locator::value_or(), value} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_handle(const meta_ctx &area, const meta_handle &other) + : any{area, other.any} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} /*! @brief Default copy constructor, deleted on purpose. */ meta_handle(const meta_handle &) = delete; @@ -45303,26 +60899,11 @@ struct meta_handle { */ meta_handle &operator=(meta_handle &&) = default; - /** - * @brief Creates a handle that points to an unmanaged object. - * @tparam Type Type of object to use to initialize the handle. - * @param value An instance of an object to use to initialize the handle. - */ - template, meta_handle>>> - meta_handle(Type &value) ENTT_NOEXCEPT - : meta_handle{} { - if constexpr(std::is_same_v, meta_any>) { - any = value.as_ref(); - } else { - any.emplace(value); - } - } - /** * @brief Returns false if a handle is invalid, true otherwise. * @return False if the handle is invalid, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(any); } @@ -45345,65 +60926,65 @@ private: /*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { - /*! @brief Node type. */ - using node_type = internal::meta_prop_node; + /*! @brief Default constructor. */ + meta_prop() noexcept + : node{}, + ctx{} {} /** - * @brief Constructs an instance from a given node. + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ - meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} - - /** - * @brief Returns the stored key as a const reference. - * @return A wrapper containing the key stored with the property. - */ - [[nodiscard]] meta_any key() const { - return node->id.as_ref(); - } + meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept + : node{&curr}, + ctx{&area} {} /** * @brief Returns the stored value by copy. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { - return node->value; + 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 true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); } private: - const node_type *node; + const internal::meta_prop_node *node; + const meta_ctx *ctx; }; /*! @brief Opaque wrapper for data members. */ struct meta_data { - /*! @brief Node type. */ - using node_type = internal::meta_data_node; /*! @brief Unsigned integer type. */ - using size_type = typename node_type::size_type; + using size_type = typename internal::meta_data_node::size_type; - /*! @copydoc meta_prop::meta_prop */ - meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} + /*! @brief Default constructor. */ + meta_data() noexcept + : node{}, + ctx{} {} - /*! @copydoc meta_type::id */ - [[nodiscard]] id_type id() const ENTT_NOEXCEPT { - return node->id; - } + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} /** * @brief Returns the number of setters available. * @return The number of setters available. */ - [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + [[nodiscard]] size_type arity() const noexcept { return node->arity; } @@ -45411,29 +60992,23 @@ struct meta_data { * @brief Indicates whether a data member is constant or not. * @return True if the data member is constant, false otherwise. */ - [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_const); + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a data member is static or not. * @return True if the data member is static, false otherwise. */ - [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_static); + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); } /*! @copydoc meta_any::type */ - [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type type() const noexcept; /** * @brief Sets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member.
- * The type of the value is such that a cast or conversion to the type of - * the variable is possible. Otherwise, invoking the setter does nothing. - * * @tparam Type Type of value to assign. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. @@ -45441,20 +61016,16 @@ struct meta_data { */ template bool set(meta_handle instance, Type &&value) const { - return node->set && node->set(std::move(instance), std::forward(value)); + return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); } /** * @brief Gets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member. - * * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { - return node->get(std::move(instance)); + return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); } /** @@ -45462,64 +61033,63 @@ struct meta_data { * @param index Index of the setter of which to return the accepted type. * @return The type accepted by the i-th setter. */ - [[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Returns a range to visit registered meta properties. * @return An iterable range to visit registered meta properties. */ - [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { - return node->prop; + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** - * @brief Lookup function for registered meta properties. + * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ - [[nodiscard]] meta_prop prop(meta_any key) const { - for(auto curr: prop()) { - if(curr.key() == key) { - return curr; - } - } - - return nullptr; + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); } private: - const node_type *node; + const internal::meta_data_node *node; + const meta_ctx *ctx; }; /*! @brief Opaque wrapper for member functions. */ struct meta_func { - /*! @brief Node type. */ - using node_type = internal::meta_func_node; /*! @brief Unsigned integer type. */ - using size_type = typename node_type::size_type; + using size_type = typename internal::meta_func_node::size_type; - /*! @copydoc meta_prop::meta_prop */ - meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} + /*! @brief Default constructor. */ + meta_func() noexcept + : node{}, + ctx{} {} - /*! @copydoc meta_type::id */ - [[nodiscard]] id_type id() const ENTT_NOEXCEPT { - return node->id; - } + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} /** * @brief Returns the number of arguments accepted by a member function. * @return The number of arguments accepted by the member function. */ - [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + [[nodiscard]] size_type arity() const noexcept { return node->arity; } @@ -45527,39 +61097,37 @@ struct meta_func { * @brief Indicates whether a member function is constant or not. * @return True if the member function is constant, false otherwise. */ - [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_const); + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a member function is static or not. * @return True if the member function is static, false otherwise. */ - [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_static); + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); } /** * @brief Returns the return type of a member function. * @return The return type of the member function. */ - [[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type ret() const noexcept; /** * @brief Returns the type of the i-th argument of a member function. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a member function. */ - [[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Invokes the underlying function, if possible. * - * To invoke a member function, the parameters must be such that a cast or - * conversion to the required types is possible. Otherwise, an empty and - * thus invalid wrapper is returned.
- * It must be possible to cast the instance to the parent type of the member - * function. + * @warning + * The context of the arguments is **not** changed.
+ * It's up to the caller to bind them to the right context(s). * * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. @@ -45567,139 +61135,159 @@ struct meta_func { * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { - return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; + return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke - * - * @sa invoke - * * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. - * @return A wrapper containing the new instance, if any. + * @return A wrapper containing the returned value, if any. */ template meta_any invoke(meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; - return invoke(std::move(instance), arguments, sizeof...(Args)); + meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; + return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /*! @copydoc meta_data::prop */ - [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { - return node->prop; + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** - * @brief Lookup function for registered meta properties. + * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ - [[nodiscard]] meta_prop prop(meta_any key) const { - for(auto curr: prop()) { - if(curr.key() == key) { - return curr; - } - } + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } - return nullptr; + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return node->next ? meta_func{*ctx, *node->next} : meta_func{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); } private: - const node_type *node; + const internal::meta_func_node *node; + const meta_ctx *ctx; }; /*! @brief Opaque wrapper for types. */ class meta_type { - template - [[nodiscard]] std::decay_t().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const { - std::decay_t*Member)> candidate{}; - size_type extent{sz + 1u}; + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; bool ambiguous{}; - for(auto *curr = (node->*Member); curr; curr = curr->next) { - if(pred(curr) && curr->arity == sz) { - size_type direct{}; - size_type ext{}; + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } - for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) { - const auto type = args[next].type(); - const auto other = curr->arg(next); + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); if(const auto &info = other.info(); info == type.info()) { - ++direct; - } else { - ext += internal::find_by<&node_type::base>(info, type.node) - || internal::find_by<&node_type::conv>(info, type.node) - || (type.node->conversion_helper && other.node->conversion_helper); + ++match; + } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { + break; } } - if((direct + ext) == sz) { - if(ext < extent) { + if(pos == sz) { + if(!candidate || match > same) { candidate = curr; - extent = ext; + same = match; ambiguous = false; - } else if(ext == extent) { + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { + candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + ambiguous = true; } } } } - return (candidate && !ambiguous) ? candidate : decltype(candidate){}; + return ambiguous ? nullptr : candidate; } public: - /*! @brief Node type. */ - using node_type = internal::meta_type_node; - /*! @brief Node type. */ - using base_node_type = internal::meta_base_node; /*! @brief Unsigned integer type. */ - using size_type = typename node_type::size_type; + using size_type = typename internal::meta_type_node::size_type; - /*! @copydoc meta_prop::meta_prop */ - meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT - : node{curr} {} + /*! @brief Default constructor. */ + meta_type() noexcept + : node{}, + ctx{} {} /** - * @brief Constructs an instance from a given base node. - * @param curr The base node with which to construct the instance. + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. */ - meta_type(const base_node_type *curr) ENTT_NOEXCEPT - : node{curr ? curr->type : nullptr} {} + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.type(internal::meta_context::from(area))} {} /** * @brief Returns the type info object of the underlying type. * @return The type info object of the underlying type. */ - [[nodiscard]] const type_info &info() const ENTT_NOEXCEPT { - return *node->info; + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; } /** * @brief Returns the identifier assigned to a type. * @return The identifier assigned to the type. */ - [[nodiscard]] id_type id() const ENTT_NOEXCEPT { - return node->id; + [[nodiscard]] id_type id() const noexcept { + return node.id; } /** * @brief Returns the size of the underlying type if known. * @return The size of the underlying type if known, 0 otherwise. */ - [[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { - return node->size_of; + [[nodiscard]] size_type size_of() const noexcept { + return node.size_of; } /** @@ -45707,40 +61295,65 @@ public: * @return True if the underlying type is an arithmetic type, false * otherwise. */ - [[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_arithmetic); + [[nodiscard]] bool is_arithmetic() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_signed); } /** * @brief Checks whether a type refers to an array type or not. * @return True if the underlying type is an array type, false otherwise. */ - [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_array); + [[nodiscard]] bool is_array() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_array); } /** * @brief Checks whether a type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. */ - [[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_enum); + [[nodiscard]] bool is_enum() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_enum); } /** * @brief Checks whether a type refers to a class or not. * @return True if the underlying type is a class, false otherwise. */ - [[nodiscard]] bool is_class() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_class); + [[nodiscard]] bool is_class() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_class); } /** * @brief Checks whether a type refers to a pointer or not. * @return True if the underlying type is a pointer, false otherwise. */ - [[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_pointer); + [[nodiscard]] bool is_pointer() const noexcept { + return node.info && (node.info->hash() != remove_pointer().info().hash()); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const noexcept { + return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; } /** @@ -45748,24 +61361,24 @@ public: * @return True if the underlying type is a pointer-like one, false * otherwise. */ - [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_meta_pointer_like); + [[nodiscard]] bool is_pointer_like() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); } /** * @brief Checks whether a type refers to a sequence container or not. * @return True if the type is a sequence container, false otherwise. */ - [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_meta_sequence_container); + [[nodiscard]] bool is_sequence_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); } /** * @brief Checks whether a type refers to an associative container or not. * @return True if the type is an associative container, false otherwise. */ - [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { - return !!(node->traits & internal::meta_traits::is_meta_associative_container); + [[nodiscard]] bool is_associative_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); } /** @@ -45774,27 +61387,24 @@ public: * @return True if the type is a recognized class template specialization, * false otherwise. */ - [[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT { - return (node->templ != nullptr); + [[nodiscard]] bool is_template_specialization() const noexcept { + return (node.templ.arity != 0u); } /** * @brief Returns the number of template arguments. * @return The number of template arguments. */ - [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { - return node->templ ? node->templ->arity : size_type{}; + [[nodiscard]] size_type template_arity() const noexcept { + return node.templ.arity; } /** * @brief Returns a tag for the class template of the underlying type. - * - * @sa meta_class_template_tag - * * @return The tag for the class template of the underlying type. */ - [[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { - return node->templ ? node->templ->type : meta_type{}; + [[nodiscard]] inline meta_type template_type() const noexcept { + return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; } /** @@ -45802,107 +61412,142 @@ public: * @param index Index of the template argument of which to return the type. * @return The type of the i-th template argument of a type. */ - [[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT { - return index < template_arity() ? node->templ->arg(index) : meta_type{}; + [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; } /** * @brief Returns a range to visit registered top-level base meta types. * @return An iterable range to visit registered top-level base meta types. */ - [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { - return node->base; - } - - /** - * @brief Lookup function for registered base meta types. - * @param id Unique identifier. - * @return The registered base meta type for the given identifier, if any. - */ - [[nodiscard]] meta_type base(const id_type id) const { - return internal::find_by<&node_type::base>(id, node); + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; } /** * @brief Returns a range to visit registered top-level meta data. * @return An iterable range to visit registered top-level meta data. */ - [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { - return node->data; + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; } /** - * @brief Lookup function for registered meta data. - * - * Registered meta data of base classes will also be visited. - * + * @brief Lookup utility for meta data (bases are also visited). * @param id Unique identifier. * @return The registered meta data for the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { - return internal::find_by<&node_type::data>(id, node); + if(node.details) { + if(const auto it = node.details->data.find(id); it != node.details->data.cend()) { + return meta_data{*ctx, it->second}; + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.data(id); elem) { + return elem; + } + } + + return meta_data{}; } /** * @brief Returns a range to visit registered top-level functions. * @return An iterable range to visit registered top-level functions. */ - [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { - return node->func; + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; } /** - * @brief Lookup function for registered meta functions. + * @brief Lookup utility for meta functions (bases are also visited). * - * Registered meta functions of base classes will also be visited.
* In case of overloaded functions, the first one with the required - * identifier will be returned. + * identifier is returned. * * @param id Unique identifier. * @return The registered meta function for the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { - return internal::find_by<&node_type::func>(id, node); + if(node.details) { + if(const auto it = node.details->func.find(id); it != node.details->func.cend()) { + return meta_func{*ctx, it->second}; + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.func(id); elem) { + return elem; + } + } + + return meta_func{}; } /** * @brief Creates an instance of the underlying type, if possible. * - * Parameters are such that a cast or conversion to the required types is - * possible. Otherwise, an empty and thus invalid wrapper is returned.
* If suitable, the implicitly generated default constructor is used. * + * @warning + * The context of the arguments is **not** changed.
+ * It's up to the caller to bind them to the right context(s). + * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { - const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; }); - return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{}); + if(node.details) { + if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(sz == 0u && node.default_constructor) { + return node.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief construct - * - * @sa construct - * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any construct(Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return construct(arguments, sizeof...(Args)); } + /** + * @brief Wraps an opaque element of the underlying 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 { + 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 { + return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; + } + /** * @brief Invokes a function given an identifier, if possible. * - * It must be possible to cast the instance to the parent type of the member - * function. - * - * @sa meta_func::invoke + * @warning + * The context of the arguments is **not** changed.
+ * It's up to the caller to bind them to the right context(s). * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. @@ -45911,40 +61556,40 @@ public: * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { - const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); - - for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) { - candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + 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) { + return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); + } + } } - return candidate ? candidate->invoke(std::move(instance), args) : meta_any{}; + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke * - * @sa invoke - * * @param id Unique identifier. * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. - * @return A wrapper containing the new instance, if any. + * @return A wrapper containing the returned value, if any. */ template meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { - meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; - return invoke(id, std::move(instance), arguments, sizeof...(Args)); + meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; + return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /** * @brief Sets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member.
- * The type of the value is such that a cast or conversion to the type of - * the variable is possible. Otherwise, invoking the setter does nothing. - * * @tparam Type Type of value to assign. * @param id Unique identifier. * @param instance An opaque instance of the underlying type. @@ -45959,45 +61604,51 @@ public: /** * @brief Gets the value of a given variable. - * - * It must be possible to cast the instance to the parent type of the data - * member. - * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { const auto candidate = data(id); - return candidate ? candidate.get(std::move(instance)) : meta_any{}; + return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Returns a range to visit registered top-level meta properties. * @return An iterable range to visit registered top-level meta properties. */ - [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { - return node->prop; + [[nodiscard]] meta_range prop() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; } /** - * @brief Lookup function for meta properties. - * - * Properties of base classes are also visited. - * + * @brief Lookup utility for meta properties (bases are also visited). * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ - [[nodiscard]] meta_prop prop(meta_any key) const { - return internal::find_by<&internal::meta_type_node::prop>(key, node); + [[nodiscard]] meta_prop prop(const id_type key) const { + if(node.details) { + if(const auto it = node.details->prop.find(key); it != node.details->prop.cend()) { + return meta_prop{*ctx, it->second}; + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.prop(key); elem) { + return elem; + } + } + + return meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { - return !(node == nullptr); + [[nodiscard]] explicit operator bool() const noexcept { + return !(ctx == nullptr); } /** @@ -46005,12 +61656,13 @@ public: * @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_type &other) const ENTT_NOEXCEPT { - return (!node && !other.node) || (node && other.node && *node->info == *other.node->info); + [[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)); } private: - const node_type *node; + internal::meta_type_node node; + const meta_ctx *ctx; }; /** @@ -46019,12 +61671,12 @@ private: * @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_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { return !(lhs == rhs); } -[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { - return node; +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return node.info ? meta_type{*ctx, node} : meta_type{}; } template @@ -46051,26 +61703,17 @@ bool meta_any::set(const id_type id, Type &&value) { } [[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { - if(const auto &info = type.info(); node && *node->info == info) { + if(node.info && *node.info == type.info()) { return as_ref(); - } else if(node) { - for(auto *it = node->conv; it; it = it->next) { - if(*it->type->info == info) { - return it->conv(*this); - } + } + + if(const auto *value = data(); node.details) { + if(auto it = node.details->conv.find(type.info().hash()); it != node.details->conv.cend()) { + return it->second.conv(*ctx, data()); } - if(node->conversion_helper && (type.is_arithmetic() || type.is_enum())) { - // exploits the fact that arithmetic types and enums are also default constructible - auto other = type.construct(); - ENTT_ASSERT(other.node->conversion_helper, "Conversion helper not found"); - const auto value = node->conversion_helper(nullptr, storage.data()); - other.node->conversion_helper(other.storage.data(), &value); - return other; - } - - for(auto *it = node->base; it; it = it->next) { - const auto as_const = it->cast(as_ref()); + for(auto &&curr: node.details->base) { + const auto &as_const = curr.second.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.second.cast(value)); if(auto other = as_const.allow_cast(type); other) { return other; @@ -46078,36 +61721,45 @@ bool meta_any::set(const id_type id, Type &&value) { } } - return {}; + if(node.conversion_helper && (type.is_arithmetic() || type.is_enum())) { + // exploits the fact that arithmetic types and enums are also default constructible + auto other = type.construct(); + ENTT_ASSERT(other.node.conversion_helper, "Conversion helper not found"); + const auto value = node.conversion_helper(nullptr, storage.data()); + other.node.conversion_helper(other.storage.data(), &value); + return other; + } + + return meta_any{meta_ctx_arg, *ctx}; } inline bool meta_any::assign(const meta_any &other) { - auto value = other.allow_cast(node); + auto value = other.allow_cast({*ctx, node}); return value && storage.assign(std::move(value.storage)); } inline bool meta_any::assign(meta_any &&other) { - if(*node->info == *other.node->info) { + if(*node.info == *other.node.info) { return storage.assign(std::move(other.storage)); } return assign(std::as_const(other)); } -[[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { - return node->type; +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; } -[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { - return node->ret; +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; } -[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT { - return index < arity() ? node->arg(index) : meta_type{}; +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; } -[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT { - return index < arity() ? node->arg(index) : meta_type{}; +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; } /** @@ -46118,11 +61770,25 @@ inline bool meta_any::assign(meta_any &&other) { class meta_sequence_container::meta_iterator final { friend class meta_sequence_container; - using deref_fn_type = meta_any(const any &, const std::ptrdiff_t); + enum class operation : std::uint8_t { + incr, + deref + }; + + using vtable_type = void(const operation, const any &, const std::ptrdiff_t, meta_any *); template - static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) { - return meta_any{std::in_place_type::reference>, any_cast(value)[pos]}; + static void basic_vtable(const operation op, const any &value, const std::ptrdiff_t offset, meta_any *other) { + switch(op) { + case operation::incr: { + auto &it = any_cast(const_cast(value)); + it = std::next(it, offset); + } break; + case operation::deref: { + const auto &it = any_cast(value); + other->emplace(*it); + } break; + } } public: @@ -46132,60 +61798,64 @@ public: using reference = value_type; using iterator_category = std::input_iterator_tag; - meta_iterator() ENTT_NOEXCEPT - : deref{}, - offset{}, + constexpr meta_iterator() noexcept + : ctx{}, + vtable{}, handle{} {} template - explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT - : deref{&deref_fn}, - offset{init}, + explicit meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, handle{std::move(iter)} {} - meta_iterator &operator++() ENTT_NOEXCEPT { - return ++offset, *this; + meta_iterator &operator++() noexcept { + vtable(operation::incr, handle, 1, nullptr); + return *this; } - meta_iterator operator++(int value) ENTT_NOEXCEPT { + meta_iterator operator++(int value) noexcept { meta_iterator orig = *this; - offset += ++value; + vtable(operation::incr, handle, ++value, nullptr); return orig; } - meta_iterator &operator--() ENTT_NOEXCEPT { - return --offset, *this; + meta_iterator &operator--() noexcept { + vtable(operation::incr, handle, -1, nullptr); + return *this; } - meta_iterator operator--(int value) ENTT_NOEXCEPT { + meta_iterator operator--(int value) noexcept { meta_iterator orig = *this; - offset -= ++value; + vtable(operation::incr, handle, --value, nullptr); return orig; } [[nodiscard]] reference operator*() const { - return deref(handle, offset); + reference other{meta_ctx_arg, *ctx}; + vtable(operation::deref, handle, 0, &other); + return other; } [[nodiscard]] pointer operator->() const { return operator*(); } - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } - [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { - return offset == other.offset; + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; } - [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: - deref_fn_type *deref; - difference_type offset; + const meta_ctx *ctx; + vtable_type *vtable; any handle; }; @@ -46222,27 +61892,29 @@ public: using reference = value_type; using iterator_category = std::input_iterator_tag; - meta_iterator() ENTT_NOEXCEPT - : vtable{}, + constexpr meta_iterator() noexcept + : ctx{}, + vtable{}, handle{} {} template - meta_iterator(std::integral_constant, It iter) ENTT_NOEXCEPT - : vtable{&basic_vtable}, + meta_iterator(const meta_ctx &area, std::integral_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, handle{std::move(iter)} {} - meta_iterator &operator++() ENTT_NOEXCEPT { + meta_iterator &operator++() noexcept { vtable(operation::incr, handle, nullptr); return *this; } - meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator operator++(int) noexcept { meta_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const { - reference other; + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; vtable(operation::deref, handle, &other); return other; } @@ -46251,19 +61923,20 @@ public: return operator*(); } - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } - [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { return handle == other.handle; } - [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: + const meta_ctx *ctx; vtable_type *vtable; any handle; }; @@ -46277,15 +61950,15 @@ private: * @brief Returns the meta value type of a container. * @return The meta value type of the container. */ -[[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { - return value_type_node; +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the size of a container. * @return The size of the container. */ -[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { return size_fn(storage); } @@ -46311,7 +61984,7 @@ inline bool meta_sequence_container::clear() { * @return An iterator to the first element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { - return iter_fn(storage, false); + return iter_fn(*ctx, storage, false); } /** @@ -46319,7 +61992,7 @@ inline bool meta_sequence_container::clear() { * @return An iterator that is past the last element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { - return iter_fn(storage, true); + return iter_fn(*ctx, storage, true); } /** @@ -46329,7 +62002,7 @@ inline bool meta_sequence_container::clear() { * @return A possibly invalid iterator to the inserted element. */ inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { - return insert_fn(storage, it.offset, value); + return insert_or_erase_fn(*ctx, storage, it.handle, value); } /** @@ -46338,7 +62011,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 erase_fn(storage, it.offset); + return insert(std::move(it), {}); } /** @@ -46357,7 +62030,7 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ -[[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { return static_cast(storage); } @@ -46365,7 +62038,7 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator * @brief Returns true if a container is also key-only, false otherwise. * @return True if the associative container is also key-only, false otherwise. */ -[[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { +[[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { return key_only_container; } @@ -46373,25 +62046,25 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator * @brief Returns the meta key type of a container. * @return The meta key type of the a container. */ -[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { - return key_type_node; +[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { + return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the meta mapped type of a container. * @return The meta mapped type of the a container. */ -[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { - return mapped_type_node; +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { + return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::value_type */ -[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { - return value_type_node; +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::size */ -[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { return size_fn(storage); } @@ -46402,22 +62075,32 @@ inline bool meta_associative_container::clear() { /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { - return iter_fn(storage, false); + return iter_fn(*ctx, storage, false); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { - return iter_fn(storage, true); + return iter_fn(*ctx, storage, true); } /** - * @brief Inserts an element (a key/value pair) into a container. + * @brief Inserts a key only element into a container. + * @param key The key of the element to insert. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key) { + meta_any value{*ctx, std::in_place_type}; + return (insert_or_erase_fn(storage, key, value) != 0u); +} + +/** + * @brief Inserts a key/value element into a container. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ -inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { - return insert_fn(storage, key, value); +inline bool meta_associative_container::insert(meta_any key, meta_any value) { + return (insert_or_erase_fn(storage, key, value) != 0u); } /** @@ -46425,8 +62108,8 @@ inline bool meta_associative_container::insert(meta_any key, meta_any value = {} * @param key The key of the element to remove. * @return A bool denoting whether the removal took place. */ -inline bool meta_associative_container::erase(meta_any key) { - return erase_fn(storage, key); +inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return insert(std::move(key), meta_any{meta_ctx_arg, *ctx}); } /** @@ -46435,14 +62118,14 @@ inline bool meta_associative_container::erase(meta_any key) { * @return An iterator to the element with the given key, if any. */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { - return find_fn(storage, key); + return find_fn(*ctx, storage, key); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ -[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { +[[nodiscard]] inline meta_associative_container::operator bool() const noexcept { return static_cast(storage); } @@ -46455,10 +62138,13 @@ inline bool meta_associative_container::erase(meta_any key) { #define ENTT_META_NODE_HPP #include +#include #include #include // #include "../config/config.h" +// #include "../container/dense_map.hpp" + // #include "../core/attribute.h" // #include "../core/enum.hpp" @@ -46469,6 +62155,10 @@ inline bool meta_associative_container::erase(meta_any key) { // #include "../core/type_traits.hpp" +// #include "../core/utility.hpp" + +// #include "context.hpp" + // #include "type_traits.hpp" @@ -46490,199 +62180,196 @@ enum class meta_traits : std::uint32_t { is_const = 0x0001, is_static = 0x0002, is_arithmetic = 0x0004, - is_array = 0x0008, - is_enum = 0x0010, - is_class = 0x0020, - is_pointer = 0x0040, - is_meta_pointer_like = 0x0080, - is_meta_sequence_container = 0x0100, - is_meta_associative_container = 0x0200, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_meta_pointer_like = 0x0100, + is_meta_sequence_container = 0x0200, + is_meta_associative_container = 0x0400, _entt_enum_as_bitmask }; struct meta_type_node; struct meta_prop_node { - meta_prop_node *next; - const meta_any &id; - meta_any &value; + meta_type_node (*type)(const meta_context &) noexcept {}; + std::shared_ptr value{}; }; struct meta_base_node { - meta_base_node *next; - meta_type_node *const type; - meta_any (*const cast)(meta_any) ENTT_NOEXCEPT; + meta_type_node (*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; }; struct meta_conv_node { - meta_conv_node *next; - meta_type_node *const type; - meta_any (*const conv)(const meta_any &); + meta_any (*conv)(const meta_ctx &, const void *){}; }; struct meta_ctor_node { using size_type = std::size_t; - meta_ctor_node *next; - const size_type arity; - meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; - meta_any (*const invoke)(meta_any *const); + + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_dtor_node { + void (*dtor)(void *){}; }; struct meta_data_node { using size_type = std::size_t; - id_type id; - const meta_traits traits; - meta_data_node *next; - meta_prop_node *prop; - const size_type arity; - meta_type_node *const type; - meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; - bool (*const set)(meta_handle, meta_any); - meta_any (*const get)(meta_handle); + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(const meta_ctx &, meta_handle){}; + dense_map prop{}; }; struct meta_func_node { using size_type = std::size_t; - id_type id; - const meta_traits traits; - meta_func_node *next; - meta_prop_node *prop; - const size_type arity; - meta_type_node *const ret; - meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; - meta_any (*const invoke)(meta_handle, meta_any *const); + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; + std::shared_ptr next{}; + dense_map prop{}; }; struct meta_template_node { using size_type = std::size_t; - const size_type arity; - meta_type_node *const type; - meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT; + + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + dense_map ctor{}; + dense_map base{}; + dense_map conv{}; + dense_map data{}; + dense_map func{}; + dense_map prop{}; }; struct meta_type_node { using size_type = std::size_t; - const type_info *info; - id_type id; - const meta_traits traits; - meta_type_node *next; - meta_prop_node *prop; - const size_type size_of; - meta_any (*const default_constructor)(); - double (*const conversion_helper)(void *, const void *); - const meta_template_node *const templ; - meta_ctor_node *ctor{nullptr}; - meta_base_node *base{nullptr}; - meta_conv_node *conv{nullptr}; - meta_data_node *data{nullptr}; - meta_func_node *func{nullptr}; - void (*dtor)(void *){nullptr}; -}; -template -meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; + const type_info *info{}; + id_type id{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + meta_type_node (*resolve)(const meta_context &) noexcept {}; + meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_dtor_node dtor{}; + std::shared_ptr details{}; +}; template -class ENTT_API meta_node { - static_assert(std::is_same_v>>, "Invalid type"); - - [[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT { - if constexpr(std::is_default_constructible_v) { - return +[]() { return meta_any{std::in_place_type}; }; - } else { - return static_cast>(nullptr); - } - } - - [[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT { - if constexpr(std::is_arithmetic_v) { - return +[](void *bin, const void *value) { - return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); - }; - } else if constexpr(std::is_enum_v) { - return +[](void *bin, const void *value) { - return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); - }; - } else { - return static_cast>(nullptr); - } - } - - [[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT { - if constexpr(is_complete_v>) { - static meta_template_node node{ - meta_template_traits::args_type::size, - meta_node::class_type>::resolve(), - [](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits::args_type{}, index); } - // tricks clang-format - }; - - return &node; - } else { - return nullptr; - } - } - -public: - [[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT { - static meta_type_node node{ - &type_id(), - {}, - internal::meta_traits::is_none - | (std::is_arithmetic_v ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none) - | (std::is_array_v ? internal::meta_traits::is_array : internal::meta_traits::is_none) - | (std::is_enum_v ? internal::meta_traits::is_enum : internal::meta_traits::is_none) - | (std::is_class_v ? internal::meta_traits::is_class : internal::meta_traits::is_none) - | (std::is_pointer_v ? internal::meta_traits::is_pointer : internal::meta_traits::is_none) - | (is_meta_pointer_like_v ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none) - | (is_complete_v> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none) - | (is_complete_v> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none), - nullptr, - nullptr, - size_of_v, - meta_default_constructor(), - meta_conversion_helper(), - meta_template_info() - // tricks clang-format - }; - - return &node; - } -}; +meta_type_node resolve(const meta_context &) noexcept; template -[[nodiscard]] meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { - meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node>>::resolve()...}; - return args[index + 1u]; +[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { + 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"); + return value(context); } -template -[[nodiscard]] static std::decay_t().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT { - for(auto *curr = node->*Member; curr; curr = curr->next) { - if constexpr(std::is_same_v) { - if(*curr->type->info == info_or_id) { - return curr; - } - } else if constexpr(std::is_same_v) { - if(curr->type->id == info_or_id) { - return curr; - } - } else { - if(curr->id == info_or_id) { - return curr; - } - } +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { + if(from.info && to.info && *from.info == *to.info) { + return instance; } - for(auto *curr = node->base; curr; curr = curr->next) { - if(auto *ret = find_by(info_or_id, curr->type); ret) { - return ret; + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { + return elem; + } } } return nullptr; } +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return it != context.value.end() ? &it->second : nullptr; +} + +template +[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + + if(auto *elem = try_resolve(context, type_id()); elem) { + return *elem; + } + + meta_type_node node{ + &type_id(), + type_id().hash(), + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), + size_of_v, + &resolve, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } + + if constexpr(!std::is_same_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { + if(element) { + return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; + } + + return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + } // namespace internal /** @@ -46754,7 +62441,7 @@ struct is_meta_pointer_like> namespace entt { /*! @brief Empty class type used to request the _as ref_ policy. */ -struct as_ref_t { +struct as_ref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -46768,7 +62455,7 @@ struct as_ref_t { }; /*! @brief Empty class type used to request the _as cref_ policy. */ -struct as_cref_t { +struct as_cref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -46782,7 +62469,7 @@ struct as_cref_t { }; /*! @brief Empty class type used to request the _as-is_ policy. */ -struct as_is_t { +struct as_is_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -46796,7 +62483,7 @@ struct as_is_t { }; /*! @brief Empty class type used to request the _as void_ policy. */ -struct as_void_t { +struct as_void_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. @@ -46809,6 +62496,26 @@ struct as_void_t { */ }; +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::disjunction< + std::is_same, + std::is_same, + std::is_same, + std::is_same> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + } // namespace entt #endif @@ -46819,8 +62526,13 @@ struct as_void_t { #include #include +#include +// #include "../core/fwd.hpp" + // #include "../core/iterator.hpp" +// #include "context.hpp" + namespace entt { @@ -46831,50 +62543,119 @@ namespace entt { namespace internal { -template +template struct meta_range_iterator final { using difference_type = std::ptrdiff_t; - using value_type = Type; + using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; - using node_type = Node; - meta_range_iterator() ENTT_NOEXCEPT - : it{} {} + meta_range_iterator() noexcept + : it{}, + ctx{} {} - meta_range_iterator(node_type *head) ENTT_NOEXCEPT - : it{head} {} + meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} - meta_range_iterator &operator++() ENTT_NOEXCEPT { - return (it = it->next), *this; + meta_range_iterator &operator++() noexcept { + return ++it, *this; } - meta_range_iterator operator++(int) ENTT_NOEXCEPT { + meta_range_iterator operator++(int) noexcept { meta_range_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return it; + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + constexpr meta_range_iterator operator--(int) noexcept { + meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, Type{*ctx, it[value].second}}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT { - return it == other.it; + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, Type{*ctx, it->second}}; } - [[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; private: - node_type *it; + It it; + const meta_ctx *ctx; }; +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + } // namespace internal /** @@ -46885,57 +62666,10 @@ private: /** * @brief Iterable range to use to iterate all types of meta objects. * @tparam Type Type of meta objects returned. - * @tparam Node Type of meta nodes iterated. + * @tparam It Type of forward iterator. */ -template -struct meta_range final { - /*! @brief Node type. */ - using node_type = Node; - /*! @brief Input iterator type. */ - using iterator = internal::meta_range_iterator; - /*! @brief Constant input iterator type. */ - using const_iterator = iterator; - - /*! @brief Default constructor. */ - meta_range() ENTT_NOEXCEPT = default; - - /** - * @brief Constructs a meta range from a given node. - * @param head The underlying node with which to construct the range. - */ - meta_range(node_type *head) ENTT_NOEXCEPT - : node{head} {} - - /** - * @brief Returns an iterator to the beginning. - * @return An iterator to the first meta object of the range. - */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { - return iterator{node}; - } - - /*! @copydoc cbegin */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return cbegin(); - } - - /** - * @brief Returns an iterator to the end. - * @return An iterator to the element following the last meta object of the - * range. - */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { - return iterator{}; - } - - /*! @copydoc cend */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return cend(); - } - -private: - node_type *node{nullptr}; -}; +template +using meta_range = iterable_adaptor>; } // namespace entt @@ -46945,10 +62679,12 @@ private: #ifndef ENTT_META_RESOLVE_HPP #define ENTT_META_RESOLVE_HPP -#include +#include // #include "../core/type_info.hpp" -// #include "ctx.hpp" +// #include "../locator/locator.hpp" + +// #include "context.hpp" // #include "meta.hpp" @@ -46962,19 +62698,57 @@ namespace entt { /** * @brief Returns the meta type associated with a given type. * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. * @return The meta type associated with the given type, if any. */ template -[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT { - return internal::meta_node>>::resolve(); +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; } /** * @brief Returns a range to use to visit all meta types. * @return An iterable range to use to visit all meta types. */ -[[nodiscard]] inline meta_range resolve() ENTT_NOEXCEPT { - return *internal::meta_context::global(); +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; } /** @@ -46982,14 +62756,20 @@ template * @param id Unique identifier. * @return The meta type associated with the given identifier, if any. */ -[[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT { - for(auto &&curr: resolve()) { - if(curr.id() == id) { - return curr; - } - } +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} - return {}; +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + auto &&context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return elem ? meta_type{ctx, *elem} : meta_type{}; } /** @@ -46997,14 +62777,8 @@ template * @param info The type info object of the requested type. * @return The meta type associated with the given type info object, if any. */ -[[nodiscard]] inline meta_type resolve(const type_info &info) ENTT_NOEXCEPT { - for(auto &&curr: resolve()) { - if(curr.info() == info) { - return curr; - } - } - - return {}; +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); } } // namespace entt @@ -47106,10 +62880,10 @@ inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value #include #include #include -// #include "../config/config.h" - // #include "../core/type_traits.hpp" +// #include "../locator/locator.hpp" + // #include "meta.hpp" // #include "node.hpp" @@ -47119,6 +62893,26 @@ inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value namespace entt { +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + /*! @brief Primary template isn't defined on purpose. */ template struct meta_function_descriptor; @@ -47131,17 +62925,12 @@ struct meta_function_descriptor; * @tparam Args Function arguments. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = std::conditional_t, type_list, type_list>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = true; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; /** * @brief Meta function descriptor. @@ -47151,17 +62940,12 @@ struct meta_function_descriptor { * @tparam Args Function arguments. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = std::conditional_t, type_list, type_list>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = false; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; /** * @brief Meta function descriptor. @@ -47170,17 +62954,12 @@ struct meta_function_descriptor { * @tparam Ret Data member type. */ template -struct meta_function_descriptor { - /*! @brief Meta data return type. */ - using return_type = Ret &; - /*! @brief Meta data arguments. */ - using args_type = std::conditional_t, type_list<>, type_list>; - - /*! @brief True if the meta data is const, false otherwise. */ - static constexpr auto is_const = false; - /*! @brief True if the meta data is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; /** * @brief Meta function descriptor. @@ -47190,17 +62969,12 @@ struct meta_function_descriptor { * @tparam Args Other function arguments. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = std::conditional_t>, Type>, type_list, type_list>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = std::is_base_of_v>, Type> && std::is_const_v>; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = !std::is_base_of_v>, Type>; -}; +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>> {}; /** * @brief Meta function descriptor. @@ -47208,17 +62982,12 @@ struct meta_function_descriptor { * @tparam Ret Function return type. */ template -struct meta_function_descriptor { - /*! @brief Meta function return type. */ - using return_type = Ret; - /*! @brief Meta function arguments. */ - using args_type = type_list<>; - - /*! @brief True if the meta function is const, false otherwise. */ - static constexpr auto is_const = false; - /*! @brief True if the meta function is static, false otherwise. */ - static constexpr auto is_static = true; -}; +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; /** * @brief Meta function helper. @@ -47259,6 +63028,33 @@ public: template using meta_function_helper_t = typename meta_function_helper::type; +/** + * @brief Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @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) { + if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + /** * @brief Wraps a value depending on the given policy. * @tparam Policy Optional policy (no policy set by default). @@ -47267,28 +63063,32 @@ using meta_function_helper_t = typename meta_function_helper::t * @return A meta any containing the returned value, if any. */ template -meta_any meta_dispatch([[maybe_unused]] Type &&value) { - if constexpr(std::is_same_v) { - return meta_any{std::in_place_type}; - } else if constexpr(std::is_same_v) { - return meta_any{std::in_place_type, std::forward(value)}; - } else if constexpr(std::is_same_v) { - static_assert(std::is_lvalue_reference_v, "Invalid type"); - return meta_any{std::in_place_type &>, std::as_const(value)}; - } else { - static_assert(std::is_same_v, "Policy not supported"); - return meta_any{std::forward(value)}; - } +std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template -[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT { - return internal::meta_arg_node(Type{}, index); +[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); } /** @@ -47334,6 +63134,49 @@ template return false; } +/** + * @brief Gets the value of a given variable. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(ctx, std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(ctx, std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, ctx}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, ctx}; + } else { + return meta_dispatch(ctx, *Data); + } + } else { + return meta_dispatch(ctx, Data); + } +} + /** * @brief Gets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. @@ -47343,32 +63186,8 @@ template * @return A meta any containing the value of the underlying variable. */ template -[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { - if constexpr(std::is_member_pointer_v || std::is_function_v>>) { - if constexpr(!std::is_array_v>>>) { - if constexpr(std::is_invocable_v) { - if(auto *clazz = instance->try_cast(); clazz) { - return meta_dispatch(std::invoke(Data, *clazz)); - } - } - - if constexpr(std::is_invocable_v) { - if(auto *fallback = instance->try_cast(); fallback) { - return meta_dispatch(std::invoke(Data, *fallback)); - } - } - } - - return meta_any{}; - } else if constexpr(std::is_pointer_v) { - if constexpr(std::is_array_v>) { - return meta_any{}; - } else { - return meta_dispatch(*Data); - } - } else { - return meta_dispatch(Data); - } +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + return meta_getter(locator::value_or(), std::move(instance)); } /** @@ -47378,44 +63197,44 @@ template namespace internal { -template -[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) { - if constexpr(std::is_same_v, void>) { - std::invoke(candidate, args...); - return meta_any{std::in_place_type}; +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_same_v(candidate), args...)), void>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; } else { - return meta_dispatch(std::invoke(candidate, args...)); + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); } } template -[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { +[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { using descriptor = meta_function_helper_t>; if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { - return meta_invoke_with_args(std::forward(candidate), *clazz, (args + Index)->cast>()...); + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { - return meta_invoke_with_args(std::forward(candidate), *clazz, (args + Index)->cast>()...); + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else { if(((args + Index)->allow_cast>() && ...)) { - return meta_invoke_with_args(std::forward(candidate), (args + Index)->cast>()...); + return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); } } - return meta_any{}; + return meta_any{meta_ctx_arg, ctx}; } template -[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence) { +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { if(((args + Index)->allow_cast() && ...)) { - return meta_any{std::in_place_type, (args + Index)->cast()...}; + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; } - return meta_any{}; + return meta_any{meta_ctx_arg, ctx}; } } // namespace internal @@ -47425,6 +63244,27 @@ template * @endcond */ +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + /** * @brief Tries to _invoke_ an object given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. @@ -47436,8 +63276,28 @@ template * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) { - return internal::meta_invoke(std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); } /** @@ -47450,8 +63310,26 @@ template * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_invoke(meta_handle instance, meta_any *const args) { - return internal::meta_invoke(std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); } /** @@ -47463,7 +63341,31 @@ template */ template [[nodiscard]] meta_any meta_construct(meta_any *const args) { - return internal::meta_construct(args, std::index_sequence_for{}); + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } } /** @@ -47471,17 +63373,32 @@ template * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. - * @param args Parameters to use to _invoke_ the object. * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) { - if constexpr(meta_function_helper_t::is_static) { - return internal::meta_invoke({}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); - } else { - return internal::meta_invoke(*args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); - } +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); } /** @@ -47493,8 +63410,8 @@ template * @return A meta any containing the returned value, if any. */ template -[[nodiscard]] meta_any meta_construct(meta_any *const args) { - return meta_construct(Candidate, args); +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); } } // namespace entt @@ -47579,107 +63496,6 @@ using invoke_result_t = typename std::invoke_result::type; #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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP @@ -47707,7 +63523,7 @@ using invoke_result_t = typename std::invoke_result::type; #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -47718,14 +63534,12 @@ using invoke_result_t = typename std::invoke_result::type; #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -47753,16 +63567,23 @@ using invoke_result_t = typename std::invoke_result::type; #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -47793,108 +63614,8 @@ using invoke_result_t = typename std::invoke_result::type; #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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 - namespace entt { @@ -47910,7 +63631,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -47923,7 +63644,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -47934,7 +63655,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -47964,7 +63685,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -47974,13 +63695,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -47996,14 +63717,13 @@ private: #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -48065,8 +63785,6 @@ using any = basic_any<>; #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -48136,12 +63854,14 @@ class basic_hashed_string: internal::basic_hashed_string { struct const_wrapper { // non-explicit constructor on purpose - constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} + 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { @@ -48152,7 +63872,7 @@ 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { @@ -48176,7 +63896,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } @@ -48187,7 +63907,7 @@ public: * @return The numeric representation of the string. */ template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } @@ -48196,12 +63916,12 @@ public: * @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) ENTT_NOEXCEPT { + [[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() ENTT_NOEXCEPT + constexpr basic_hashed_string() noexcept : base_type{} {} /** @@ -48209,7 +63929,7 @@ public: * @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) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** @@ -48218,7 +63938,7 @@ public: * @param str Human-readable identifier. */ template - constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** @@ -48230,14 +63950,14 @@ public: * * @param wrapper Helps achieving the purpose by relying on overloading. */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + 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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } @@ -48245,7 +63965,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } @@ -48253,12 +63973,12 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } @@ -48266,7 +63986,7 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; @@ -48297,7 +64017,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; * @return True if the two hashed strings are identical, false otherwise. */ template -[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } @@ -48309,7 +64029,7 @@ template * @return True if the two hashed strings differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } @@ -48321,7 +64041,7 @@ template * @return True if the first element is less than the second, false otherwise. */ template -[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } @@ -48334,7 +64054,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } @@ -48347,7 +64067,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } @@ -48360,7 +64080,7 @@ template * false otherwise. */ template -[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } @@ -48377,7 +64097,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } @@ -48386,7 +64106,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } @@ -48407,14 +64127,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -48426,26 +64146,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -48469,13 +64189,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -48491,16 +64211,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -48515,12 +64235,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -48532,7 +64252,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -48541,7 +64261,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -48549,7 +64269,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -48557,7 +64277,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -48573,7 +64293,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -48583,7 +64303,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -48593,7 +64313,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -48604,7 +64324,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -48615,7 +64335,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -48626,7 +64346,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -48642,7 +64362,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -48653,7 +64373,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -48787,22 +64507,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -48813,6 +64533,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -48943,6 +64715,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -49142,7 +64937,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -49230,7 +65025,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -49272,7 +65067,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -49312,6 +65107,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -49319,6 +65146,36 @@ using member_class_t = typename member_class::type; namespace entt { +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +enum class any_policy : std::uint8_t { + owner, + ref, + cref +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. @@ -49326,30 +65183,19 @@ namespace entt { */ template class basic_any { - enum class operation : std::uint8_t { - copy, - move, - transfer, - assign, - destroy, - compare, - get - }; - - enum class policy : std::uint8_t { - owner, - ref, - cref - }; - - using storage_type = std::aligned_storage_t; + using operation = internal::any_operation; + using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); - template - static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; template - static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) { + 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) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; @@ -49396,7 +65242,7 @@ class basic_any { break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { - return *static_cast(element) == *static_cast(other) ? other : nullptr; + return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } @@ -49409,31 +65255,32 @@ class basic_any { template void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + if constexpr(!std::is_void_v) { - info = &type_id>>(); vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "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) { - new(&storage) Type{std::forward(args)...}; + } else if constexpr(in_situ>>) { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { - new(&storage) Type(std::forward(args)...); + new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { - if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { - instance = new Type{std::forward(args)...}; + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { + instance = new std::remove_cv_t>{std::forward(args)...}; } else { - instance = new Type(std::forward(args)...); + instance = new std::remove_cv_t>(std::forward(args)...); } } } } - basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, @@ -49446,11 +65293,8 @@ public: static constexpr auto alignment = Align; /*! @brief Default constructor. */ - constexpr basic_any() ENTT_NOEXCEPT - : instance{}, - info{&type_id()}, - vtable{}, - mode{policy::owner} {} + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. @@ -49460,7 +65304,10 @@ public: */ template explicit basic_any(std::in_place_type_t, Args &&...args) - : basic_any{} { + : instance{}, + info{}, + vtable{}, + mode{policy::owner} { initialize(std::forward(args)...); } @@ -49471,9 +65318,7 @@ public: */ template, basic_any>>> basic_any(Type &&value) - : basic_any{} { - initialize>(std::forward(value)); - } + : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. @@ -49490,7 +65335,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_any(basic_any &&other) ENTT_NOEXCEPT + basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, @@ -49527,7 +65372,7 @@ public: * @param other The instance to move from. * @return This any object. */ - basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT { + basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { @@ -49557,7 +65402,7 @@ public: * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ - [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { + [[nodiscard]] const type_info &type() const noexcept { return *info; } @@ -49565,7 +65410,7 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } @@ -49574,7 +65419,7 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT { + [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } @@ -49582,8 +65427,8 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data() ENTT_NOEXCEPT { - return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + [[nodiscard]] void *data() noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** @@ -49591,8 +65436,8 @@ public: * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT { - return *info == req ? data() : nullptr; + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** @@ -49612,7 +65457,7 @@ public: * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ - bool assign(const any &other) { + bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } @@ -49621,7 +65466,7 @@ public: } /*! @copydoc assign */ - bool assign(any &&other) { + bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); @@ -49639,6 +65484,8 @@ public: vtable(operation::destroy, *this, nullptr); } + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; @@ -49648,7 +65495,7 @@ public: * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } @@ -49657,7 +65504,7 @@ public: * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ - bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } @@ -49665,16 +65512,25 @@ public: return (!vtable && !other.vtable); } + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ - [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ - [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } @@ -49682,7 +65538,7 @@ public: * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ - [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } @@ -49696,19 +65552,6 @@ private: policy mode; }; -/** - * @brief Checks if two wrappers differ in their content. - * @tparam Len Size of the storage reserved for the small buffer optimization. - * @tparam Align Alignment requirement. - * @param lhs A wrapper, either empty or not. - * @param rhs A wrapper, either empty or not. - * @return True if the two wrappers differ in their content, false otherwise. - */ -template -[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. @@ -49718,7 +65561,7 @@ template * @return The element converted to the requested type. */ template -Type any_cast(const basic_any &data) ENTT_NOEXCEPT { +Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); @@ -49726,7 +65569,7 @@ Type any_cast(const basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &data) ENTT_NOEXCEPT { +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"); @@ -49735,7 +65578,7 @@ Type any_cast(basic_any &data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -Type any_cast(basic_any &&data) ENTT_NOEXCEPT { +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)); @@ -49751,17 +65594,21 @@ Type any_cast(basic_any &&data) ENTT_NOEXCEPT { /*! @copydoc any_cast */ template -const Type *any_cast(const basic_any *data) ENTT_NOEXCEPT { - const auto &info = type_id>>(); +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) ENTT_NOEXCEPT { - const auto &info = type_id>>(); - // last attempt to make wrappers for const references return their values - return static_cast(static_cast, Type> *>(data)->data(info)); +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)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } } /** @@ -49788,7 +65635,7 @@ basic_any make_any(Args &&...args) { */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { - return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; + return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt @@ -49821,14 +65668,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -49840,26 +65687,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -49883,13 +65730,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -49905,16 +65752,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -49929,12 +65776,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -49946,7 +65793,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -49955,7 +65802,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -49963,7 +65810,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -49971,7 +65818,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -49987,7 +65834,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -49997,7 +65844,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -50007,7 +65854,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -50018,7 +65865,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -50029,7 +65876,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -50040,7 +65887,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -50056,7 +65903,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -50067,7 +65914,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -50201,22 +66048,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -50227,6 +66074,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -50357,6 +66256,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -50556,7 +66478,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -50644,7 +66566,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -50686,7 +66608,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -50726,6 +66648,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -50734,12 +66688,11 @@ using member_class_t = typename member_class::type; #ifndef ENTT_POLY_FWD_HPP #define ENTT_POLY_FWD_HPP -#include -#include +#include namespace entt { -template)> +template class basic_poly; /** @@ -50806,11 +66759,11 @@ class poly_vtable { static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any &, Args...); template - static auto make_vtable(value_list) ENTT_NOEXCEPT + static auto make_vtable(value_list) noexcept -> decltype(std::make_tuple(vtable_entry(Candidate)...)); template - [[nodiscard]] static constexpr auto make_vtable(type_list) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto make_vtable(type_list) noexcept { if constexpr(sizeof...(Func) == 0u) { return decltype(make_vtable(typename Concept::template impl{})){}; } else if constexpr((std::is_function_v && ...)) { @@ -50819,7 +66772,7 @@ class poly_vtable { } template - static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) ENTT_NOEXCEPT { + static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept { if constexpr(std::is_invocable_r_v) { entry = +[](Any &, Args... args) -> Ret { return std::invoke(Candidate, std::forward(args)...); @@ -50832,7 +66785,7 @@ class poly_vtable { } template - [[nodiscard]] static auto fill_vtable(std::index_sequence) ENTT_NOEXCEPT { + [[nodiscard]] static auto fill_vtable(std::index_sequence) noexcept { vtable_type impl{}; (fill_vtable_entry>>(std::get(impl)), ...); return impl; @@ -50851,7 +66804,7 @@ public: * @return A static virtual table for the given concept and type. */ template - [[nodiscard]] static type instance() ENTT_NOEXCEPT { + [[nodiscard]] static type instance() noexcept { static_assert(std::is_same_v>, "Type differs from its decayed form"); static const vtable_type vtable = fill_vtable(std::make_index_sequence::size>{}); @@ -50943,7 +66896,7 @@ public: using vtable_type = typename poly_vtable::type; /*! @brief Default constructor. */ - basic_poly() ENTT_NOEXCEPT + basic_poly() noexcept : storage{}, vtable{} {} @@ -50964,14 +66917,14 @@ public: * @param value An instance of an object to use to initialize the poly. */ template>, basic_poly>>> - basic_poly(Type &&value) ENTT_NOEXCEPT + basic_poly(Type &&value) noexcept : basic_poly{std::in_place_type>>, std::forward(value)} {} /** * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ - [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { + [[nodiscard]] const type_info &type() const noexcept { return storage.type(); } @@ -50979,12 +66932,12 @@ public: * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ - [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return storage.data(); } /*! @copydoc data */ - [[nodiscard]] void *data() ENTT_NOEXCEPT { + [[nodiscard]] void *data() noexcept { return storage.data(); } @@ -51010,7 +66963,7 @@ public: * @brief Returns false if a poly is empty, true otherwise. * @return False if the poly is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(storage); } @@ -51018,12 +66971,12 @@ public: * @brief Returns a pointer to the underlying concept. * @return A pointer to the underlying concept. */ - [[nodiscard]] concept_type *operator->() ENTT_NOEXCEPT { + [[nodiscard]] concept_type *operator->() noexcept { return this; } /*! @copydoc operator-> */ - [[nodiscard]] const concept_type *operator->() const ENTT_NOEXCEPT { + [[nodiscard]] const concept_type *operator->() const noexcept { return this; } @@ -51031,7 +66984,7 @@ public: * @brief Aliasing constructor. * @return A poly that shares a reference to an unmanaged object. */ - [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { + [[nodiscard]] basic_poly as_ref() noexcept { basic_poly ref{}; ref.storage = storage.as_ref(); ref.vtable = vtable; @@ -51039,7 +66992,7 @@ public: } /*! @copydoc as_ref */ - [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { + [[nodiscard]] basic_poly as_ref() const noexcept { basic_poly ref{}; ref.storage = storage.as_ref(); ref.vtable = vtable; @@ -51062,107 +67015,6 @@ private: #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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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 - namespace entt { @@ -51268,7 +67120,7 @@ class process { static_cast(this)->aborted(); } - void next(...) const ENTT_NOEXCEPT {} + void next(...) const noexcept {} protected: /** @@ -51277,7 +67129,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * alive. */ - void succeed() ENTT_NOEXCEPT { + void succeed() noexcept { if(alive()) { current = state::succeeded; } @@ -51289,7 +67141,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * alive. */ - void fail() ENTT_NOEXCEPT { + void fail() noexcept { if(alive()) { current = state::failed; } @@ -51301,7 +67153,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * running. */ - void pause() ENTT_NOEXCEPT { + void pause() noexcept { if(current == state::running) { current = state::paused; } @@ -51313,7 +67165,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * paused. */ - void unpause() ENTT_NOEXCEPT { + void unpause() noexcept { if(current == state::paused) { current = state::running; } @@ -51324,7 +67176,7 @@ public: using delta_type = Delta; /*! @brief Default destructor. */ - virtual ~process() ENTT_NOEXCEPT { + virtual ~process() noexcept { static_assert(std::is_base_of_v, "Incorrect use of the class template"); } @@ -51350,7 +67202,7 @@ public: * @brief Returns true if a process is either running or paused. * @return True if the process is still alive, false otherwise. */ - [[nodiscard]] bool alive() const ENTT_NOEXCEPT { + [[nodiscard]] bool alive() const noexcept { return current == state::running || current == state::paused; } @@ -51358,7 +67210,7 @@ public: * @brief Returns true if a process is already terminated. * @return True if the process is terminated, false otherwise. */ - [[nodiscard]] bool finished() const ENTT_NOEXCEPT { + [[nodiscard]] bool finished() const noexcept { return current == state::finished; } @@ -51366,7 +67218,7 @@ public: * @brief Returns true if a process is currently paused. * @return True if the process is paused, false otherwise. */ - [[nodiscard]] bool paused() const ENTT_NOEXCEPT { + [[nodiscard]] bool paused() const noexcept { return current == state::paused; } @@ -51374,7 +67226,7 @@ public: * @brief Returns true if a process terminated with errors. * @return True if the process terminated with errors, false otherwise. */ - [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { + [[nodiscard]] bool rejected() const noexcept { return current == state::rejected; } @@ -51499,8 +67351,6 @@ struct process_adaptor: process, Delta>, private Fu #include #include #include -// #include "../config/config.h" - // #include "process.hpp" #ifndef ENTT_PROCESS_PROCESS_HPP #define ENTT_PROCESS_PROCESS_HPP @@ -51508,8 +67358,6 @@ struct process_adaptor: process, Delta>, private Fu #include #include #include -// #include "../config/config.h" - namespace entt { @@ -51615,7 +67463,7 @@ class process { static_cast(this)->aborted(); } - void next(...) const ENTT_NOEXCEPT {} + void next(...) const noexcept {} protected: /** @@ -51624,7 +67472,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * alive. */ - void succeed() ENTT_NOEXCEPT { + void succeed() noexcept { if(alive()) { current = state::succeeded; } @@ -51636,7 +67484,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * alive. */ - void fail() ENTT_NOEXCEPT { + void fail() noexcept { if(alive()) { current = state::failed; } @@ -51648,7 +67496,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * running. */ - void pause() ENTT_NOEXCEPT { + void pause() noexcept { if(current == state::running) { current = state::paused; } @@ -51660,7 +67508,7 @@ protected: * The function is idempotent and it does nothing if the process isn't * paused. */ - void unpause() ENTT_NOEXCEPT { + void unpause() noexcept { if(current == state::paused) { current = state::running; } @@ -51671,7 +67519,7 @@ public: using delta_type = Delta; /*! @brief Default destructor. */ - virtual ~process() ENTT_NOEXCEPT { + virtual ~process() noexcept { static_assert(std::is_base_of_v, "Incorrect use of the class template"); } @@ -51697,7 +67545,7 @@ public: * @brief Returns true if a process is either running or paused. * @return True if the process is still alive, false otherwise. */ - [[nodiscard]] bool alive() const ENTT_NOEXCEPT { + [[nodiscard]] bool alive() const noexcept { return current == state::running || current == state::paused; } @@ -51705,7 +67553,7 @@ public: * @brief Returns true if a process is already terminated. * @return True if the process is terminated, false otherwise. */ - [[nodiscard]] bool finished() const ENTT_NOEXCEPT { + [[nodiscard]] bool finished() const noexcept { return current == state::finished; } @@ -51713,7 +67561,7 @@ public: * @brief Returns true if a process is currently paused. * @return True if the process is paused, false otherwise. */ - [[nodiscard]] bool paused() const ENTT_NOEXCEPT { + [[nodiscard]] bool paused() const noexcept { return current == state::paused; } @@ -51721,7 +67569,7 @@ public: * @brief Returns true if a process terminated with errors. * @return True if the process terminated with errors, false otherwise. */ - [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { + [[nodiscard]] bool rejected() const noexcept { return current == state::rejected; } @@ -51880,7 +67728,7 @@ class scheduler { }; struct continuation { - continuation(process_handler *ref) ENTT_NOEXCEPT + continuation(process_handler *ref) noexcept : handler{ref} {} template @@ -51936,7 +67784,8 @@ public: using size_type = std::size_t; /*! @brief Default constructor. */ - scheduler() = default; + scheduler() + : handlers{} {} /*! @brief Default move constructor. */ scheduler(scheduler &&) = default; @@ -51948,7 +67797,7 @@ public: * @brief Number of processes currently scheduled. * @return Number of processes currently scheduled. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return handlers.size(); } @@ -51956,7 +67805,7 @@ public: * @brief Returns true if at least a process is currently scheduled. * @return True if there are scheduled processes, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return handlers.empty(); } @@ -52120,112 +67969,10 @@ private: #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 10 -#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_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) -# define ENTT_THROW throw -# define ENTT_TRY try -# define ENTT_CATCH catch(...) -#else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) -# 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(...) (void(0)) -#elif !defined ENTT_ASSERT -# include -# define ENTT_ASSERT(condition, ...) assert(condition) -#endif - -#ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false -#else -# define ENTT_IGNORE_IF_EMPTY true -#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 "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP -#include #include #include #include @@ -52255,7 +68002,7 @@ private: #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -52266,14 +68013,12 @@ private: #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -52301,16 +68046,23 @@ private: #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -52345,6 +68097,14 @@ private: #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 @@ -52364,7 +68124,7 @@ private: #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -52375,14 +68135,12 @@ private: #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -52410,16 +68168,23 @@ private: #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -52446,28 +68211,17 @@ private: #endif -// #include "type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - -#include -#include -#include -#include -// #include "../config/config.h" - // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -52594,22 +68348,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -52620,6 +68374,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -52750,6 +68556,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -52949,7 +68778,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -53037,7 +68866,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -53079,7 +68908,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -53119,6 +68948,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -53139,22 +69000,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -53169,22 +69030,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -53227,7 +69088,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -53235,13 +69096,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -53251,7 +69112,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -53263,7 +69124,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -53272,25 +69133,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -53298,12 +69159,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -53311,7 +69172,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -53324,7 +69185,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -53335,7 +69196,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -53361,7 +69222,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -53401,9 +69262,8 @@ struct tuple_element>: conditional #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -53413,46 +69273,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -53468,22 +69406,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -53492,17 +69432,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -53530,69 +69470,12 @@ private: namespace entt { -/** - * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). - * @tparam Type Pointer type. - * @param ptr Fancy or raw pointer. - * @return A raw pointer that represents the address of the original pointer. - */ -template -[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { - if constexpr(std::is_pointer_v>>) { - return ptr; - } else { - return to_address(std::forward(ptr).operator->()); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { - lhs = rhs; - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { - lhs = std::move(rhs); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); - - if constexpr(std::allocator_traits::propagate_on_container_swap::value) { - using std::swap; - swap(lhs, rhs); - } -} - /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ -[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } @@ -53601,8 +69484,8 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ -[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { - ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { @@ -53618,11 +69501,68 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ -[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { - ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. @@ -53638,14 +69578,14 @@ struct allocation_deleter: private Allocator { * @brief Inherited constructors. * @param alloc The allocator to use. */ - allocation_deleter(const allocator_type &alloc) + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ - void operator()(pointer ptr) { + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); @@ -53662,7 +69602,7 @@ struct allocation_deleter: private Allocator { * @return A properly initialized unique pointer with a custom deleter. */ template -auto allocate_unique(Allocator &allocator, Args &&...args) { +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; @@ -53692,14 +69632,14 @@ namespace internal { template struct uses_allocator_construction { template - static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { - return std::tuple(std::allocator_arg, allocator, std::forward(params)...); + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); @@ -53713,7 +69653,7 @@ struct uses_allocator_construction> { using type = std::pair; template - static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), @@ -53721,22 +69661,22 @@ struct uses_allocator_construction> { } template - static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template - static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template - static constexpr auto args(const Allocator &allocator, const std::pair &value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template - static constexpr auto args(const Allocator &allocator, std::pair &&value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; @@ -53762,7 +69702,7 @@ struct uses_allocator_construction> { * @return The arguments needed to create an object of the given type. */ template -constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } @@ -53800,7 +69740,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { - return std::apply([&](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt @@ -53933,22 +69873,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -53959,6 +69899,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -54089,6 +70081,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -54288,7 +70303,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -54376,7 +70391,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -54418,7 +70433,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -54458,6 +70473,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -54543,109 +70590,109 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_iterator() ENTT_NOEXCEPT + constexpr dense_map_iterator() noexcept : it{} {} - dense_map_iterator(const It iter) ENTT_NOEXCEPT + constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} - dense_map_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } - dense_map_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } - dense_map_iterator &operator--() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } - dense_map_iterator operator--(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } - dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } - dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template - friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -54664,37 +70711,37 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_local_iterator() ENTT_NOEXCEPT + constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} - dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> - dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} - dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } - dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } - [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } @@ -54704,12 +70751,12 @@ private: }; template -[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -54745,7 +70792,7 @@ class dense_map { using packed_container_type = std::vector>; template - [[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(sparse.second()(key), bucket_count()); } @@ -54804,8 +70851,8 @@ class dense_map { void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { - packed.first()[pos] = std::move(packed.first().back()); size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } @@ -54845,7 +70892,7 @@ public: /*! @brief Default constructor. */ dense_map() - : dense_map(minimum_capacity) {} + : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. @@ -54857,35 +70904,35 @@ public: /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const allocator_type &allocator) - : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) - : dense_map{bucket_count, hash, key_equal{}, allocator} {} + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ - explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { - rehash(bucket_count); + rehash(cnt); } /*! @brief Default copy constructor. */ @@ -54902,7 +70949,7 @@ public: threshold{other.threshold} {} /*! @brief Default move constructor. */ - dense_map(dense_map &&) = default; + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. @@ -54924,13 +70971,13 @@ public: * @brief Default move assignment operator. * @return This container. */ - dense_map &operator=(dense_map &&) = default; + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } @@ -54942,17 +70989,17 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } @@ -54966,17 +71013,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return packed.first().end(); } @@ -54984,7 +71031,7 @@ public: * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } @@ -54992,12 +71039,20 @@ public: * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + /*! @brief Clears the container. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); @@ -55213,6 +71268,27 @@ public: return insert_or_do_nothing(std::move(key)).first->second; } + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. @@ -55230,7 +71306,7 @@ public: /** * @brief Finds an element with a key that compares _equivalent_ to a given - * value. + * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element @@ -55249,6 +71325,46 @@ public: return constrained_find(key, key_to_bucket(key)); } + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. @@ -55312,7 +71428,7 @@ public: * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ - [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { + [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } @@ -55388,15 +71504,19 @@ public: /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. - * @param count New number of buckets. + * @param cnt New number of buckets. */ - void rehash(const size_type count) { - auto value = (std::max)(count, minimum_capacity); - value = (std::max)(value, static_cast(size() / max_load_factor())); + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); - std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits::max)()); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); @@ -55408,11 +71528,11 @@ public: /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. - * @param count New number of elements. + * @param cnt New number of elements. */ - void reserve(const size_type count) { - packed.first().reserve(count); - rehash(static_cast(std::ceil(count / max_load_factor()))); + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** @@ -55467,6 +71587,14 @@ struct uses_allocator, Allocator> #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 @@ -55486,7 +71614,7 @@ struct uses_allocator, Allocator> #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -55497,14 +71625,12 @@ struct uses_allocator, Allocator> #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -55532,16 +71658,23 @@ struct uses_allocator, Allocator> #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -55568,28 +71701,17 @@ struct uses_allocator, Allocator> #endif -// #include "type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - -#include -#include -#include -#include -// #include "../config/config.h" - // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -55716,22 +71838,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -55742,6 +71864,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -55872,6 +72046,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -56071,7 +72268,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -56159,7 +72356,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -56201,7 +72398,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -56241,6 +72438,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -56261,22 +72490,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -56291,22 +72520,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -56349,7 +72578,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -56357,13 +72586,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -56373,7 +72602,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -56385,7 +72614,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -56394,25 +72623,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -56420,12 +72649,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -56433,7 +72662,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -56446,7 +72675,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -56457,7 +72686,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -56483,7 +72712,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -56521,14 +72750,13 @@ struct tuple_element>: conditional -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -56547,9 +72775,8 @@ using any = basic_any<>; #include #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -56559,46 +72786,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -56614,22 +72919,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -56638,17 +72945,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -56665,9 +72972,8 @@ private: #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#include #include -// #include "../config/config.h" - namespace entt { @@ -56683,7 +72989,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -56696,7 +73002,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -56707,7 +73013,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -56737,7 +73043,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -56747,13 +73053,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -56787,8 +73093,8 @@ class resource; #endif // #include "loader.hpp" -#ifndef ENTT_RESOURCE_LOADEr_HPP -#define ENTT_RESOURCE_LOADEr_HPP +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP #include #include @@ -56829,8 +73135,6 @@ struct resource_loader { #include #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -56856,22 +73160,27 @@ class resource { static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + /*! @brief Default constructor. */ - resource() ENTT_NOEXCEPT + resource() noexcept : value{} {} /** * @brief Creates a handle from a weak pointer, namely a resource. * @param res A weak pointer to a resource. */ - explicit resource(std::shared_ptr res) ENTT_NOEXCEPT + explicit resource(handle_type res) noexcept : value{std::move(res)} {} /*! @brief Default copy constructor. */ - resource(const resource &) ENTT_NOEXCEPT = default; + resource(const resource &) noexcept = default; /*! @brief Default move constructor. */ - resource(resource &&) ENTT_NOEXCEPT = default; + resource(resource &&) noexcept = default; /** * @brief Aliasing constructor. @@ -56880,7 +73189,7 @@ public: * @param res Unrelated and unmanaged resources. */ template - resource(const resource &other, Type &res) ENTT_NOEXCEPT + resource(const resource &other, element_type &res) noexcept : value{other.value, std::addressof(res)} {} /** @@ -56889,7 +73198,7 @@ public: * @param other The handle to copy from. */ template>> - resource(const resource &other) ENTT_NOEXCEPT + resource(const resource &other) noexcept : value{other.value} {} /** @@ -56898,20 +73207,20 @@ public: * @param other The handle to move from. */ template>> - resource(resource &&other) ENTT_NOEXCEPT + resource(resource &&other) noexcept : value{std::move(other.value)} {} /** * @brief Default copy assignment operator. * @return This resource handle. */ - resource &operator=(const resource &) ENTT_NOEXCEPT = default; + resource &operator=(const resource &) noexcept = default; /** * @brief Default move assignment operator. * @return This resource handle. */ - resource &operator=(resource &&) ENTT_NOEXCEPT = default; + resource &operator=(resource &&) noexcept = default; /** * @brief Copy assignment operator from foreign handle. @@ -56921,7 +73230,7 @@ public: */ template std::enable_if_t, resource &> - operator=(const resource &other) ENTT_NOEXCEPT { + operator=(const resource &other) noexcept { value = other.value; return *this; } @@ -56934,7 +73243,7 @@ public: */ template std::enable_if_t, resource &> - operator=(resource &&other) ENTT_NOEXCEPT { + operator=(resource &&other) noexcept { value = std::move(other.value); return *this; } @@ -56947,12 +73256,12 @@ public: * * @return A reference to the managed resource. */ - [[nodiscard]] Type &operator*() const ENTT_NOEXCEPT { + [[nodiscard]] element_type &operator*() const noexcept { return *value; } /*! @copydoc operator* */ - [[nodiscard]] operator Type &() const ENTT_NOEXCEPT { + [[nodiscard]] operator element_type &() const noexcept { return *value; } @@ -56960,7 +73269,7 @@ public: * @brief Returns a pointer to the managed resource. * @return A pointer to the managed resource. */ - [[nodiscard]] Type *operator->() const ENTT_NOEXCEPT { + [[nodiscard]] element_type *operator->() const noexcept { return value.get(); } @@ -56968,20 +73277,20 @@ public: * @brief Returns true if a handle contains a resource, false otherwise. * @return True if the handle contains a resource, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(value); } /** - * @brief Returns the number of handles pointing the same resource. - * @return The number of handles pointing the same resource. + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. */ - [[nodiscard]] long use_count() const ENTT_NOEXCEPT { - return value.use_count(); + [[nodiscard]] const handle_type &handle() const noexcept { + return value; } private: - std::shared_ptr value; + handle_type value; }; /** @@ -56993,7 +73302,7 @@ private: * @return True if both handles refer to the same resource, false otherwise. */ template -[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) ENTT_NOEXCEPT { +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) == std::addressof(*rhs)); } @@ -57005,11 +73314,65 @@ template * @param rhs A valid handle. * @return False if both handles refer to the same registry, true otherwise. */ -template -[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) ENTT_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. + * @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 { + 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. + * @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)); +} + +/** + * @brief Compares two handles. + * @tparam Res Type of resource managed by the first handle. + * @tparam Other 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 { + 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. + * @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 { + return !(lhs < rhs); +} + } // namespace entt #endif @@ -57036,108 +73399,108 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - resource_cache_iterator() ENTT_NOEXCEPT = default; + constexpr resource_cache_iterator() noexcept = default; - resource_cache_iterator(const It iter) ENTT_NOEXCEPT + constexpr resource_cache_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - resource_cache_iterator(const resource_cache_iterator, Other> &other) ENTT_NOEXCEPT + constexpr resource_cache_iterator(const resource_cache_iterator, Other> &other) noexcept : it{other.it} {} - resource_cache_iterator &operator++() ENTT_NOEXCEPT { + constexpr resource_cache_iterator &operator++() noexcept { return ++it, *this; } - resource_cache_iterator operator++(int) ENTT_NOEXCEPT { + constexpr resource_cache_iterator operator++(int) noexcept { resource_cache_iterator orig = *this; return ++(*this), orig; } - resource_cache_iterator &operator--() ENTT_NOEXCEPT { + constexpr resource_cache_iterator &operator--() noexcept { return --it, *this; } - resource_cache_iterator operator--(int) ENTT_NOEXCEPT { + constexpr resource_cache_iterator operator--(int) noexcept { resource_cache_iterator orig = *this; return operator--(), orig; } - resource_cache_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr resource_cache_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - resource_cache_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr resource_cache_iterator operator+(const difference_type value) const noexcept { resource_cache_iterator copy = *this; return (copy += value); } - resource_cache_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr resource_cache_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - resource_cache_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr resource_cache_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].first, resource{it[value].second}}; } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return (*this)[0]; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } template - friend std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; template - friend bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; template - friend bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -57232,7 +73595,7 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return pool.first().get_allocator(); } @@ -57244,17 +73607,17 @@ public: * * @return An iterator to the first instance of the internal cache. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return pool.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return pool.first().begin(); } @@ -57268,17 +73631,17 @@ public: * @return An iterator to the element following the last instance of the * internal cache. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return pool.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return pool.first().end(); } @@ -57286,7 +73649,7 @@ public: * @brief Returns true if a cache contains no resources, false otherwise. * @return True if the cache contains no resources, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return pool.first().empty(); } @@ -57294,12 +73657,12 @@ public: * @brief Number of resources managed by a cache. * @return Number of resources currently stored. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return pool.first().size(); } /*! @brief Clears a cache. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { pool.first().clear(); } @@ -57421,8 +73784,8 @@ private: #endif // #include "resource/loader.hpp" -#ifndef ENTT_RESOURCE_LOADEr_HPP -#define ENTT_RESOURCE_LOADEr_HPP +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP #include #include @@ -57463,8 +73826,6 @@ struct resource_loader { #include #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -57490,22 +73851,27 @@ class resource { static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + /*! @brief Default constructor. */ - resource() ENTT_NOEXCEPT + resource() noexcept : value{} {} /** * @brief Creates a handle from a weak pointer, namely a resource. * @param res A weak pointer to a resource. */ - explicit resource(std::shared_ptr res) ENTT_NOEXCEPT + explicit resource(handle_type res) noexcept : value{std::move(res)} {} /*! @brief Default copy constructor. */ - resource(const resource &) ENTT_NOEXCEPT = default; + resource(const resource &) noexcept = default; /*! @brief Default move constructor. */ - resource(resource &&) ENTT_NOEXCEPT = default; + resource(resource &&) noexcept = default; /** * @brief Aliasing constructor. @@ -57514,7 +73880,7 @@ public: * @param res Unrelated and unmanaged resources. */ template - resource(const resource &other, Type &res) ENTT_NOEXCEPT + resource(const resource &other, element_type &res) noexcept : value{other.value, std::addressof(res)} {} /** @@ -57523,7 +73889,7 @@ public: * @param other The handle to copy from. */ template>> - resource(const resource &other) ENTT_NOEXCEPT + resource(const resource &other) noexcept : value{other.value} {} /** @@ -57532,20 +73898,20 @@ public: * @param other The handle to move from. */ template>> - resource(resource &&other) ENTT_NOEXCEPT + resource(resource &&other) noexcept : value{std::move(other.value)} {} /** * @brief Default copy assignment operator. * @return This resource handle. */ - resource &operator=(const resource &) ENTT_NOEXCEPT = default; + resource &operator=(const resource &) noexcept = default; /** * @brief Default move assignment operator. * @return This resource handle. */ - resource &operator=(resource &&) ENTT_NOEXCEPT = default; + resource &operator=(resource &&) noexcept = default; /** * @brief Copy assignment operator from foreign handle. @@ -57555,7 +73921,7 @@ public: */ template std::enable_if_t, resource &> - operator=(const resource &other) ENTT_NOEXCEPT { + operator=(const resource &other) noexcept { value = other.value; return *this; } @@ -57568,7 +73934,7 @@ public: */ template std::enable_if_t, resource &> - operator=(resource &&other) ENTT_NOEXCEPT { + operator=(resource &&other) noexcept { value = std::move(other.value); return *this; } @@ -57581,12 +73947,12 @@ public: * * @return A reference to the managed resource. */ - [[nodiscard]] Type &operator*() const ENTT_NOEXCEPT { + [[nodiscard]] element_type &operator*() const noexcept { return *value; } /*! @copydoc operator* */ - [[nodiscard]] operator Type &() const ENTT_NOEXCEPT { + [[nodiscard]] operator element_type &() const noexcept { return *value; } @@ -57594,7 +73960,7 @@ public: * @brief Returns a pointer to the managed resource. * @return A pointer to the managed resource. */ - [[nodiscard]] Type *operator->() const ENTT_NOEXCEPT { + [[nodiscard]] element_type *operator->() const noexcept { return value.get(); } @@ -57602,20 +73968,20 @@ public: * @brief Returns true if a handle contains a resource, false otherwise. * @return True if the handle contains a resource, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(value); } /** - * @brief Returns the number of handles pointing the same resource. - * @return The number of handles pointing the same resource. + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. */ - [[nodiscard]] long use_count() const ENTT_NOEXCEPT { - return value.use_count(); + [[nodiscard]] const handle_type &handle() const noexcept { + return value; } private: - std::shared_ptr value; + handle_type value; }; /** @@ -57627,7 +73993,7 @@ private: * @return True if both handles refer to the same resource, false otherwise. */ template -[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) ENTT_NOEXCEPT { +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) == std::addressof(*rhs)); } @@ -57639,11 +74005,65 @@ template * @param rhs A valid handle. * @return False if both handles refer to the same registry, true otherwise. */ -template -[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) ENTT_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. + * @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 { + 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. + * @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)); +} + +/** + * @brief Compares two handles. + * @tparam Res Type of resource managed by the first handle. + * @tparam Other 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 { + 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. + * @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 { + return !(lhs < rhs); +} + } // namespace entt #endif @@ -57676,7 +74096,7 @@ template #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -57687,14 +74107,12 @@ template #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -57722,16 +74140,23 @@ template #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -57785,7 +74210,7 @@ template #define ENTT_VERSION_MAJOR 3 -#define ENTT_VERSION_MINOR 10 +#define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 0 #define ENTT_VERSION \ @@ -57796,14 +74221,12 @@ template #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) -# define ENTT_NOEXCEPT noexcept -# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else -# define ENTT_NOEXCEPT -# define ENTT_NOEXCEPT_IF(...) +# 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) @@ -57831,16 +74254,23 @@ template #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT -# define ENTT_ASSERT(...) (void(0)) +# define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -57871,14 +74301,13 @@ template #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -58005,22 +74434,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -58031,6 +74460,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -58161,6 +74642,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -58360,7 +74864,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -58448,7 +74952,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -58490,7 +74994,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -58530,6 +75034,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -58545,10 +75081,10 @@ namespace entt { template class delegate; -template> +template> class basic_dispatcher; -template +template> class emitter; class connection; @@ -58558,12 +75094,26 @@ struct scoped_connection; template class sink; -template> +template> class sigh; /*! @brief Alias declaration for the most common use case. */ using dispatcher = basic_dispatcher<>; +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @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{}; + } // namespace entt #endif @@ -58579,22 +75129,22 @@ namespace entt { namespace internal { template -auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); template -auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); template -auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); template -auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); template -auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); template -using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); +using function_pointer_t = decltype(function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { @@ -58608,14 +75158,6 @@ template * @endcond */ -/*! @brief Used to wrap a function or a member of a specified type. */ -template -struct connect_arg_t {}; - -/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ -template -inline constexpr connect_arg_t connect_arg{}; - /** * @brief Basic delegate implementation. * @@ -58640,7 +75182,7 @@ class delegate; template class delegate { template - [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + [[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))...)); @@ -58648,7 +75190,7 @@ class delegate { } template - [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + [[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)); @@ -58657,7 +75199,7 @@ class delegate { } template - [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + [[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)); @@ -58674,30 +75216,19 @@ public: using result_type = Ret; /*! @brief Default constructor. */ - delegate() ENTT_NOEXCEPT + delegate() noexcept : instance{nullptr}, fn{nullptr} {} /** - * @brief Constructs a delegate and connects a free function or an unbound - * member. + * @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) ENTT_NOEXCEPT { - connect(); - } - - /** - * @brief Constructs a delegate and connects a free function with payload or - * a bound member. - * @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 object that fits the purpose. - */ - template - delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { - connect(std::forward(value_or_instance)); + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); } /** @@ -58706,7 +75237,7 @@ public: * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ - delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + delegate(function_type *function, const void *payload = nullptr) noexcept { connect(function, payload); } @@ -58715,7 +75246,7 @@ public: * @tparam Candidate Function or member to connect to the delegate. */ template - void connect() ENTT_NOEXCEPT { + void connect() noexcept { instance = nullptr; if constexpr(std::is_invocable_r_v) { @@ -58745,7 +75276,7 @@ public: * @param value_or_instance A valid reference that fits the purpose. */ template - void connect(Type &value_or_instance) ENTT_NOEXCEPT { + void connect(Type &value_or_instance) noexcept { instance = &value_or_instance; if constexpr(std::is_invocable_r_v) { @@ -58769,7 +75300,7 @@ public: * @param value_or_instance A valid pointer that fits the purpose. */ template - void connect(Type *value_or_instance) ENTT_NOEXCEPT { + void connect(Type *value_or_instance) noexcept { instance = value_or_instance; if constexpr(std::is_invocable_r_v) { @@ -58795,7 +75326,8 @@ public: * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ - void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); instance = payload; fn = function; } @@ -58805,7 +75337,7 @@ public: * * After a reset, a delegate cannot be invoked anymore. */ - void reset() ENTT_NOEXCEPT { + void reset() noexcept { instance = nullptr; fn = nullptr; } @@ -58814,7 +75346,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return instance; } @@ -58839,7 +75371,7 @@ public: * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { // no need to also test instance return !(fn == nullptr); } @@ -58849,7 +75381,7 @@ public: * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ - [[nodiscard]] bool operator==(const delegate &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const delegate &other) const noexcept { return fn == other.fn && instance == other.instance; } @@ -58867,7 +75399,7 @@ private: * @return True if the two contents differ, false otherwise. */ template -[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } @@ -58902,19 +75434,16 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate #include +#include #include #include #include #include -// #include "../config/config.h" - // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP -#include #include #include #include @@ -58944,7 +75473,7 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate delegate delegate -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -59034,6 +75568,14 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate #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 @@ -59053,7 +75595,7 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate delegate delegate -# define ENTT_ASSERT(condition, ...) assert(condition) +# 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 #ifdef ENTT_NO_ETO -# define ENTT_IGNORE_IF_EMPTY false +# define ENTT_ETO_TYPE(Type) void #else -# define ENTT_IGNORE_IF_EMPTY true +# define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP @@ -59135,28 +75682,17 @@ delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate -#include -#include -#include -// #include "../config/config.h" - // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP -#include -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -59283,22 +75819,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -59309,6 +75845,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -59439,6 +76027,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -59638,7 +76249,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -59726,7 +76337,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -59768,7 +76379,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -59808,6 +76419,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -59828,22 +76471,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -59858,22 +76501,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -59916,7 +76559,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -59924,13 +76567,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -59940,7 +76583,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -59952,7 +76595,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -59961,25 +76604,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -59987,12 +76630,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -60000,7 +76643,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -60013,7 +76656,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -60024,7 +76667,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -60050,7 +76693,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -60090,9 +76733,8 @@ struct tuple_element>: conditional #include +#include #include -// #include "../config/config.h" - namespace entt { @@ -60102,46 +76744,124 @@ namespace entt { */ template struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; - - /*! @brief Default copy constructor, deleted on purpose. */ - input_iterator_pointer(const input_iterator_pointer &) = delete; - - /*! @brief Default move constructor. */ - input_iterator_pointer(input_iterator_pointer &&) = default; + /*! @brief Reference type. */ + using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ - input_iterator_pointer(Type &&val) + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This proxy object. - */ - input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; - - /** - * @brief Default move assignment operator. - * @return This proxy object. - */ - input_iterator_pointer &operator=(input_iterator_pointer &&) = default; - /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ - [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + private: Type value; }; +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. @@ -60157,22 +76877,24 @@ struct iterable_adaptor final { using sentinel = Sentinel; /*! @brief Default constructor. */ - iterable_adaptor() = default; + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ - iterable_adaptor(iterator from, sentinel to) - : first{from}, - last{to} {} + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator begin() const noexcept { return first; } @@ -60181,17 +76903,17 @@ struct iterable_adaptor final { * @return An iterator to the element following the last element of the * range. */ - [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ - [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ - [[nodiscard]] sentinel cend() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } @@ -60219,69 +76941,12 @@ private: namespace entt { -/** - * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). - * @tparam Type Pointer type. - * @param ptr Fancy or raw pointer. - * @return A raw pointer that represents the address of the original pointer. - */ -template -[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT { - if constexpr(std::is_pointer_v>>) { - return ptr; - } else { - return to_address(std::forward(ptr).operator->()); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { - lhs = rhs; - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { - lhs = std::move(rhs); - } -} - -/** - * @brief Utility function to design allocation-aware containers. - * @tparam Allocator Type of allocator. - * @param lhs A valid allocator. - * @param rhs Another valid allocator. - */ -template -constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); - - if constexpr(std::allocator_traits::propagate_on_container_swap::value) { - using std::swap; - swap(lhs, rhs); - } -} - /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ -[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } @@ -60290,8 +76955,8 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ -[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT { - ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { @@ -60307,11 +76972,68 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ -[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT { - ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. @@ -60327,14 +77049,14 @@ struct allocation_deleter: private Allocator { * @brief Inherited constructors. * @param alloc The allocator to use. */ - allocation_deleter(const allocator_type &alloc) + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ - void operator()(pointer ptr) { + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); @@ -60351,7 +77073,7 @@ struct allocation_deleter: private Allocator { * @return A properly initialized unique pointer with a custom deleter. */ template -auto allocate_unique(Allocator &allocator, Args &&...args) { +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; @@ -60381,14 +77103,14 @@ namespace internal { template struct uses_allocator_construction { template - static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT { + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { - return std::tuple(std::allocator_arg, allocator, std::forward(params)...); + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); @@ -60402,7 +77124,7 @@ struct uses_allocator_construction> { using type = std::pair; template - static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), @@ -60410,22 +77132,22 @@ struct uses_allocator_construction> { } template - static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template - static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template - static constexpr auto args(const Allocator &allocator, const std::pair &value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template - static constexpr auto args(const Allocator &allocator, std::pair &&value) ENTT_NOEXCEPT { + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; @@ -60451,7 +77173,7 @@ struct uses_allocator_construction> { * @return The arguments needed to create an object of the given type. */ template -constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT { +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } @@ -60489,7 +77211,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { - return std::apply([&](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt @@ -60622,22 +77344,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -60648,6 +77370,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -60778,6 +77552,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -60977,7 +77774,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -61065,7 +77862,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -61107,7 +77904,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -61147,6 +77944,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -61232,109 +78061,109 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_iterator() ENTT_NOEXCEPT + constexpr dense_map_iterator() noexcept : it{} {} - dense_map_iterator(const It iter) ENTT_NOEXCEPT + constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> - dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} - dense_map_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } - dense_map_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } - dense_map_iterator &operator--() ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } - dense_map_iterator operator--(int) ENTT_NOEXCEPT { + constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } - dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } - dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } - dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } - dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } - [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template - friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template - friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template -[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template -[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template -[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template -[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template -[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template -[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template -[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } @@ -61353,37 +78182,37 @@ public: using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; - dense_map_local_iterator() ENTT_NOEXCEPT + constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} - dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> - dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} - dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } - dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } - [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } - [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } @@ -61393,12 +78222,12 @@ private: }; template -[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template -[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } @@ -61434,7 +78263,7 @@ class dense_map { using packed_container_type = std::vector>; template - [[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT { + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(sparse.second()(key), bucket_count()); } @@ -61493,8 +78322,8 @@ class dense_map { void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { - packed.first()[pos] = std::move(packed.first().back()); size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } @@ -61534,7 +78363,7 @@ public: /*! @brief Default constructor. */ dense_map() - : dense_map(minimum_capacity) {} + : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. @@ -61546,35 +78375,35 @@ public: /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const allocator_type &allocator) - : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ - dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) - : dense_map{bucket_count, hash, key_equal{}, allocator} {} + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. - * @param bucket_count Minimal number of buckets. + * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ - explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { - rehash(bucket_count); + rehash(cnt); } /*! @brief Default copy constructor. */ @@ -61591,7 +78420,7 @@ public: threshold{other.threshold} {} /*! @brief Default move constructor. */ - dense_map(dense_map &&) = default; + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. @@ -61613,13 +78442,13 @@ public: * @brief Default move assignment operator. * @return This container. */ - dense_map &operator=(dense_map &&) = default; + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } @@ -61631,17 +78460,17 @@ public: * * @return An iterator to the first instance of the internal array. */ - [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ - [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ - [[nodiscard]] iterator begin() ENTT_NOEXCEPT { + [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } @@ -61655,17 +78484,17 @@ public: * @return An iterator to the element following the last instance of the * internal array. */ - [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ - [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ - [[nodiscard]] iterator end() ENTT_NOEXCEPT { + [[nodiscard]] iterator end() noexcept { return packed.first().end(); } @@ -61673,7 +78502,7 @@ public: * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } @@ -61681,12 +78510,20 @@ public: * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + /*! @brief Clears the container. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); @@ -61902,6 +78739,27 @@ public: return insert_or_do_nothing(std::move(key)).first->second; } + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. @@ -61919,7 +78777,7 @@ public: /** * @brief Finds an element with a key that compares _equivalent_ to a given - * value. + * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element @@ -61938,6 +78796,46 @@ public: return constrained_find(key, key_to_bucket(key)); } + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. @@ -62001,7 +78899,7 @@ public: * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ - [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const { + [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } @@ -62077,15 +78975,19 @@ public: /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. - * @param count New number of buckets. + * @param cnt New number of buckets. */ - void rehash(const size_type count) { - auto value = (std::max)(count, minimum_capacity); - value = (std::max)(value, static_cast(size() / max_load_factor())); + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); - std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits::max)()); + + for(auto &&elem: sparse.first()) { + elem = std::numeric_limits::max(); + } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); @@ -62097,11 +78999,11 @@ public: /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. - * @param count New number of elements. + * @param cnt New number of elements. */ - void reserve(const size_type count) { - packed.first().reserve(count); - rehash(static_cast(std::ceil(count / max_load_factor()))); + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** @@ -62156,8 +79058,6 @@ struct uses_allocator, Allocator> #include #include #include -// #include "../config/config.h" - // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP @@ -62284,22 +79184,22 @@ struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. - * @tparam Type First type provided by the 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> +template +struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. - * @tparam Type First type provided by the 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> { +template +struct type_list_element<0u, type_list> { /*! @brief Searched type. */ - using type = Type; + using type = First; }; /** @@ -62310,6 +79210,58 @@ struct type_list_element<0u, type_list> { template using type_list_element_t = typename type_list_element::type; +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @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 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 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 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 Concatenates multiple type lists. * @tparam Type Types provided by the first type list. @@ -62440,6 +79392,29 @@ struct type_list_diff, type_list> { template using type_list_diff_t = typename type_list_diff::type; +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @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 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 A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. @@ -62639,7 +79614,7 @@ struct has_iterator_category -struct is_iterator>, void>>> +struct is_iterator>, void>>> : internal::has_iterator_category {}; /** @@ -62727,7 +79702,7 @@ template } template -[[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { +[[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 { @@ -62769,7 +79744,7 @@ struct constness_as { template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ - using type = std::add_const_t; + using type = const To; }; /** @@ -62809,6 +79784,38 @@ public: template using member_class_t = typename member_class::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...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t; +}; + +/** + * @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; + } // namespace entt #endif @@ -62829,22 +79836,22 @@ struct compressed_pair_element { using const_reference = const Type &; template>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : value{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return value; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return value; } @@ -62859,22 +79866,22 @@ struct compressed_pair_element>> - compressed_pair_element() + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} - template>, compressed_pair_element>>> - compressed_pair_element(Args &&args) - : base_type{std::forward(args)} {} + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} template - compressed_pair_element(std::tuple args, std::index_sequence) + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} - [[nodiscard]] reference get() ENTT_NOEXCEPT { + [[nodiscard]] constexpr reference get() noexcept { return *this; } - [[nodiscard]] const_reference get() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; @@ -62917,7 +79924,7 @@ public: * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> - constexpr compressed_pair() + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} @@ -62925,13 +79932,13 @@ public: * @brief Copy constructor. * @param other The instance to copy from. */ - constexpr compressed_pair(const compressed_pair &other) = default; + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ - constexpr compressed_pair(compressed_pair &&other) = default; + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. @@ -62941,7 +79948,7 @@ public: * @param other Value to use to initialize the second element. */ template - constexpr compressed_pair(Arg &&arg, Other &&other) + 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)} {} @@ -62953,7 +79960,7 @@ public: * @param other Arguments to use to initialize the second element. */ template - constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) + 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{}} {} @@ -62962,25 +79969,25 @@ public: * @param other The instance to copy from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(const compressed_pair &other) = default; + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ - constexpr compressed_pair &operator=(compressed_pair &&other) = default; + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ - [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ - [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } @@ -62988,12 +79995,12 @@ public: * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ - [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ - [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } @@ -63001,7 +80008,7 @@ public: * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ - void swap(compressed_pair &other) { + 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()); @@ -63014,7 +80021,7 @@ public: * reference to the second element if `Index` is 1. */ template - decltype(auto) get() ENTT_NOEXCEPT { + constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -63025,7 +80032,7 @@ public: /*! @copydoc get */ template - decltype(auto) get() const ENTT_NOEXCEPT { + constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { @@ -63051,7 +80058,7 @@ compressed_pair(Type &&, Other &&) -> compressed_pair, std::d * @param rhs A valid compressed pair object. */ template -inline void swap(compressed_pair &lhs, compressed_pair &rhs) { +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } @@ -63089,14 +80096,13 @@ struct tuple_element>: conditional -#include +#include // #include "../config/config.h" namespace entt { -template)> +template class basic_any; /*! @brief Alias declaration for type identifiers. */ @@ -63158,8 +80164,6 @@ using any = basic_any<>; #include #include -// #include "../config/config.h" - // #include "fwd.hpp" @@ -63229,12 +80233,14 @@ class basic_hashed_string: internal::basic_hashed_string { struct const_wrapper { // non-explicit constructor on purpose - constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {} + 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { @@ -63245,7 +80251,7 @@ 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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { @@ -63269,7 +80275,7 @@ public: * @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) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } @@ -63280,7 +80286,7 @@ public: * @return The numeric representation of the string. */ template - [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } @@ -63289,12 +80295,12 @@ public: * @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) ENTT_NOEXCEPT { + [[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() ENTT_NOEXCEPT + constexpr basic_hashed_string() noexcept : base_type{} {} /** @@ -63302,7 +80308,7 @@ public: * @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) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** @@ -63311,7 +80317,7 @@ public: * @param str Human-readable identifier. */ template - constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** @@ -63323,14 +80329,14 @@ public: * * @param wrapper Helps achieving the purpose by relying on overloading. */ - explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT + 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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } @@ -63338,7 +80344,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } @@ -63346,12 +80352,12 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ - [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } @@ -63359,7 +80365,7 @@ public: * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ - [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; @@ -63390,7 +80396,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; * @return True if the two hashed strings are identical, false otherwise. */ template -[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } @@ -63402,7 +80408,7 @@ template * @return True if the two hashed strings differ, false otherwise. */ template -[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } @@ -63414,7 +80420,7 @@ template * @return True if the first element is less than the second, false otherwise. */ template -[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } @@ -63427,7 +80433,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } @@ -63440,7 +80446,7 @@ template * otherwise. */ template -[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } @@ -63453,7 +80459,7 @@ template * false otherwise. */ template -[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } @@ -63470,7 +80476,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } @@ -63479,7 +80485,7 @@ inline namespace literals { * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } @@ -63500,14 +80506,14 @@ namespace entt { namespace internal { struct ENTT_API type_index final { - [[nodiscard]] static id_type next() ENTT_NOEXCEPT { + [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template -[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +[[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); @@ -63519,26 +80525,26 @@ template } template().find_first_of('.')> -[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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) ENTT_NOEXCEPT { +[[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()); @@ -63562,13 +80568,13 @@ 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() ENTT_NOEXCEPT { + [[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 ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -63584,16 +80590,16 @@ struct type_hash final { * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else - [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ - [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; @@ -63608,12 +80614,12 @@ 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() ENTT_NOEXCEPT { + [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ - [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; @@ -63625,7 +80631,7 @@ struct type_info final { * @tparam Type Type for which to construct a type info object. */ template - constexpr type_info(std::in_place_type_t) ENTT_NOEXCEPT + constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} @@ -63634,7 +80640,7 @@ struct type_info final { * @brief Type index. * @return Type index. */ - [[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type index() const noexcept { return seq; } @@ -63642,7 +80648,7 @@ struct type_info final { * @brief Type hash. * @return Type hash. */ - [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } @@ -63650,7 +80656,7 @@ struct type_info final { * @brief Type name. * @return Type name. */ - [[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } @@ -63666,7 +80672,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } @@ -63676,7 +80682,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } @@ -63686,7 +80692,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } @@ -63697,7 +80703,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } @@ -63708,7 +80714,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } @@ -63719,7 +80725,7 @@ private: * @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) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } @@ -63735,7 +80741,7 @@ private: * @return A reference to a properly initialized type info object. */ template -[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; @@ -63746,7 +80752,7 @@ template /*! @copydoc type_id */ template -[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT { +[[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } @@ -63758,9 +80764,8 @@ template #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP +#include #include -// #include "../config/config.h" - namespace entt { @@ -63776,7 +80781,7 @@ struct identity { * @return The submitted value as-is. */ template - [[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; @@ -63789,7 +80794,7 @@ struct identity { * @return Pointer to the member. */ template -[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } @@ -63800,7 +80805,7 @@ template * @return Pointer to the function. */ template -[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { +[[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } @@ -63830,7 +80835,7 @@ struct y_combinator { * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ - y_combinator(Func recursive) + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : func{std::move(recursive)} {} /** @@ -63840,13 +80845,13 @@ struct y_combinator { * @return Return value of the underlying function, if any. */ template - decltype(auto) operator()(Args &&...args) const { + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template - decltype(auto) operator()(Args &&...args) { + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } @@ -63869,8 +80874,6 @@ private: #include #include #include -// #include "../config/config.h" - // #include "delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP @@ -63897,22 +80900,22 @@ namespace entt { namespace internal { template -auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); template -auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); template -auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); template -auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); template -auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); template -using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); +using function_pointer_t = decltype(function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { @@ -63926,14 +80929,6 @@ template * @endcond */ -/*! @brief Used to wrap a function or a member of a specified type. */ -template -struct connect_arg_t {}; - -/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ -template -inline constexpr connect_arg_t connect_arg{}; - /** * @brief Basic delegate implementation. * @@ -63958,7 +80953,7 @@ class delegate; template class delegate { template - [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + [[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))...)); @@ -63966,7 +80961,7 @@ class delegate { } template - [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + [[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)); @@ -63975,7 +80970,7 @@ class delegate { } template - [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + [[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)); @@ -63992,30 +80987,19 @@ public: using result_type = Ret; /*! @brief Default constructor. */ - delegate() ENTT_NOEXCEPT + delegate() noexcept : instance{nullptr}, fn{nullptr} {} /** - * @brief Constructs a delegate and connects a free function or an unbound - * member. + * @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) ENTT_NOEXCEPT { - connect(); - } - - /** - * @brief Constructs a delegate and connects a free function with payload or - * a bound member. - * @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 object that fits the purpose. - */ - template - delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { - connect(std::forward(value_or_instance)); + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); } /** @@ -64024,7 +81008,7 @@ public: * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ - delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + delegate(function_type *function, const void *payload = nullptr) noexcept { connect(function, payload); } @@ -64033,7 +81017,7 @@ public: * @tparam Candidate Function or member to connect to the delegate. */ template - void connect() ENTT_NOEXCEPT { + void connect() noexcept { instance = nullptr; if constexpr(std::is_invocable_r_v) { @@ -64063,7 +81047,7 @@ public: * @param value_or_instance A valid reference that fits the purpose. */ template - void connect(Type &value_or_instance) ENTT_NOEXCEPT { + void connect(Type &value_or_instance) noexcept { instance = &value_or_instance; if constexpr(std::is_invocable_r_v) { @@ -64087,7 +81071,7 @@ public: * @param value_or_instance A valid pointer that fits the purpose. */ template - void connect(Type *value_or_instance) ENTT_NOEXCEPT { + void connect(Type *value_or_instance) noexcept { instance = value_or_instance; if constexpr(std::is_invocable_r_v) { @@ -64113,7 +81097,8 @@ public: * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ - void connect(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); instance = payload; fn = function; } @@ -64123,7 +81108,7 @@ public: * * After a reset, a delegate cannot be invoked anymore. */ - void reset() ENTT_NOEXCEPT { + void reset() noexcept { instance = nullptr; fn = nullptr; } @@ -64132,7 +81117,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] const void *data() const noexcept { return instance; } @@ -64157,7 +81142,7 @@ public: * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { // no need to also test instance return !(fn == nullptr); } @@ -64167,7 +81152,7 @@ public: * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ - [[nodiscard]] bool operator==(const delegate &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator==(const delegate &other) const noexcept { return fn == other.fn && instance == other.instance; } @@ -64185,7 +81170,7 @@ private: * @return True if the two contents differ, false otherwise. */ template -[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } @@ -64266,7 +81251,6 @@ class sigh { friend class sink>; using alloc_traits = std::allocator_traits; - static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; public: @@ -64278,21 +81262,21 @@ public: using sink_type = sink>; /*! @brief Default constructor. */ - sigh() + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) : sigh{allocator_type{}} {} /** * @brief Constructs a signal handler with a given allocator. * @param allocator The allocator to use. */ - explicit sigh(const allocator_type &allocator) + 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) + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) : calls{other.calls} {} /** @@ -64300,14 +81284,14 @@ public: * @param other The instance to copy from. * @param allocator The allocator to use. */ - sigh(const sigh &other, const allocator_type &allocator) + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{other.calls, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ - sigh(sigh &&other) ENTT_NOEXCEPT + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) : calls{std::move(other.calls)} {} /** @@ -64315,7 +81299,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{std::move(other.calls), allocator} {} /** @@ -64323,7 +81307,7 @@ public: * @param other The instance to copy from. * @return This signal handler. */ - sigh &operator=(const sigh &other) { + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { calls = other.calls; return *this; } @@ -64333,7 +81317,7 @@ public: * @param other The instance to move from. * @return This signal handler. */ - sigh &operator=(sigh &&other) ENTT_NOEXCEPT { + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { calls = std::move(other.calls); return *this; } @@ -64342,7 +81326,7 @@ public: * @brief Exchanges the contents with those of a given signal handler. * @param other Signal handler to exchange the content with. */ - void swap(sigh &other) { + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { using std::swap; swap(calls, other.calls); } @@ -64351,22 +81335,15 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return calls.get_allocator(); } - /** - * @brief Instance type when it comes to connecting member functions. - * @tparam Class Type of class to which the member function belongs. - */ - template - using instance_type = Class *; - /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return calls.size(); } @@ -64374,7 +81351,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return calls.empty(); } @@ -64411,14 +81388,18 @@ public: if constexpr(std::is_void_v) { if constexpr(std::is_invocable_r_v) { call(args...); - if(func()) { break; } + if(func()) { + break; + } } else { call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { - if(func(call(args...))) { break; } + if(func(call(args...))) { + break; + } } else { func(call(args...)); } @@ -64447,13 +81428,15 @@ class connection { public: /*! @brief Default constructor. */ - connection() = default; + connection() + : disconnect{}, + signal{} {} /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(disconnect); } @@ -64467,7 +81450,7 @@ public: private: delegate disconnect; - void *signal{}; + void *signal; }; /** @@ -64497,7 +81480,7 @@ struct scoped_connection { * @brief Move constructor. * @param other The scoped connection to move from. */ - scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT + scoped_connection(scoped_connection &&other) noexcept : conn{std::exchange(other.conn, {})} {} /*! @brief Automatically breaks the link on destruction. */ @@ -64516,7 +81499,7 @@ struct scoped_connection { * @param other The scoped connection to move from. * @return This scoped connection. */ - scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT { + scoped_connection &operator=(scoped_connection &&other) noexcept { conn = std::exchange(other.conn, {}); return *this; } @@ -64535,7 +81518,7 @@ struct scoped_connection { * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(conn); } @@ -64582,12 +81565,21 @@ 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; + } + 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) ENTT_NOEXCEPT + sink(sigh &ref) noexcept : offset{}, signal{&ref} {} @@ -64595,7 +81587,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return signal->calls.empty(); } @@ -64609,13 +81601,7 @@ public: [[nodiscard]] sink before() { delegate call{}; call.template connect(); - - 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; + return before(std::move(call)); } /** @@ -64630,13 +81616,7 @@ public: [[nodiscard]] sink before(Type &&value_or_instance) { delegate call{}; call.template connect(value_or_instance); - - 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; + return before(std::move(call)); } /** @@ -64646,7 +81626,7 @@ public: * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ - template + template>, void>, sink>> [[nodiscard]] sink before(Type &value_or_instance) { return before(&value_or_instance); } @@ -64654,12 +81634,10 @@ public: /** * @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 pointer that fits the purpose. * @return A properly initialized sink object. */ - template - [[nodiscard]] sink before(Type *value_or_instance) { + [[nodiscard]] sink before(const void *value_or_instance) { sink other{*this}; if(value_or_instance) { @@ -64685,81 +81663,47 @@ public: } /** - * @brief Connects a free function or an unbound member to a signal. + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. * - * The signal handler performs checks to avoid multiple connections for the - * same function. - * - * @tparam Candidate Function or member to connect to the signal. - * @return A properly initialized connection object. - */ - template - connection connect() { - disconnect(); - - delegate call{}; - call.template connect(); - signal->calls.insert(signal->calls.end() - offset, std::move(call)); - - delegate conn{}; - conn.template connect<&release>(); - return {std::move(conn), signal}; - } - - /** - * @brief Connects a free function with payload or a bound member to a - * signal. - * - * The signal 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 signal. On the other side, the signal handler performs + * 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. - * @param value_or_instance A valid object that fits the purpose. + * @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); + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); delegate call{}; - call.template connect(value_or_instance); + call.template connect(value_or_instance...); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; - conn.template connect<&release>(value_or_instance); + conn.template connect<&release>(value_or_instance...); return {std::move(conn), signal}; } /** - * @brief Disconnects a free function or an unbound member from a signal. + * @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() { + template + void disconnect(Type &&...value_or_instance) { auto &calls = signal->calls; delegate call{}; - call.template connect(); - calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); - } - - /** - * @brief Disconnects a free function with payload or a bound member from a - * signal. - * @tparam Candidate Function or member to disconnect from the signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - */ - template - void disconnect(Type &&value_or_instance) { - auto &calls = signal->calls; - delegate call{}; - call.template connect(value_or_instance); + call.template connect(value_or_instance...); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } @@ -64769,7 +81713,7 @@ public: * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ - template + template>, void>>> void disconnect(Type &value_or_instance) { disconnect(&value_or_instance); } @@ -64777,11 +81721,9 @@ public: /** * @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 disconnect(Type *value_or_instance) { + 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; }; @@ -64830,17 +81772,17 @@ struct basic_dispatcher_handler { virtual ~basic_dispatcher_handler() = default; virtual void publish() = 0; virtual void disconnect(void *) = 0; - virtual void clear() ENTT_NOEXCEPT = 0; - virtual std::size_t size() const ENTT_NOEXCEPT = 0; + virtual void clear() noexcept = 0; + virtual std::size_t size() const noexcept = 0; }; -template +template class dispatcher_handler final: public basic_dispatcher_handler { - static_assert(std::is_same_v>, "Invalid event type"); + static_assert(std::is_same_v>, "Invalid type"); using alloc_traits = std::allocator_traits; - using signal_type = sigh>; - using container_type = std::vector>; + using signal_type = sigh; + using container_type = std::vector>; public: using allocator_type = Allocator; @@ -64863,29 +81805,28 @@ public: bucket().disconnect(instance); } - void clear() ENTT_NOEXCEPT override { + void clear() noexcept override { events.clear(); } - [[nodiscard]] auto bucket() ENTT_NOEXCEPT { - using sink_type = typename sigh::sink_type; - return sink_type{signal}; + [[nodiscard]] auto bucket() noexcept { + return typename signal_type::sink_type{signal}; } - void trigger(Event event) { + void trigger(Type event) { signal.publish(event); } template void enqueue(Args &&...args) { - if constexpr(std::is_aggregate_v) { - events.push_back(Event{std::forward(args)...}); + if constexpr(std::is_aggregate_v) { + events.push_back(Type{std::forward(args)...}); } else { events.emplace_back(std::forward(args)...); } } - std::size_t size() const ENTT_NOEXCEPT override { + std::size_t size() const noexcept override { return events.size(); } @@ -64907,8 +81848,8 @@ private: * A dispatcher can be used either to trigger an immediate event or to enqueue * events to be published all together once per tick.
* Listeners are provided in the form of member functions. For each event of - * type `Event`, listeners are such that they can be invoked with an argument of - * type `Event &`, no matter what the return type is. + * type `Type`, listeners are such that they can be invoked with an argument of + * type `Type &`, no matter what the return type is. * * The dispatcher creates instances of the `sigh` class internally. Refer to the * documentation of the latter for more details. @@ -64917,35 +81858,36 @@ private: */ template class basic_dispatcher { - template - using handler_type = internal::dispatcher_handler; + template + using handler_type = internal::dispatcher_handler; using key_type = id_type; - // std::shared_ptr because of its type erased allocator which is pretty useful here + // std::shared_ptr because of its type erased allocator which is useful here using mapped_type = std::shared_ptr; using alloc_traits = std::allocator_traits; using container_allocator = typename alloc_traits::template rebind_alloc>; - using container_type = dense_map, container_allocator>; + using container_type = dense_map, container_allocator>; - template - [[nodiscard]] handler_type &assure(const id_type id) { + template + [[nodiscard]] handler_type &assure(const id_type id) { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); auto &&ptr = pools.first()[id]; if(!ptr) { - const auto &allocator = pools.second(); - ptr = std::allocate_shared>(allocator, allocator); + const auto &allocator = get_allocator(); + ptr = std::allocate_shared>(allocator, allocator); } - return static_cast &>(*ptr); + return static_cast &>(*ptr); } - template - [[nodiscard]] const handler_type *assure(const id_type id) const { - auto &container = pools.first(); + template + [[nodiscard]] const handler_type *assure(const id_type id) const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); - if(const auto it = container.find(id); it != container.end()) { - return static_cast *>(it->second.get()); + if(auto it = pools.first().find(id); it != pools.first().cend()) { + return static_cast *>(it->second.get()); } return nullptr; @@ -64972,7 +81914,7 @@ public: * @brief Move constructor. * @param other The instance to move from. */ - basic_dispatcher(basic_dispatcher &&other) ENTT_NOEXCEPT + basic_dispatcher(basic_dispatcher &&other) noexcept : pools{std::move(other.pools)} {} /** @@ -64980,7 +81922,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) ENTT_NOEXCEPT + basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {} /** @@ -64988,7 +81930,7 @@ public: * @param other The instance to move from. * @return This dispatcher. */ - basic_dispatcher &operator=(basic_dispatcher &&other) ENTT_NOEXCEPT { + basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { pools = std::move(other.pools); return *this; } @@ -65006,19 +81948,19 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return pools.second(); } /** * @brief Returns the number of pending events for a given type. - * @tparam Event Type of event for which to return the count. + * @tparam Type Type of event for which to return the count. * @param id Name used to map the event queue within the dispatcher. * @return The number of pending events for the given type. */ - template - size_type size(const id_type id = type_hash::value()) const ENTT_NOEXCEPT { - const auto *cpool = assure(id); + template + size_type size(const id_type id = type_hash::value()) const noexcept { + const auto *cpool = assure>(id); return cpool ? cpool->size() : 0u; } @@ -65026,7 +81968,7 @@ public: * @brief Returns the total number of pending events. * @return The total number of pending events. */ - size_type size() const ENTT_NOEXCEPT { + size_type size() const noexcept { size_type count{}; for(auto &&cpool: pools.first()) { @@ -65042,86 +81984,87 @@ public: * A sink is an opaque object used to connect listeners to events. * * The function type for a listener is _compatible_ with: + * * @code{.cpp} - * void(Event &); + * void(Type &); * @endcode * * The order of invocation of the listeners isn't guaranteed. * * @sa sink * - * @tparam Event Type of event of which to get the sink. + * @tparam Type Type of event of which to get the sink. * @param id Name used to map the event queue within the dispatcher. * @return A temporary sink object. */ - template - [[nodiscard]] auto sink(const id_type id = type_hash::value()) { - return assure(id).bucket(); + template + [[nodiscard]] auto sink(const id_type id = type_hash::value()) { + return assure(id).bucket(); } /** * @brief Triggers an immediate event of a given type. - * @tparam Event Type of event to trigger. - * @param event An instance of the given type of event. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. */ - template - void trigger(Event &&event = {}) { - trigger(type_hash>::value(), std::forward(event)); + template + void trigger(Type &&value = {}) { + trigger(type_hash>::value(), std::forward(value)); } /** * @brief Triggers an immediate event on a queue of a given type. - * @tparam Event Type of event to trigger. - * @param event An instance of the given type of event. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. * @param id Name used to map the event queue within the dispatcher. */ - template - void trigger(const id_type id, Event &&event = {}) { - assure>(id).trigger(std::forward(event)); + template + void trigger(const id_type id, Type &&value = {}) { + assure>(id).trigger(std::forward(value)); } /** * @brief Enqueues an event of the given type. - * @tparam Event Type of event to enqueue. + * @tparam Type Type of event to enqueue. * @tparam Args Types of arguments to use to construct the event. * @param args Arguments to use to construct the event. */ - template + template void enqueue(Args &&...args) { - enqueue_hint(type_hash::value(), std::forward(args)...); + enqueue_hint(type_hash::value(), std::forward(args)...); } /** * @brief Enqueues an event of the given type. - * @tparam Event Type of event to enqueue. - * @param event An instance of the given type of event. + * @tparam Type Type of event to enqueue. + * @param value An instance of the given type of event. */ - template - void enqueue(Event &&event) { - enqueue_hint(type_hash>::value(), std::forward(event)); + template + void enqueue(Type &&value) { + enqueue_hint(type_hash>::value(), std::forward(value)); } /** * @brief Enqueues an event of the given type. - * @tparam Event Type of event to enqueue. + * @tparam Type Type of event to enqueue. * @tparam Args Types of arguments to use to construct the event. * @param id Name used to map the event queue within the dispatcher. * @param args Arguments to use to construct the event. */ - template + template void enqueue_hint(const id_type id, Args &&...args) { - assure(id).enqueue(std::forward(args)...); + assure(id).enqueue(std::forward(args)...); } /** * @brief Enqueues an event of the given type. - * @tparam Event Type of event to enqueue. + * @tparam Type Type of event to enqueue. * @param id Name used to map the event queue within the dispatcher. - * @param event An instance of the given type of event. + * @param value An instance of the given type of event. */ - template - void enqueue_hint(const id_type id, Event &&event) { - assure>(id).enqueue(std::forward(event)); + template + void enqueue_hint(const id_type id, Type &&value) { + assure>(id).enqueue(std::forward(value)); } /** @@ -65150,16 +82093,16 @@ public: /** * @brief Discards all the events stored so far in a given queue. - * @tparam Event Type of event to discard. + * @tparam Type Type of event to discard. * @param id Name used to map the event queue within the dispatcher. */ - template - void clear(const id_type id = type_hash::value()) { - assure(id).clear(); + template + void clear(const id_type id = type_hash::value()) { + assure(id).clear(); } /*! @brief Discards all the events queued so far. */ - void clear() ENTT_NOEXCEPT { + void clear() noexcept { for(auto &&cpool: pools.first()) { cpool.second->clear(); } @@ -65167,12 +82110,12 @@ public: /** * @brief Delivers all the pending events of a given queue. - * @tparam Event Type of event to send. + * @tparam Type Type of event to send. * @param id Name used to map the event queue within the dispatcher. */ - template - void update(const id_type id = type_hash::value()) { - assure(id).publish(); + template + void update(const id_type id = type_hash::value()) { + assure(id).publish(); } /*! @brief Delivers all the pending events. */ @@ -65194,17 +82137,13 @@ private: #ifndef ENTT_SIGNAL_EMITTER_HPP #define ENTT_SIGNAL_EMITTER_HPP -#include #include -#include -#include -#include #include #include -// #include "../config/config.h" - // #include "../container/dense_map.hpp" +// #include "../core/compressed_pair.hpp" + // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" @@ -65219,8 +82158,7 @@ namespace entt { /** * @brief General purpose event emitter. * - * The emitter class template follows the CRTP idiom. To create a custom emitter - * type, derived classes must inherit directly from the base class as: + * To create an emitter type, derived classes must inherit from the base as: * * @code{.cpp} * struct my_emitter: emitter { @@ -65228,287 +82166,145 @@ namespace entt { * } * @endcode * - * Pools for the type of events are created internally on the fly. It's not - * required to specify in advance the full list of accepted types.
- * Moreover, whenever an event is published, an emitter provides the listeners - * with a reference to itself along with a reference to the event. Therefore - * listeners have an handy way to work with it without incurring in the need of - * capturing a reference to the emitter. + * Handlers for the different events are created internally on the fly. It's not + * required to specify in advance the full list of accepted events.
+ * Moreover, whenever an event is published, an emitter also passes a reference + * to itself to its listeners. * - * @tparam Derived Actual type of emitter that extends the class template. + * @tparam Derived Emitter type. + * @tparam Allocator Type of allocator used to manage memory and elements. */ -template +template class emitter { - struct basic_pool { - virtual ~basic_pool() = default; - virtual bool empty() const ENTT_NOEXCEPT = 0; - virtual void clear() ENTT_NOEXCEPT = 0; - }; + using key_type = id_type; + using mapped_type = std::function; - template - struct pool_handler final: basic_pool { - static_assert(std::is_same_v>, "Invalid event type"); - - using listener_type = std::function; - using element_type = std::pair; - using container_type = std::list; - using connection_type = typename container_type::iterator; - - [[nodiscard]] bool empty() const ENTT_NOEXCEPT override { - auto pred = [](auto &&element) { return element.first; }; - - return std::all_of(once_list.cbegin(), once_list.cend(), pred) - && std::all_of(on_list.cbegin(), on_list.cend(), pred); - } - - void clear() ENTT_NOEXCEPT override { - if(publishing) { - for(auto &&element: once_list) { - element.first = true; - } - - for(auto &&element: on_list) { - element.first = true; - } - } else { - once_list.clear(); - on_list.clear(); - } - } - - connection_type once(listener_type listener) { - return once_list.emplace(once_list.cend(), false, std::move(listener)); - } - - connection_type on(listener_type listener) { - return on_list.emplace(on_list.cend(), false, std::move(listener)); - } - - void erase(connection_type conn) { - conn->first = true; - - if(!publishing) { - auto pred = [](auto &&element) { return element.first; }; - once_list.remove_if(pred); - on_list.remove_if(pred); - } - } - - void publish(Event &event, Derived &ref) { - container_type swap_list; - once_list.swap(swap_list); - - publishing = true; - - for(auto &&element: on_list) { - element.first ? void() : element.second(event, ref); - } - - for(auto &&element: swap_list) { - element.first ? void() : element.second(event, ref); - } - - publishing = false; - - on_list.remove_if([](auto &&element) { return element.first; }); - } - - private: - bool publishing{false}; - container_type once_list{}; - container_type on_list{}; - }; - - template - [[nodiscard]] pool_handler *assure() { - if(auto &&ptr = pools[type_hash::value()]; !ptr) { - auto *cpool = new pool_handler{}; - ptr.reset(cpool); - return cpool; - } else { - return static_cast *>(ptr.get()); - } - } - - template - [[nodiscard]] const pool_handler *assure() const { - const auto it = pools.find(type_hash::value()); - return (it == pools.cend()) ? nullptr : static_cast *>(it->second.get()); - } + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; public: - /** @brief Type of listeners accepted for the given event. */ - template - using listener = typename pool_handler::listener_type; - - /** - * @brief Generic connection type for events. - * - * Type of the connection object returned by the event emitter whenever a - * listener for the given type is registered.
- * It can be used to break connections still in use. - * - * @tparam Event Type of event for which the connection is created. - */ - template - struct connection: private pool_handler::connection_type { - /** @brief Event emitters are friend classes of connections. */ - friend class emitter; - - /*! @brief Default constructor. */ - connection() ENTT_NOEXCEPT = default; - - /** - * @brief Creates a connection that wraps its underlying instance. - * @param conn A connection object to wrap. - */ - connection(typename pool_handler::connection_type conn) - : pool_handler::connection_type{std::move(conn)} {} - }; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; /*! @brief Default constructor. */ - emitter() = default; + emitter() + : emitter{allocator_type{}} {} + + /** + * @brief Constructs an emitter with a given allocator. + * @param allocator The allocator to use. + */ + explicit emitter(const allocator_type &allocator) + : handlers{allocator, allocator} {} /*! @brief Default destructor. */ - virtual ~emitter() ENTT_NOEXCEPT { - static_assert(std::is_base_of_v, Derived>, "Incorrect use of the class template"); - } - - /*! @brief Default move constructor. */ - emitter(emitter &&) = default; - - /*! @brief Default move assignment operator. @return This emitter. */ - emitter &operator=(emitter &&) = default; - - /** - * @brief Emits the given event. - * - * All the listeners registered for the specific event type are invoked with - * the given event. The event type must either have a proper constructor for - * the arguments provided or be an aggregate type. - * - * @tparam Event Type of event to publish. - * @tparam Args Types of arguments to use to construct the event. - * @param args Parameters to use to initialize the event. - */ - template - void publish(Args &&...args) { - Event instance{std::forward(args)...}; - assure()->publish(instance, *static_cast(this)); + virtual ~emitter() noexcept { + static_assert(std::is_base_of_v, Derived>, "Invalid emitter type"); } /** - * @brief Registers a long-lived listener with the event emitter. - * - * This method can be used to register a listener designed to be invoked - * more than once for the given event type.
- * The connection returned by the method can be freely discarded. It's meant - * to be used later to disconnect the listener if required. - * - * The listener is as a callable object that can be moved and the type of - * which is _compatible_ with `void(Event &, Derived &)`. - * - * @note - * Whenever an event is emitted, the emitter provides the listener with a - * reference to the derived class. Listeners don't have to capture those - * instances for later uses. - * - * @tparam Event Type of event to which to connect the listener. - * @param instance The listener to register. - * @return Connection object that can be used to disconnect the listener. + * @brief Move constructor. + * @param other The instance to move from. */ - template - connection on(listener instance) { - return assure()->on(std::move(instance)); + emitter(emitter &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + emitter(emitter &&other, const allocator_type &allocator) noexcept + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ + emitter &operator=(emitter &&other) noexcept { + handlers = std::move(other.handlers); + return *this; } /** - * @brief Registers a short-lived listener with the event emitter. - * - * This method can be used to register a listener designed to be invoked - * only once for the given event type.
- * The connection returned by the method can be freely discarded. It's meant - * to be used later to disconnect the listener if required. - * - * The listener is as a callable object that can be moved and the type of - * which is _compatible_ with `void(Event &, Derived &)`. - * - * @note - * Whenever an event is emitted, the emitter provides the listener with a - * reference to the derived class. Listeners don't have to capture those - * instances for later uses. - * - * @tparam Event Type of event to which to connect the listener. - * @param instance The listener to register. - * @return Connection object that can be used to disconnect the listener. + * @brief Exchanges the contents with those of a given emitter. + * @param other Emitter to exchange the content with. */ - template - connection once(listener instance) { - return assure()->once(std::move(instance)); + void swap(emitter &other) { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Publishes a given event. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template + void publish(Type &&value) { + if(const auto id = type_id().hash(); handlers.first().contains(id)) { + handlers.first()[id](&value); + } + } + + /** + * @brief Registers a listener with the event emitter. + * @tparam Type Type of event to which to connect the listener. + * @param func The listener to register. + */ + template + void on(std::function func) { + handlers.first().insert_or_assign(type_id().hash(), [func = std::move(func), this](void *value) { + func(*static_cast(value), static_cast(*this)); + }); } /** * @brief Disconnects a listener from the event emitter. - * - * Do not use twice the same connection to disconnect a listener, it results - * in undefined behavior. Once used, discard the connection object. - * - * @tparam Event Type of event of the connection. - * @param conn A valid connection. + * @tparam Type Type of event of the listener. */ - template - void erase(connection conn) { - assure()->erase(std::move(conn)); + template + void erase() { + handlers.first().erase(type_hash>>::value()); } - /** - * @brief Disconnects all the listeners for the given event type. - * - * All the connections previously returned for the given event are - * invalidated. Using them results in undefined behavior. - * - * @tparam Event Type of event to reset. - */ - template - void clear() { - assure()->clear(); - } - - /** - * @brief Disconnects all the listeners. - * - * All the connections previously returned are invalidated. Using them - * results in undefined behavior. - */ - void clear() ENTT_NOEXCEPT { - for(auto &&cpool: pools) { - cpool.second->clear(); - } + /*! @brief Disconnects all the listeners. */ + void clear() noexcept { + handlers.first().clear(); } /** * @brief Checks if there are listeners registered for the specific event. - * @tparam Event Type of event to test. + * @tparam Type Type of event to test. * @return True if there are no listeners registered, false otherwise. */ - template - [[nodiscard]] bool empty() const { - const auto *cpool = assure(); - return !cpool || cpool->empty(); + template + [[nodiscard]] bool contains() const { + return handlers.first().contains(type_hash>>::value()); } /** * @brief Checks if there are listeners registered with the event emitter. * @return True if there are no listeners registered, false otherwise. */ - [[nodiscard]] bool empty() const ENTT_NOEXCEPT { - return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { - return cpool.second->empty(); - }); + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); } private: - dense_map, identity> pools{}; + compressed_pair handlers; }; } // namespace entt @@ -65524,8 +82320,6 @@ private: #include #include #include -// #include "../config/config.h" - // #include "delegate.hpp" // #include "fwd.hpp" @@ -65578,7 +82372,6 @@ class sigh { friend class sink>; using alloc_traits = std::allocator_traits; - static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; public: @@ -65590,21 +82383,21 @@ public: using sink_type = sink>; /*! @brief Default constructor. */ - sigh() + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) : sigh{allocator_type{}} {} /** * @brief Constructs a signal handler with a given allocator. * @param allocator The allocator to use. */ - explicit sigh(const allocator_type &allocator) + 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) + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) : calls{other.calls} {} /** @@ -65612,14 +82405,14 @@ public: * @param other The instance to copy from. * @param allocator The allocator to use. */ - sigh(const sigh &other, const allocator_type &allocator) + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{other.calls, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ - sigh(sigh &&other) ENTT_NOEXCEPT + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) : calls{std::move(other.calls)} {} /** @@ -65627,7 +82420,7 @@ public: * @param other The instance to move from. * @param allocator The allocator to use. */ - sigh(sigh &&other, const allocator_type &allocator) ENTT_NOEXCEPT + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{std::move(other.calls), allocator} {} /** @@ -65635,7 +82428,7 @@ public: * @param other The instance to copy from. * @return This signal handler. */ - sigh &operator=(const sigh &other) { + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { calls = other.calls; return *this; } @@ -65645,7 +82438,7 @@ public: * @param other The instance to move from. * @return This signal handler. */ - sigh &operator=(sigh &&other) ENTT_NOEXCEPT { + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { calls = std::move(other.calls); return *this; } @@ -65654,7 +82447,7 @@ public: * @brief Exchanges the contents with those of a given signal handler. * @param other Signal handler to exchange the content with. */ - void swap(sigh &other) { + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { using std::swap; swap(calls, other.calls); } @@ -65663,22 +82456,15 @@ public: * @brief Returns the associated allocator. * @return The associated allocator. */ - [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return calls.get_allocator(); } - /** - * @brief Instance type when it comes to connecting member functions. - * @tparam Class Type of class to which the member function belongs. - */ - template - using instance_type = Class *; - /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ - [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const noexcept { return calls.size(); } @@ -65686,7 +82472,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return calls.empty(); } @@ -65723,14 +82509,18 @@ public: if constexpr(std::is_void_v) { if constexpr(std::is_invocable_r_v) { call(args...); - if(func()) { break; } + if(func()) { + break; + } } else { call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { - if(func(call(args...))) { break; } + if(func(call(args...))) { + break; + } } else { func(call(args...)); } @@ -65759,13 +82549,15 @@ class connection { public: /*! @brief Default constructor. */ - connection() = default; + connection() + : disconnect{}, + signal{} {} /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(disconnect); } @@ -65779,7 +82571,7 @@ public: private: delegate disconnect; - void *signal{}; + void *signal; }; /** @@ -65809,7 +82601,7 @@ struct scoped_connection { * @brief Move constructor. * @param other The scoped connection to move from. */ - scoped_connection(scoped_connection &&other) ENTT_NOEXCEPT + scoped_connection(scoped_connection &&other) noexcept : conn{std::exchange(other.conn, {})} {} /*! @brief Automatically breaks the link on destruction. */ @@ -65828,7 +82620,7 @@ struct scoped_connection { * @param other The scoped connection to move from. * @return This scoped connection. */ - scoped_connection &operator=(scoped_connection &&other) ENTT_NOEXCEPT { + scoped_connection &operator=(scoped_connection &&other) noexcept { conn = std::exchange(other.conn, {}); return *this; } @@ -65847,7 +82639,7 @@ struct scoped_connection { * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ - [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const noexcept { return static_cast(conn); } @@ -65894,12 +82686,21 @@ 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; + } + 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) ENTT_NOEXCEPT + sink(sigh &ref) noexcept : offset{}, signal{&ref} {} @@ -65907,7 +82708,7 @@ public: * @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 ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const noexcept { return signal->calls.empty(); } @@ -65921,13 +82722,7 @@ public: [[nodiscard]] sink before() { delegate call{}; call.template connect(); - - 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; + return before(std::move(call)); } /** @@ -65942,13 +82737,7 @@ public: [[nodiscard]] sink before(Type &&value_or_instance) { delegate call{}; call.template connect(value_or_instance); - - 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; + return before(std::move(call)); } /** @@ -65958,7 +82747,7 @@ public: * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ - template + template>, void>, sink>> [[nodiscard]] sink before(Type &value_or_instance) { return before(&value_or_instance); } @@ -65966,12 +82755,10 @@ public: /** * @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 pointer that fits the purpose. * @return A properly initialized sink object. */ - template - [[nodiscard]] sink before(Type *value_or_instance) { + [[nodiscard]] sink before(const void *value_or_instance) { sink other{*this}; if(value_or_instance) { @@ -65997,81 +82784,47 @@ public: } /** - * @brief Connects a free function or an unbound member to a signal. + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. * - * The signal handler performs checks to avoid multiple connections for the - * same function. - * - * @tparam Candidate Function or member to connect to the signal. - * @return A properly initialized connection object. - */ - template - connection connect() { - disconnect(); - - delegate call{}; - call.template connect(); - signal->calls.insert(signal->calls.end() - offset, std::move(call)); - - delegate conn{}; - conn.template connect<&release>(); - return {std::move(conn), signal}; - } - - /** - * @brief Connects a free function with payload or a bound member to a - * signal. - * - * The signal 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 signal. On the other side, the signal handler performs + * 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. - * @param value_or_instance A valid object that fits the purpose. + * @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); + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); delegate call{}; - call.template connect(value_or_instance); + call.template connect(value_or_instance...); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; - conn.template connect<&release>(value_or_instance); + conn.template connect<&release>(value_or_instance...); return {std::move(conn), signal}; } /** - * @brief Disconnects a free function or an unbound member from a signal. + * @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() { + template + void disconnect(Type &&...value_or_instance) { auto &calls = signal->calls; delegate call{}; - call.template connect(); - calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); - } - - /** - * @brief Disconnects a free function with payload or a bound member from a - * signal. - * @tparam Candidate Function or member to disconnect from the signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid object that fits the purpose. - */ - template - void disconnect(Type &&value_or_instance) { - auto &calls = signal->calls; - delegate call{}; - call.template connect(value_or_instance); + call.template connect(value_or_instance...); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } @@ -66081,7 +82834,7 @@ public: * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ - template + template>, void>>> void disconnect(Type &value_or_instance) { disconnect(&value_or_instance); } @@ -66089,11 +82842,9 @@ public: /** * @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 disconnect(Type *value_or_instance) { + 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; }; @@ -66128,3 +82879,4 @@ sink(sigh &) -> sink>; #endif +// IWYU pragma: end_exports