diff --git a/AUTHORS b/AUTHORS index 63c8b7c01..71156fce8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,7 @@ eugeneko gale83 ghost Green-Sky +mhammerc Milerius morbo84 m-waka diff --git a/single_include/entt/entt.hpp b/single_include/entt/entt.hpp index 087be804e..f183fc79e 100644 --- a/single_include/entt/entt.hpp +++ b/single_include/entt/entt.hpp @@ -117,7 +117,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -270,9 +270,21 @@ public: return helper(traits_type::offset, wrapper.str); } + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + inline static hash_type to_value(const char *str, std::size_t size) ENTT_NOEXCEPT { + ENTT_ID_TYPE partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /*! @brief Constructs an empty hashed string. */ constexpr hashed_string() ENTT_NOEXCEPT - : hash{}, str{nullptr} + : str{nullptr}, hash{} {} /** @@ -291,17 +303,16 @@ public: */ template constexpr hashed_string(const char (&curr)[N]) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, curr)}, str{curr} + : str{curr}, hash{helper(traits_type::offset, curr)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const char *`. - * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, wrapper.str)}, str{wrapper.str} + : str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)} {} /** @@ -339,8 +350,8 @@ public: } private: - hash_type hash; const char *str; + hash_type hash; }; @@ -549,7 +560,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -659,9 +670,21 @@ public: return helper(traits_type::offset, wrapper.str); } + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + inline static hash_type to_value(const char *str, std::size_t size) ENTT_NOEXCEPT { + ENTT_ID_TYPE partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /*! @brief Constructs an empty hashed string. */ constexpr hashed_string() ENTT_NOEXCEPT - : hash{}, str{nullptr} + : str{nullptr}, hash{} {} /** @@ -680,17 +703,16 @@ public: */ template constexpr hashed_string(const char (&curr)[N]) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, curr)}, str{curr} + : str{curr}, hash{helper(traits_type::offset, curr)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const char *`. - * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, wrapper.str)}, str{wrapper.str} + : str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)} {} /** @@ -728,8 +750,8 @@ public: } private: - hash_type hash; const char *str; + hash_type hash; }; @@ -764,9 +786,97 @@ constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::si namespace entt { -/*! @brief A class to use to push around lists of types, nothing more. */ +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the given type list. + */ template -struct type_list {}; +struct type_list { + /*! @brief Unsigned integer type. */ + static constexpr auto size = sizeof...(Type); +}; + + +/*! @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::disjunction_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 Traits class used mainly to push things across boundaries. */ @@ -977,7 +1087,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -991,7 +1101,7 @@ using maybe_atomic_t = Type; #endif // ENTT_CONFIG_CONFIG_H // #include "registry.hpp" -#ifndef ENTT_ENTITY_REGISTRY_HPP +#ifndef ENTT_ENTITY_REGISTRY_HPP #define ENTT_ENTITY_REGISTRY_HPP @@ -1045,7 +1155,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -1278,9 +1388,21 @@ public: return helper(traits_type::offset, wrapper.str); } + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + inline static hash_type to_value(const char *str, std::size_t size) ENTT_NOEXCEPT { + ENTT_ID_TYPE partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /*! @brief Constructs an empty hashed string. */ constexpr hashed_string() ENTT_NOEXCEPT - : hash{}, str{nullptr} + : str{nullptr}, hash{} {} /** @@ -1299,17 +1421,16 @@ public: */ template constexpr hashed_string(const char (&curr)[N]) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, curr)}, str{curr} + : str{curr}, hash{helper(traits_type::offset, curr)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const char *`. - * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, wrapper.str)}, str{wrapper.str} + : str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)} {} /** @@ -1347,8 +1468,8 @@ public: } private: - hash_type hash; const char *str; + hash_type hash; }; @@ -1423,7 +1544,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -1533,9 +1654,21 @@ public: return helper(traits_type::offset, wrapper.str); } + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + inline static hash_type to_value(const char *str, std::size_t size) ENTT_NOEXCEPT { + ENTT_ID_TYPE partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /*! @brief Constructs an empty hashed string. */ constexpr hashed_string() ENTT_NOEXCEPT - : hash{}, str{nullptr} + : str{nullptr}, hash{} {} /** @@ -1554,17 +1687,16 @@ public: */ template constexpr hashed_string(const char (&curr)[N]) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, curr)}, str{curr} + : str{curr}, hash{helper(traits_type::offset, curr)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const char *`. - * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, wrapper.str)}, str{wrapper.str} + : str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)} {} /** @@ -1602,8 +1734,8 @@ public: } private: - hash_type hash; const char *str; + hash_type hash; }; @@ -1638,9 +1770,97 @@ constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::si namespace entt { -/*! @brief A class to use to push around lists of types, nothing more. */ +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the given type list. + */ template -struct type_list {}; +struct type_list { + /*! @brief Unsigned integer type. */ + static constexpr auto size = sizeof...(Type); +}; + + +/*! @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::disjunction_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 Traits class used mainly to push things across boundaries. */ @@ -1819,7 +2039,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -2086,7 +2306,7 @@ bool operator!=(const delegate &lhs, const delegate /** - * @brief Deduction guideline. + * @brief Deduction guide. * * It allows to deduce the function type of the delegate directly from a * function provided to the constructor. @@ -2099,7 +2319,7 @@ delegate(connect_arg_t) ENTT_NOEXCEPT /** - * @brief Deduction guideline. + * @brief Deduction guide. * * It allows to deduce the function type of the delegate directly from a member * or a free function with payload provided to the constructor. @@ -2512,16 +2732,12 @@ private: #include #include -#include #include #include #include #include -#include // #include "../config/config.h" -// #include "../core/algorithm.hpp" - // #include "entity.hpp" #ifndef ENTT_ENTITY_ENTITY_HPP #define ENTT_ENTITY_ENTITY_HPP @@ -2699,16 +2915,6 @@ constexpr auto null = internal::null{}; namespace entt { -/** - * @brief Sparse set. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. - */ -template -class sparse_set; - - /** * @brief Basic sparse set implementation. * @@ -2717,8 +2923,8 @@ class sparse_set; * _packed_ one; one used for direct access through contiguous memory, the other * one used to get the data through an extra level of indirection.
* This is largely used by the registry to offer users the fastest access ever - * to the components. Views in general are almost entirely designed around - * sparse sets. + * to the components. Views and groups in general are almost entirely designed + * around sparse sets. * * This type of data structure is widely documented in the literature and on the * web. This is nothing more than a customized implementation suitable for the @@ -2737,7 +2943,7 @@ class sparse_set; * @tparam Entity A valid entity type (see entt_traits for more details). */ template -class sparse_set { +class sparse_set { using traits_type = entt_traits; static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0)); @@ -2749,15 +2955,15 @@ class sparse_set { using direct_type = const std::vector; using index_type = typename traits_type::difference_type; - iterator(direct_type *ref, index_type idx) ENTT_NOEXCEPT + iterator(direct_type *ref, const index_type idx) ENTT_NOEXCEPT : direct{ref}, index{idx} {} public: using difference_type = index_type; - using value_type = const Entity; - using pointer = value_type *; - using reference = value_type &; + using value_type = Entity; + using pointer = const value_type *; + using reference = const value_type &; using iterator_category = std::random_access_iterator_tag; iterator() ENTT_NOEXCEPT = default; @@ -2844,7 +3050,7 @@ class sparse_set { index_type index; }; - void assure(std::size_t page) { + void assure(const std::size_t page) { if(!(page < reverse.size())) { reverse.resize(page+1); } @@ -2856,7 +3062,7 @@ class sparse_set { } } - auto index(Entity entt) const ENTT_NOEXCEPT { + auto index(const Entity entt) const ENTT_NOEXCEPT { const auto identifier = entt & traits_type::entity_mask; const auto page = size_type(identifier / entt_per_page); const auto offset = size_type(identifier & (entt_per_page - 1)); @@ -2882,11 +3088,11 @@ public: : reverse{}, direct{other.direct} { - for(size_type i = {}, last = other.reverse.size(); i < last; ++i) { - if(other.reverse[i].first) { - assure(i); - std::copy_n(other.reverse[i].first.get(), entt_per_page, reverse[i].first.get()); - reverse[i].second = other.reverse[i].second; + for(size_type pos{}, last = other.reverse.size(); pos < last; ++pos) { + if(other.reverse[pos].first) { + assure(pos); + std::copy_n(other.reverse[pos].first.get(), entt_per_page, reverse[pos].first.get()); + reverse[pos].second = other.reverse[pos].second; } } } @@ -2897,9 +3103,6 @@ public: /*! @brief Default destructor. */ virtual ~sparse_set() ENTT_NOEXCEPT = default; - /*! @brief Default move assignment operator. @return This sparse set. */ - sparse_set & operator=(sparse_set &&) = default; - /** * @brief Copy assignment operator. * @param other The instance to copy from. @@ -2914,6 +3117,9 @@ public: return *this; } + /*! @brief Default move assignment operator. @return This sparse set. */ + sparse_set & operator=(sparse_set &&) = default; + /** * @brief Increases the capacity of a sparse set. * @@ -3140,8 +3346,8 @@ public: ENTT_ASSERT(has(entt)); auto [from_page, from_offset] = index(entt); auto [to_page, to_offset] = index(direct.back()); - std::swap(direct[size_type(reverse[from_page].first[from_offset])], direct.back()); - std::swap(reverse[from_page].first[from_offset], reverse[to_page].first[to_offset]); + direct[size_type(reverse[from_page].first[from_offset])] = direct.back(); + reverse[to_page].first[to_offset] = reverse[from_page].first[from_offset]; reverse[from_page].first[from_offset] = null; reverse[from_page].second--; direct.pop_back(); @@ -3217,689 +3423,12 @@ public: direct.clear(); } - /** - * @brief Clones and returns a sparse set. - * - * The basic implementation of a sparse set is always copyable. Therefore, - * the returned instance is always valid. - * - * @return A fresh copy of the given sparse set. - */ - virtual std::unique_ptr clone() const { - return std::make_unique(*this); - } - private: std::vector, size_type>> reverse; std::vector direct; }; -/** - * @brief Extended sparse set implementation. - * - * This specialization of a sparse set associates an object to an entity. The - * main purpose of this class is to use sparse sets to store components in a - * registry. It guarantees fast access both to the elements and to the entities. - * - * @note - * Entities and objects have the same order. It's guaranteed both in case of raw - * access (either to entities or objects) and when using input iterators. - * - * @note - * Internal data structures arrange elements to maximize performance. Because of - * that, there are no guarantees that elements have the expected order when - * iterate directly the internal packed array (see `raw` and `size` member - * functions for that). Use `begin` and `end` instead. - * - * @sa sparse_set - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Type Type of objects assigned to the entities. - */ -template -class sparse_set: public sparse_set { - using underlying_type = sparse_set; - using traits_type = entt_traits; - - template> - class iterator { - friend class sparse_set; - - using instance_type = std::conditional_t, std::vector>; - using index_type = typename traits_type::difference_type; - - iterator(instance_type *ref, index_type idx) ENTT_NOEXCEPT - : instances{ref}, index{idx} - {} - - public: - using difference_type = index_type; - using value_type = std::conditional_t; - using pointer = value_type *; - using reference = value_type &; - using iterator_category = std::random_access_iterator_tag; - - iterator() ENTT_NOEXCEPT = default; - - iterator & operator++() ENTT_NOEXCEPT { - return --index, *this; - } - - iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; - return ++(*this), orig; - } - - iterator & operator--() ENTT_NOEXCEPT { - return ++index, *this; - } - - iterator operator--(int) ENTT_NOEXCEPT { - iterator orig = *this; - return --(*this), orig; - } - - iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { - index -= value; - return *this; - } - - iterator operator+(const difference_type value) const ENTT_NOEXCEPT { - return iterator{instances, index-value}; - } - - inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { - return (*this += -value); - } - - inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT { - return (*this + -value); - } - - difference_type operator-(const iterator &other) const ENTT_NOEXCEPT { - return other.index - index; - } - - reference operator[](const difference_type value) const ENTT_NOEXCEPT { - const auto pos = size_type(index-value-1); - return (*instances)[pos]; - } - - bool operator==(const iterator &other) const ENTT_NOEXCEPT { - return other.index == index; - } - - inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - bool operator<(const iterator &other) const ENTT_NOEXCEPT { - return index > other.index; - } - - bool operator>(const iterator &other) const ENTT_NOEXCEPT { - return index < other.index; - } - - inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this > other); - } - - inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this < other); - } - - pointer operator->() const ENTT_NOEXCEPT { - const auto pos = size_type(index-1); - return &(*instances)[pos]; - } - - inline reference operator*() const ENTT_NOEXCEPT { - return *operator->(); - } - - private: - instance_type *instances; - index_type index; - }; - - template - class iterator { - friend class sparse_set; - - using instance_type = std::conditional_t; - using index_type = typename traits_type::difference_type; - - iterator(instance_type *ref, index_type idx) ENTT_NOEXCEPT - : instance{ref}, index{idx} - {} - - public: - using difference_type = index_type; - using value_type = std::conditional_t; - using pointer = value_type *; - using reference = value_type &; - using iterator_category = std::random_access_iterator_tag; - - iterator() ENTT_NOEXCEPT = default; - - iterator & operator++() ENTT_NOEXCEPT { - return --index, *this; - } - - iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; - return ++(*this), orig; - } - - iterator & operator--() ENTT_NOEXCEPT { - return ++index, *this; - } - - iterator operator--(int) ENTT_NOEXCEPT { - iterator orig = *this; - return --(*this), orig; - } - - iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { - index -= value; - return *this; - } - - iterator operator+(const difference_type value) const ENTT_NOEXCEPT { - return iterator{instance, index-value}; - } - - inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { - return (*this += -value); - } - - inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT { - return (*this + -value); - } - - difference_type operator-(const iterator &other) const ENTT_NOEXCEPT { - return other.index - index; - } - - reference operator[](const difference_type) const ENTT_NOEXCEPT { - return *instance; - } - - bool operator==(const iterator &other) const ENTT_NOEXCEPT { - return other.index == index; - } - - inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - bool operator<(const iterator &other) const ENTT_NOEXCEPT { - return index > other.index; - } - - bool operator>(const iterator &other) const ENTT_NOEXCEPT { - return index < other.index; - } - - inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this > other); - } - - inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this < other); - } - - pointer operator->() const ENTT_NOEXCEPT { - return instance; - } - - inline reference operator*() const ENTT_NOEXCEPT { - return *operator->(); - } - - private: - instance_type *instance; - index_type index; - }; - -public: - /*! @brief Type of the objects associated with the entities. */ - using object_type = Type; - /*! @brief Underlying entity identifier. */ - using entity_type = typename underlying_type::entity_type; - /*! @brief Unsigned integer type. */ - using size_type = typename underlying_type::size_type; - /*! @brief Random access iterator type. */ - using iterator_type = iterator; - /*! @brief Constant random access iterator type. */ - using const_iterator_type = iterator; - - /** - * @brief Increases the capacity of a sparse set. - * - * If the new capacity is greater than the current capacity, new storage is - * allocated, otherwise the method does nothing. - * - * @param cap Desired capacity. - */ - void reserve(const size_type cap) override { - underlying_type::reserve(cap); - - if constexpr(!std::is_empty_v) { - instances.reserve(cap); - } - } - - /** - * @brief Requests the removal of unused capacity. - * - * @note - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function does nothing. - */ - void shrink_to_fit() override { - underlying_type::shrink_to_fit(); - - if constexpr(!std::is_empty_v) { - instances.shrink_to_fit(); - } - } - - /** - * @brief Direct access to the array of objects. - * - * The returned pointer is such that range `[raw(), raw() + size()]` is - * always a valid range, even if the container is empty. - * - * @note - * There are no guarantees on the order, even though either `sort` or - * `respect` has been previously invoked. Internal data structures arrange - * elements to maximize performance. Accessing them directly gives a - * performance boost but less guarantees. Use `begin` and `end` if you want - * to iterate the sparse set in the expected order. - * - * @note - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function always returns a pointer - * to that instance. - * - * @return A pointer to the array of objects. - */ - const object_type * raw() const ENTT_NOEXCEPT { - if constexpr(std::is_empty_v) { - return &instances; - } else { - return instances.data(); - } - } - - /*! @copydoc raw */ - object_type * raw() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).raw()); - } - - /** - * @brief Returns an iterator to the beginning. - * - * The returned iterator points to the first instance of the given type. If - * the sparse set is empty, the returned iterator will be equal to `end()`. - * - * @note - * Input iterators stay true to the order imposed by a call to either `sort` - * or `respect`. - * - * @return An iterator to the first instance of the given type. - */ - const_iterator_type cbegin() const ENTT_NOEXCEPT { - const typename traits_type::difference_type pos = underlying_type::size(); - return const_iterator_type{&instances, pos}; - } - - /*! @copydoc cbegin */ - inline const_iterator_type begin() const ENTT_NOEXCEPT { - return cbegin(); - } - - /*! @copydoc begin */ - iterator_type begin() ENTT_NOEXCEPT { - const typename traits_type::difference_type pos = underlying_type::size(); - return iterator_type{&instances, pos}; - } - - /** - * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the given type. Attempting to dereference the returned iterator - * results in undefined behavior. - * - * @note - * Input iterators stay true to the order imposed by a call to either `sort` - * or `respect`. - * - * @return An iterator to the element following the last instance of the - * given type. - */ - const_iterator_type cend() const ENTT_NOEXCEPT { - return const_iterator_type{&instances, {}}; - } - - /*! @copydoc cend */ - inline const_iterator_type end() const ENTT_NOEXCEPT { - return cend(); - } - - /*! @copydoc end */ - iterator_type end() ENTT_NOEXCEPT { - return iterator_type{&instances, {}}; - } - - /** - * @brief Returns the object associated with an entity. - * - * @warning - * Attempting to use an entity that doesn't belong to the sparse set results - * in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entity. - * - * @param entt A valid entity identifier. - * @return The object associated with the entity. - */ - const object_type & get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { - if constexpr(std::is_empty_v) { - ENTT_ASSERT(underlying_type::has(entt)); - return instances; - } else { - return instances[underlying_type::get(entt)]; - } - } - - /*! @copydoc get */ - inline object_type & get(const entity_type entt) ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).get(entt)); - } - - /** - * @brief Returns a pointer to the object associated with an entity, if any. - * @param entt A valid entity identifier. - * @return The object associated with the entity, if any. - */ - const object_type * try_get(const entity_type entt) const ENTT_NOEXCEPT { - if constexpr(std::is_empty_v) { - return underlying_type::has(entt) ? &instances : nullptr; - } else { - return underlying_type::has(entt) ? (instances.data() + underlying_type::get(entt)) : nullptr; - } - } - - /*! @copydoc try_get */ - inline object_type * try_get(const entity_type entt) ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).try_get(entt)); - } - - /** - * @brief Assigns an entity to a sparse set and constructs its object. - * - * This version accept both types that can be constructed in place directly - * and types like aggregates that do not work well with a placement new as - * performed usually under the hood during an _emplace back_. - * - * @warning - * Attempting to use an entity that already belongs to the sparse set - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set already contains the given entity. - * - * @tparam Args Types of arguments to use to construct the object. - * @param entt A valid entity identifier. - * @param args Parameters to use to construct an object for the entity. - * @return The object associated with the entity. - */ - template - object_type & construct(const entity_type entt, [[maybe_unused]] Args &&... args) { - if constexpr(std::is_empty_v) { - underlying_type::construct(entt); - return instances; - } else { - if constexpr(std::is_aggregate_v) { - instances.emplace_back(Type{std::forward(args)...}); - } else { - instances.emplace_back(std::forward(args)...); - } - - // entity goes after component in case constructor throws - underlying_type::construct(entt); - return instances.back(); - } - } - - /** - * @brief Assigns one or more entities to a sparse set and constructs their - * objects. - * - * The object type must be at least default constructible. - * - * @note - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function always returns a pointer - * to that instance. - * - * @warning - * Attempting to assign an entity that already belongs to the sparse set - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set already contains the given entity. - * - * @tparam It Type of forward 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 A pointer to the array of instances just created and sorted the - * same of the entities. - */ - template - object_type * batch(It first, It last) { - if constexpr(std::is_empty_v) { - underlying_type::batch(first, last); - return &instances; - } else { - static_assert(std::is_default_constructible_v); - const auto skip = instances.size(); - instances.insert(instances.end(), last-first, {}); - // entity goes after component in case constructor throws - underlying_type::batch(first, last); - return instances.data() + skip; - } - } - - /** - * @brief Removes an entity from a sparse set and destroies its object. - * - * @warning - * Attempting to use an entity that doesn't belong to the sparse set results - * in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entity. - * - * @param entt A valid entity identifier. - */ - void destroy(const entity_type entt) override { - if constexpr(!std::is_empty_v) { - std::swap(instances[underlying_type::get(entt)], instances.back()); - instances.pop_back(); - } - - underlying_type::destroy(entt); - } - - /** - * @brief Sort components according to the given comparison function. - * - * Sort the elements so that iterating the sparse set with a couple of - * iterators returns them 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(const Entity, const Entity); - * bool(const Type &, const Type &); - * @endcode - * - * Moreover, the comparison function object shall induce a - * _strict weak ordering_ on the values. - * - * The sort function oject 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. - * - * The comparison function object received by the sort function object - * hasn't necessarily the type of the one passed along with the other - * parameters to this member function. - * - * @note - * Empty components aren't explicitly instantiated. Therefore, the - * comparison function must necessarily accept entity identifiers. - * - * @note - * Attempting to iterate elements using a raw pointer returned by a call to - * either `data` or `raw` gives no guarantees on the order, even though - * `sort` has been invoked. - * - * @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) { - std::vector copy(instances.size()); - std::iota(copy.begin(), copy.end(), 0); - - if constexpr(std::is_invocable_v) { - static_assert(!std::is_empty_v); - - algo(copy.rbegin(), copy.rend(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { - return compare(std::as_const(instances[lhs]), std::as_const(instances[rhs])); - }, std::forward(args)...); - } else { - algo(copy.rbegin(), copy.rend(), [compare = std::move(compare), data = underlying_type::data()](const auto lhs, const auto rhs) { - return compare(data[lhs], data[rhs]); - }, std::forward(args)...); - } - - for(size_type pos = 0, last = copy.size(); pos < last; ++pos) { - auto curr = pos; - auto next = copy[curr]; - - while(curr != next) { - const auto lhs = copy[curr]; - const auto rhs = copy[next]; - - if constexpr(!std::is_empty_v) { - std::swap(instances[lhs], instances[rhs]); - } - - underlying_type::swap(lhs, rhs); - copy[curr] = curr; - curr = next; - next = copy[curr]; - } - } - } - - /** - * @brief Sort components according to the order of the entities in another - * sparse set. - * - * Entities that are part of both the sparse sets are ordered internally - * according to the order they have in `other`. All the other entities goes - * to the end of the list and there are no guarantess on their order. - * Components are sorted according to the entities to which they - * belong.
- * In other terms, this function can be used to impose the same order on two - * sets by using one of them as a master and the other one as a slave. - * - * Iterating the sparse set with a couple of iterators returns elements in - * the expected order after a call to `respect`. See `begin` and `end` for - * more details. - * - * @note - * Attempting to iterate elements using a raw pointer returned by a call to - * either `data` or `raw` gives no guarantees on the order, even though - * `respect` has been invoked. - * - * @param other The sparse sets that imposes the order of the entities. - */ - void respect(const sparse_set &other) ENTT_NOEXCEPT override { - if constexpr(std::is_empty_v) { - underlying_type::respect(other); - } else { - const auto to = other.end(); - auto from = other.begin(); - - size_type pos = underlying_type::size() - 1; - const auto *local = underlying_type::data(); - - while(pos && from != to) { - const auto curr = *from; - - if(underlying_type::has(curr)) { - if(curr != *(local + pos)) { - auto candidate = underlying_type::get(curr); - std::swap(instances[pos], instances[candidate]); - underlying_type::swap(pos, candidate); - } - - --pos; - } - - ++from; - } - } - } - - /*! @brief Resets a sparse set. */ - void reset() override { - underlying_type::reset(); - - if constexpr(!std::is_empty_v) { - instances.clear(); - } - } - - /** - * @brief Clones and returns a sparse set if possible. - * - * The extended implementation of a sparse set is copyable only if its - * object type is copyable. Because of that, this member functions isn't - * guaranteed to return always a valid pointer. - * - * @return A fresh copy of the given sparse set if its object type is - * copyable, an empty unique pointer otherwise. - */ - std::unique_ptr> clone() const override { - if constexpr(std::is_copy_constructible_v) { - return std::make_unique(*this); - } else { - return nullptr; - } - } - -private: - std::conditional_t, object_type, std::vector> instances; -}; - - } @@ -4410,8 +3939,8 @@ public: archive(static_cast(sz)); - for(std::remove_const_t i{}; i < sz; ++i) { - const auto entt = entities[i]; + for(std::remove_const_t pos{}; pos < sz; ++pos) { + const auto entt = entities[pos]; if constexpr(std::is_empty_v) { archive(entt); @@ -4496,18 +4025,19 @@ class basic_snapshot_loader { archive(length); while(length--) { + static constexpr auto destroyed = false; Entity entt{}; - Type instance{}; if constexpr(std::is_empty_v) { archive(entt); + force(*reg, entt, destroyed); + reg->template assign(args..., entt); } else { + Type instance{}; archive(entt, instance); + force(*reg, entt, destroyed); + reg->template assign(args..., entt, std::as_const(instance)); } - - static constexpr auto destroyed = false; - force(*reg, entt, destroyed); - reg->template assign(args..., entt, std::as_const(instance)); } } @@ -4675,24 +4205,25 @@ class basic_continuous_loader { } } - template - void assign(Archive &archive, Func func, Member Type:: *... member) { + template + void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) { Entity length{}; archive(length); while(length--) { Entity entt{}; - Other instance{}; if constexpr(std::is_empty_v) { archive(entt); + restore(entt); + reg->template assign_or_replace(map(entt)); } else { + Other instance{}; archive(entt, instance); + (update(instance, member), ...); + restore(entt); + reg->template assign_or_replace(map(entt), std::as_const(instance)); } - - restore(entt); - (update(instance, member), ...); - func(map(entt), instance); } } @@ -4767,12 +4298,8 @@ public: */ template basic_continuous_loader & component(Archive &archive, Member Type:: *... member) { - auto apply = [this](const auto entt, const auto &component) { - reg->template assign_or_replace>(entt, component); - }; - (reset(), ...); - (assign(archive, apply, member...), ...); + (assign(archive, member...), ...); return *this; } @@ -4860,6 +4387,710 @@ private: #endif // ENTT_ENTITY_SNAPSHOT_HPP +// #include "storage.hpp" +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "sparse_set.hpp" + +// #include "entity.hpp" + + + +namespace entt { + + +/** + * @brief Basic storage implementation. + * + * This class is a refinement of a sparse set that associates an object to an + * entity. The main purpose of this class is to extend sparse sets to store + * components in a registry. It guarantees fast access both to the elements and + * to the entities. + * + * @note + * Entities and objects have the same order. It's guaranteed both in case of raw + * access (either to entities or objects) and when using input iterators. + * + * @note + * Internal data structures arrange elements to maximize performance. Because of + * that, there are no guarantees that elements have the expected order when + * iterate directly the internal packed array (see `raw` and `size` member + * functions for that). Use `begin` and `end` instead. + * + * @warning + * Empty types aren't explicitly instantiated. Temporary objects are returned in + * place of the instances of the components and raw access isn't available for + * them. + * + * @sa sparse_set + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + */ +template> +class basic_storage: public sparse_set { + using underlying_type = sparse_set; + using traits_type = entt_traits; + + template + class iterator { + friend class basic_storage; + + using instance_type = std::conditional_t, std::vector>; + using index_type = typename traits_type::difference_type; + + iterator(instance_type *ref, const index_type idx) ENTT_NOEXCEPT + : instances{ref}, index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Type; + using pointer = std::conditional_t; + using reference = std::conditional_t; + using iterator_category = std::random_access_iterator_tag; + + iterator() ENTT_NOEXCEPT = default; + + iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + iterator operator++(int) ENTT_NOEXCEPT { + iterator orig = *this; + return ++(*this), orig; + } + + iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + iterator operator--(int) ENTT_NOEXCEPT { + iterator orig = *this; + return --(*this), orig; + } + + iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + return iterator{instances, index-value}; + } + + inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = size_type(index-value-1); + return (*instances)[pos]; + } + + bool operator==(const iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + bool operator<(const iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + bool operator>(const iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + pointer operator->() const ENTT_NOEXCEPT { + const auto pos = size_type(index-1); + return &(*instances)[pos]; + } + + inline reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + private: + instance_type *instances; + index_type index; + }; + +public: + /*! @brief Type of the objects associated with the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename underlying_type::size_type; + /*! @brief Random access iterator type. */ + using iterator_type = iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator_type = iterator; + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) override { + underlying_type::reserve(cap); + instances.reserve(cap); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + underlying_type::shrink_to_fit(); + instances.shrink_to_fit(); + } + + /** + * @brief Direct access to the array of objects. + * + * The returned pointer is such that range `[raw(), raw() + size()]` is + * always a valid range, even if the container is empty. + * + * @note + * There are no guarantees on the order, even though either `sort` or + * `respect` has been previously invoked. Internal data structures arrange + * elements to maximize performance. Accessing them directly gives a + * performance boost but less guarantees. Use `begin` and `end` if you want + * to iterate the storage in the expected order. + * + * @return A pointer to the array of objects. + */ + const object_type * raw() const ENTT_NOEXCEPT { + return instances.data(); + } + + /*! @copydoc raw */ + object_type * raw() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).raw()); + } + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the given type. If + * the storage is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the first instance of the given type. + */ + const_iterator_type cbegin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return const_iterator_type{&instances, pos}; + } + + /*! @copydoc cbegin */ + inline const_iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + iterator_type begin() ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return iterator_type{&instances, pos}; + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + const_iterator_type cend() const ENTT_NOEXCEPT { + return const_iterator_type{&instances, {}}; + } + + /*! @copydoc cend */ + inline const_iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + iterator_type end() ENTT_NOEXCEPT { + return iterator_type{&instances, {}}; + } + + /** + * @brief Returns the object associated with an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The object associated with the entity. + */ + const object_type & get(const entity_type entt) const ENTT_NOEXCEPT { + return instances[underlying_type::get(entt)]; + } + + /*! @copydoc get */ + inline object_type & get(const entity_type entt) ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns a pointer to the object associated with an entity, if any. + * @param entt A valid entity identifier. + * @return The object associated with the entity, if any. + */ + const object_type * try_get(const entity_type entt) const ENTT_NOEXCEPT { + return underlying_type::has(entt) ? (instances.data() + underlying_type::get(entt)) : nullptr; + } + + /*! @copydoc try_get */ + inline object_type * try_get(const entity_type entt) ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).try_get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * This version accept both types that can be constructed in place directly + * and types like aggregates that do not work well with a placement new as + * performed usually under the hood during an _emplace back_. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid entity identifier. + * @param args Parameters to use to construct an object for the entity. + * @return The object associated with the entity. + */ + template + object_type & construct(const entity_type entt, Args &&... args) { + if constexpr(std::is_aggregate_v) { + instances.emplace_back(Type{std::forward(args)...}); + } else { + instances.emplace_back(std::forward(args)...); + } + + // entity goes after component in case constructor throws + underlying_type::construct(entt); + return instances.back(); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects. + * + * The object type must be at least default constructible. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage already contains the given entity. + * + * @tparam It Type of forward 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 A pointer to the array of instances just created and sorted the + * same of the entities. + */ + template + object_type * batch(It first, It last) { + static_assert(std::is_default_constructible_v); + const auto skip = instances.size(); + instances.insert(instances.end(), last-first, {}); + // entity goes after component in case constructor throws + underlying_type::batch(first, last); + return instances.data() + skip; + } + + /** + * @brief Removes an entity from a storage and destroys its object. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage doesn't contain the given entity. + * + * @param entt A valid entity identifier. + */ + void destroy(const entity_type entt) override { + std::swap(instances[underlying_type::get(entt)], instances.back()); + instances.pop_back(); + underlying_type::destroy(entt); + } + + /** + * @brief Sort instances according to the given comparison function. + * + * Sort the elements so that iterating the storage with a couple of + * iterators returns them 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(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function oject 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. + * + * The comparison function object received by the sort function object + * hasn't necessarily the type of the one passed along with the other + * parameters to this member function. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * either `data` or `raw` gives no guarantees on the order, even though + * `sort` has been invoked. + * + * @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) { + std::vector copy(instances.size()); + std::iota(copy.begin(), copy.end(), 0); + + if constexpr(std::is_invocable_v) { + static_assert(!std::is_empty_v); + + algo(copy.rbegin(), copy.rend(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + return compare(std::as_const(instances[lhs]), std::as_const(instances[rhs])); + }, std::forward(args)...); + } else { + algo(copy.rbegin(), copy.rend(), [compare = std::move(compare), data = underlying_type::data()](const auto lhs, const auto rhs) { + return compare(data[lhs], data[rhs]); + }, std::forward(args)...); + } + + for(size_type pos{}, last = copy.size(); pos < last; ++pos) { + auto curr = pos; + auto next = copy[curr]; + + while(curr != next) { + const auto lhs = copy[curr]; + const auto rhs = copy[next]; + std::swap(instances[lhs], instances[rhs]); + underlying_type::swap(lhs, rhs); + copy[curr] = curr; + curr = next; + next = copy[curr]; + } + } + } + + /** + * @brief Sort instances according to the order of the entities in another + * sparse set. + * + * Entities that are part of both the storage are ordered internally + * according to the order they have in `other`. All the other entities goes + * to the end of the list and there are no guarantess on their order. + * Instances are sorted according to the entities to which they belong.
+ * In other terms, this function can be used to impose the same order on two + * sets by using one of them as a master and the other one as a slave. + * + * Iterating the storage with a couple of iterators returns elements in the + * expected order after a call to `respect`. See `begin` and `end` for more + * details. + * + * @note + * Attempting to iterate elements using a raw pointer returned by a call to + * either `data` or `raw` gives no guarantees on the order, even though + * `respect` has been invoked. + * + * @param other The sparse sets that imposes the order of the entities. + */ + void respect(const sparse_set &other) ENTT_NOEXCEPT override { + const auto to = other.end(); + auto from = other.begin(); + + size_type pos = underlying_type::size() - 1; + const auto *local = underlying_type::data(); + + while(pos && from != to) { + const auto curr = *from; + + if(underlying_type::has(curr)) { + if(curr != *(local + pos)) { + auto candidate = underlying_type::get(curr); + std::swap(instances[pos], instances[candidate]); + underlying_type::swap(pos, candidate); + } + + --pos; + } + + ++from; + } + } + + /*! @brief Resets a storage. */ + void reset() override { + underlying_type::reset(); + instances.clear(); + } + +private: + std::vector instances; +}; + + +/*! @copydoc basic_storage */ +template +class basic_storage>>: public sparse_set { + using underlying_type = sparse_set; + using traits_type = entt_traits; + + class iterator { + friend class basic_storage; + + using index_type = typename traits_type::difference_type; + + iterator(const index_type idx) ENTT_NOEXCEPT + : index{idx} + {} + + public: + using difference_type = index_type; + using value_type = Type; + using pointer = const value_type *; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + iterator() ENTT_NOEXCEPT = default; + + iterator & operator++() ENTT_NOEXCEPT { + return --index, *this; + } + + iterator operator++(int) ENTT_NOEXCEPT { + iterator orig = *this; + return ++(*this), orig; + } + + iterator & operator--() ENTT_NOEXCEPT { + return ++index, *this; + } + + iterator operator--(int) ENTT_NOEXCEPT { + iterator orig = *this; + return --(*this), orig; + } + + iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + index -= value; + return *this; + } + + iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + return iterator{index-value}; + } + + inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + difference_type operator-(const iterator &other) const ENTT_NOEXCEPT { + return other.index - index; + } + + reference operator[](const difference_type) const ENTT_NOEXCEPT { + return {}; + } + + bool operator==(const iterator &other) const ENTT_NOEXCEPT { + return other.index == index; + } + + inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + bool operator<(const iterator &other) const ENTT_NOEXCEPT { + return index > other.index; + } + + bool operator>(const iterator &other) const ENTT_NOEXCEPT { + return index < other.index; + } + + inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT { + return !(*this > other); + } + + inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT { + return !(*this < other); + } + + pointer operator->() const ENTT_NOEXCEPT { + return nullptr; + } + + inline reference operator*() const ENTT_NOEXCEPT { + return {}; + } + + private: + index_type index; + }; + +public: + /*! @brief Type of the objects associated with the entities. */ + using object_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename underlying_type::size_type; + /*! @brief Random access iterator type. */ + using iterator_type = iterator; + + /** + * @brief Returns an iterator to the beginning. + * + * The returned iterator points to the first instance of the given type. If + * the storage is empty, the returned iterator will be equal to `end()`. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the first instance of the given type. + */ + iterator_type cbegin() const ENTT_NOEXCEPT { + const typename traits_type::difference_type pos = underlying_type::size(); + return iterator_type{pos}; + } + + /*! @copydoc cbegin */ + inline iterator_type begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the given type. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @note + * Input iterators stay true to the order imposed by a call to either `sort` + * or `respect`. + * + * @return An iterator to the element following the last instance of the + * given type. + */ + iterator_type cend() const ENTT_NOEXCEPT { + return iterator_type{}; + } + + /*! @copydoc cend */ + inline iterator_type end() const ENTT_NOEXCEPT { + return cend(); + } + + /** + * @brief Returns the object associated with an entity. + * + * @note + * Empty types aren't explicitly instantiated. Therefore, this function + * always returns a temporary object. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode if the + * storage doesn't contain the given entity. + * + * @param entt A valid entity identifier. + * @return The object associated with the entity. + */ + object_type get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + ENTT_ASSERT(underlying_type::has(entt)); + return {}; + } +}; + +/*! @copydoc basic_storage */ +template +struct storage: basic_storage {}; + + +} + + +#endif // ENTT_ENTITY_STORAGE_HPP + // #include "entity.hpp" // #include "group.hpp" @@ -4876,6 +5107,8 @@ private: // #include "sparse_set.hpp" +// #include "storage.hpp" + // #include "fwd.hpp" @@ -4954,14 +5187,25 @@ class basic_group> { friend class basic_registry; template - using pool_type = std::conditional_t, const sparse_set>, sparse_set>; + using pool_type = std::conditional_t, const storage>, storage>; // we could use pool_type *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug) - basic_group(sparse_set *ref, sparse_set> *... get) ENTT_NOEXCEPT + basic_group(sparse_set *ref, storage> *... get) ENTT_NOEXCEPT : handler{ref}, pools{get...} {} + template + inline void traverse(Func func, type_list) const { + for(const auto entt: *handler) { + if constexpr(std::is_invocable_v({}))...>) { + func(std::get *>(pools)->get(entt)...); + } else { + func(entt, std::get *>(pools)->get(entt)...); + } + }; + } + public: /*! @brief Underlying entity identifier. */ using entity_type = typename sparse_set::entity_type; @@ -5032,11 +5276,6 @@ public: * There are no guarantees on the order of the components. Use `begin` and * `end` if you want to iterate the group in the expected order. * - * @warning - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function always returns a pointer - * to that instance. - * * @tparam Component Type of component in which one is interested. * @return A pointer to the array of components. */ @@ -5164,14 +5403,13 @@ public: * @return The components assigned to the entity. */ template - std::conditional_t>, std::tuple> - get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(contains(entt)); if constexpr(sizeof...(Component) == 1) { return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple{get(entt)...}; + return std::tuple(entt))...>{get(entt)...}; } } @@ -5190,18 +5428,43 @@ public: * void(Get &...); * @endcode * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned during iterations. They can be caught only by copy or with + * const references. + * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template inline void each(Func func) const { - for(const auto entt: *handler) { - if constexpr(std::is_invocable_v...>) { - func(std::get *>(pools)->get(entt)...); - } else { - func(entt, std::get *>(pools)->get(entt)...); - } - }; + traverse(std::move(func), type_list{}); + } + + /** + * @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 + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void less(Func func) const { + using non_empty_get = type_list_cat_t, type_list<>, type_list>...>; + traverse(std::move(func), non_empty_get{}); } /** @@ -5284,26 +5547,61 @@ class basic_group, Owned...> { friend class basic_registry; template - using pool_type = std::conditional_t, const sparse_set>, sparse_set>; + using pool_type = std::conditional_t, const storage>, storage>; template using component_iterator_type = decltype(std::declval>().begin()); - template - const Component & from_index(const typename sparse_set::size_type index) { - if constexpr(std::disjunction_v...>) { - return std::get *>(pools)->raw()[index]; - } else { - return std::get *>(pools)->get(data()[index]); - } - } - // we could use pool_type *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug) - basic_group(const typename basic_registry::size_type *sz, sparse_set> *... owned, sparse_set> *... get) ENTT_NOEXCEPT + basic_group(const typename basic_registry::size_type *sz, storage> *... owned, storage> *... get) ENTT_NOEXCEPT : length{sz}, pools{owned..., get...} {} + template + decltype(auto) from_index(const typename sparse_set::size_type index) { + static_assert(!std::is_empty_v); + + if constexpr(std::disjunction_v...>) { + return std::as_const(*std::get *>(pools)).raw()[index]; + } else { + return std::as_const(*std::get *>(pools)).get(data()[index]); + } + } + + template + inline auto swap(int, const std::size_t lhs, const std::size_t rhs) + -> decltype(std::declval>().raw(), void()) { + auto *cpool = std::get *>(pools); + std::swap(cpool->raw()[lhs], cpool->raw()[rhs]); + cpool->swap(lhs, rhs); + } + + template + inline void swap(char, const std::size_t lhs, const std::size_t rhs) { + std::get *>(pools)->swap(lhs, rhs); + } + + template + inline void traverse(Func func, type_list, type_list) const { + auto raw = std::make_tuple((std::get *>(pools)->end() - *length)...); + [[maybe_unused]] auto data = std::get<0>(pools)->sparse_set::end() - *length; + + for(auto next = *length; next; --next) { + if constexpr(std::is_invocable_v({}))..., decltype(get({}))...>) { + if constexpr(sizeof...(Weak) == 0) { + func(*(std::get>(raw)++)...); + } else { + const auto entt = *(data++); + func(*(std::get>(raw)++)..., std::get *>(pools)->get(entt)...); + } + } else { + const auto entt = *(data++); + func(entt, *(std::get>(raw)++)..., std::get *>(pools)->get(entt)...); + } + } + } + public: /*! @brief Underlying entity identifier. */ using entity_type = typename sparse_set::entity_type; @@ -5363,11 +5661,6 @@ public: * There are no guarantees on the order of the components. Use `begin` and * `end` if you want to iterate the group in the expected order. * - * @warning - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function always returns a pointer - * to that instance. - * * @tparam Component Type of component in which one is interested. * @return A pointer to the array of components. */ @@ -5498,14 +5791,13 @@ public: * @return The components assigned to the entity. */ template - std::conditional_t>, std::tuple> - get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(contains(entt)); if constexpr(sizeof...(Component) == 1) { return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple{get(entt)...}; + return std::tuple(entt))...>{get(entt)...}; } } @@ -5524,27 +5816,44 @@ public: * void(Owned &..., Get &...); * @endcode * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned during iterations. They can be caught only by copy or with + * const references. + * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template inline void each(Func func) const { - auto raw = std::make_tuple((std::get *>(pools)->end() - *length)...); - [[maybe_unused]] auto data = std::get<0>(pools)->sparse_set::end() - *length; + traverse(std::move(func), type_list{}, type_list{}); + } - for(auto next = *length; next; --next) { - if constexpr(std::is_invocable_v..., std::add_lvalue_reference_t...>) { - if constexpr(sizeof...(Get) == 0) { - func(*(std::get>(raw)++)...); - } else { - const auto entt = *(data++); - func(*(std::get>(raw)++)..., std::get *>(pools)->get(entt)...); - } - } else { - const auto entt = *(data++); - func(entt, *(std::get>(raw)++)..., std::get *>(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 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 + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void less(Func func) const { + using non_empty_owned = type_list_cat_t, type_list<>, type_list>...>; + using non_empty_get = type_list_cat_t, type_list<>, type_list>...>; + traverse(std::move(func), non_empty_owned{}, non_empty_get{}); } /** @@ -5603,19 +5912,19 @@ public: }, std::forward(args)...); } else { algo(copy.rbegin(), copy.rend(), [compare = std::move(compare), this](const auto lhs, const auto rhs) { - return compare(from_index(lhs)..., from_index(rhs)...); + // useless this-> used to suppress a warning with clang + return compare(this->from_index(lhs)..., this->from_index(rhs)...); }, std::forward(args)...); } - for(size_type pos = 0, last = copy.size(); pos < last; ++pos) { + for(size_type pos{}, last = copy.size(); pos < last; ++pos) { auto curr = pos; auto next = copy[curr]; while(curr != next) { const auto lhs = copy[curr]; const auto rhs = copy[next]; - (std::swap(std::get *>(pools)->raw()[lhs], std::get *>(pools)->raw()[rhs]), ...); - (std::get *>(pools)->swap(lhs, rhs), ...); + (swap(0, lhs, rhs), ...); copy[curr] = curr; curr = next; next = copy[curr]; @@ -5647,8 +5956,12 @@ private: #include // #include "../config/config.h" +// #include "../core/type_traits.hpp" + // #include "sparse_set.hpp" +// #include "storage.hpp" + // #include "entity.hpp" // #include "fwd.hpp" @@ -5702,10 +6015,10 @@ class basic_view { friend class basic_registry; template - using pool_type = std::conditional_t, const sparse_set>, sparse_set>; + using pool_type = std::conditional_t, const storage>, storage>; template - using component_type = std::remove_reference_t>().get(0))>; + using component_iterator_type = decltype(std::declval>().begin()); using underlying_iterator_type = typename sparse_set::iterator_type; using unchecked_type = std::array *, (sizeof...(Component) - 1)>; @@ -5783,7 +6096,7 @@ class basic_view { }; // we could use pool_type *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug) - basic_view(sparse_set> *... ref) ENTT_NOEXCEPT + basic_view(storage> *... ref) ENTT_NOEXCEPT : pools{ref...} {} @@ -5800,23 +6113,43 @@ class basic_view { return other; } - template - void each(std::tuple pack, Func func) const { + template + inline decltype(auto) get([[maybe_unused]] component_iterator_type it, [[maybe_unused]] pool_type *cpool, [[maybe_unused]] const Entity entt) const ENTT_NOEXCEPT { + if constexpr(std::is_same_v) { + return *it; + } else { + return cpool->get(entt); + } + } + + template + void traverse(Func func, type_list, type_list) const { const auto end = std::get *>(pools)->sparse_set::end(); auto begin = std::get *>(pools)->sparse_set::begin(); - auto raw = std::get *>(pools)->begin(); - std::for_each(begin, end, [&pack, &func, &raw, this](const auto entity) { - std::get *>(pack) = &*raw++; + if constexpr(std::disjunction_v...>) { + std::for_each(begin, end, [raw = std::get *>(pools)->begin(), &func, this](const auto entity) mutable { + auto curr = raw++; - if(((std::get *>(pack) = std::get *>(pools)->try_get(entity)) && ...)) { - if constexpr(std::is_invocable_v...>) { - func(*std::get *>(pack)...); - } else { - func(entity, *std::get *>(pack)...); + if((std::get *>(pools)->has(entity) && ...)) { + if constexpr(std::is_invocable_v({}))...>) { + func(get(curr, std::get *>(pools), entity)...); + } else { + func(entity, get(curr, std::get *>(pools), entity)...); + } } - } - }); + }); + } else { + std::for_each(begin, end, [&func, this](const auto entity) mutable { + if((std::get *>(pools)->has(entity) && ...)) { + if constexpr(std::is_invocable_v({}))...>) { + func(std::get *>(pools)->get(entity)...); + } else { + func(entity, std::get *>(pools)->get(entity)...); + } + } + }); + } } public: @@ -5875,11 +6208,6 @@ public: * There are no guarantees on the order of the components. Use `begin` and * `end` if you want to iterate the view in the expected order. * - * @warning - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function always returns a pointer - * to that instance. - * * @tparam Comp Type of component in which one is interested. * @return A pointer to the array of components. */ @@ -5985,14 +6313,13 @@ public: * @return The components assigned to the entity. */ template - std::conditional_t>, std::tuple> - get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { + decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(contains(entt)); if constexpr(sizeof...(Comp) == 1) { return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple{get(entt)...}; + return std::tuple(entt))...>{get(entt)...}; } } @@ -6011,6 +6338,11 @@ public: * void(Component &...); * @endcode * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned during iterations. They can be caught only by copy or with + * const references. + * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ @@ -6041,13 +6373,79 @@ public: * It is no longer guaranteed that the performance is the best possible, but * there will be greater control over the order of iteration. * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned during iterations. They can be caught only by copy or with + * const references. + * * @tparam Comp Type of component to use to enforce the iteration order. * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template inline void each(Func func) const { - each(std::tuple_cat(std::tuple{}, std::conditional_t, std::tuple<>, std::tuple>{}...), std::move(func)); + using other_type = type_list_cat_t, type_list<>, type_list>...>; + traverse(std::move(func), other_type{}, type_list{}); + } + + /** + * @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 + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void less(Func func) const { + const auto *view = candidate(); + ((std::get *>(pools) == view ? less(std::move(func)) : void()), ...); + } + + /** + * @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 + * + * The pool of the suggested component is used to lead the iterations. The + * returned entities will therefore respect the order of the pool associated + * with that type.
+ * It is no longer guaranteed that the performance is the best possible, but + * there will be greater control over the order of iteration. + * + * @sa each + * + * @tparam Comp Type of component to use to enforce the iteration order. + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void less(Func func) const { + using other_type = type_list_cat_t, type_list<>, type_list>...>; + using non_empty_type = type_list_cat_t, type_list<>, type_list>...>; + traverse(std::move(func), other_type{}, non_empty_type{}); } private: @@ -6093,7 +6491,7 @@ class basic_view { /*! @brief A registry is allowed to create views. */ friend class basic_registry; - using pool_type = std::conditional_t, const sparse_set>, sparse_set>; + using pool_type = std::conditional_t, const storage>, storage>; basic_view(pool_type *ref) ENTT_NOEXCEPT : pool{ref} @@ -6101,7 +6499,7 @@ class basic_view { public: /*! @brief Type of component iterated by the view. */ - using raw_type = std::remove_reference_t().get(0))>; + using raw_type = Component; /*! @brief Underlying entity identifier. */ using entity_type = typename pool_type::entity_type; /*! @brief Unsigned integer type. */ @@ -6135,11 +6533,6 @@ public: * There are no guarantees on the order of the components. Use `begin` and * `end` if you want to iterate the view in the expected order. * - * @warning - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function always returns a pointer - * to that instance. - * * @return A pointer to the array of components. */ raw_type * raw() const ENTT_NOEXCEPT { @@ -6243,7 +6636,7 @@ public: * @param entt A valid entity identifier. * @return The component assigned to the entity. */ - raw_type & get(const entity_type entt) const ENTT_NOEXCEPT { + decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(contains(entt)); return pool->get(entt); } @@ -6263,12 +6656,17 @@ public: * void(Component &); * @endcode * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned during iterations. They can be caught only by copy or with + * const references. + * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template - void each(Func func) const { - if constexpr(std::is_invocable_v>) { + inline void each(Func func) const { + if constexpr(std::is_invocable_v) { std::for_each(pool->begin(), pool->end(), std::move(func)); } else { std::for_each(pool->sparse_set::begin(), pool->sparse_set::end(), [&func, raw = pool->begin()](const auto entt) mutable { @@ -6277,6 +6675,49 @@ public: } } + /** + * @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 its 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 in case the component isn't an empty one: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @endcode + * + * In case the component is an empty one instead, the following forms are + * accepted: + * + * @code{.cpp} + * void(const entity_type); + * void(); + * @endcode + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + inline void less(Func func) const { + if constexpr(std::is_empty_v) { + if constexpr(std::is_invocable_v) { + for(auto pos = pool->size(); pos; --pos) { + func(); + } + } else { + std::for_each(pool->sparse_set::begin(), pool->sparse_set::end(), std::move(func)); + } + } else { + each(std::move(func)); + } + } + private: pool_type *pool; }; @@ -6327,50 +6768,79 @@ class basic_registry { using traits_type = entt_traits; template - struct pool_wrapper: sparse_set { - sigh on_construct; - sigh on_replace; + struct pool_handler: storage { + using reference_type = std::conditional_t, const Component &, Component &>; + + sigh on_construct; + sigh on_replace; sigh on_destroy; - basic_registry *owner{}; void *group{}; + pool_handler() ENTT_NOEXCEPT = default; + + pool_handler(const storage &other) + : storage{other} + {} + template - Component & construct(const Entity entt, Args &&... args) { - auto &component = sparse_set::construct(entt, std::forward(args)...); - on_construct.publish(*owner, entt, component); - return component; + decltype(auto) assign(basic_registry ®istry, const Entity entt, Args &&... args) { + if constexpr(std::is_empty_v) { + storage::construct(entt); + on_construct.publish(registry, entt, Component{}); + return Component{std::forward(args)...}; + } else { + auto &component = storage::construct(entt, std::forward(args)...); + on_construct.publish(registry, entt, component); + return component; + } } template - Component * batch(It first, It last) { - auto *component = sparse_set::batch(first, last); + Component * batch(basic_registry ®istry, It first, It last) { + Component *component = nullptr; - if(!on_construct.empty()) { - std::for_each(first, last, [component, this](const auto entt) mutable { - on_construct.publish(*owner, entt, *(component++)); - }); + if constexpr(std::is_empty_v) { + storage::batch(first, last); + + if(!on_construct.empty()) { + std::for_each(first, last, [®istry, this](const auto entt) { + on_construct.publish(registry, entt, Component{}); + }); + } + } else { + component = storage::batch(first, last); + + if(!on_construct.empty()) { + std::for_each(first, last, [®istry, component, this](const auto entt) mutable { + on_construct.publish(registry, entt, *(component++)); + }); + } } return component; } - void destroy(const Entity entt) override { - on_destroy.publish(*owner, entt); - sparse_set::destroy(entt); + void remove(basic_registry ®istry, const Entity entt) { + on_destroy.publish(registry, entt); + storage::destroy(entt); } template - Component & replace(const Entity entt, Args &&... args) { - Component component{std::forward(args)...}; - on_replace.publish(*owner, entt, component); - auto &other = sparse_set::get(entt); - std::swap(other, component); - return other; + decltype(auto) replace(basic_registry ®istry, const Entity entt, Args &&... args) { + if constexpr(std::is_empty_v) { + ENTT_ASSERT((storage::has(entt))); + on_replace.publish(registry, entt, Component{}); + return Component{std::forward(args)...}; + } else { + Component component{std::forward(args)...}; + on_replace.publish(registry, entt, component); + return (storage::get(entt) = std::move(component)); + } } }; template - using pool_type = pool_wrapper>; + using pool_type = pool_handler>; template struct group_handler; @@ -6383,10 +6853,8 @@ class basic_registry { if(((std::is_same_v || reg.pool()->has(entt)) && ...) && !(reg.pool()->has(entt) || ...)) { this->construct(entt); } - } else { - static_assert(std::disjunction_v...>); - - if((reg.pool()->has(entt) && ...) && !((!std::is_same_v && reg.pool()->has(entt)) || ...)) { + } else if constexpr(std::disjunction_v...>) { + if((reg.pool()->has(entt) && ...) && ((std::is_same_v || !reg.pool()->has(entt)) && ...)) { this->construct(entt); } } @@ -6408,27 +6876,21 @@ class basic_registry { void maybe_valid_if(basic_registry ®, const Entity entt, const Args &...) { const auto cpools = std::make_tuple(reg.pool()...); - auto construct = [&cpools, entt, this]() { - const auto pos = this->owned++; - (std::swap(std::get *>(cpools)->get(entt), std::get *>(cpools)->raw()[pos]), ...); - (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entt), pos), ...); - }; - if constexpr(std::disjunction_v..., std::is_same...>) { if(((std::is_same_v || std::get *>(cpools)->has(entt)) && ...) && ((std::is_same_v || reg.pool()->has(entt)) && ...) && !(reg.pool()->has(entt) || ...)) { - construct(); + const auto pos = this->owned++; + (reg.swap(0, std::get *>(cpools), entt, pos), ...); } - } else { - static_assert(std::disjunction_v...>); - + } else if constexpr(std::disjunction_v...>) { if((std::get *>(cpools)->has(entt) && ...) && (reg.pool()->has(entt) && ...) - && !((!std::is_same_v && reg.pool()->has(entt)) || ...)) + && ((std::is_same_v || !reg.pool()->has(entt)) && ...)) { - construct(); + const auto pos = this->owned++; + (reg.swap(0, std::get *>(cpools), entt, pos), ...); } } } @@ -6439,14 +6901,15 @@ class basic_registry { if(std::get<0>(cpools)->has(entt) && std::get<0>(cpools)->sparse_set::get(entt) < this->owned) { const auto pos = --this->owned; - (std::swap(std::get *>(cpools)->get(entt), std::get *>(cpools)->raw()[pos]), ...); - (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entt), pos), ...); + (reg.swap(0, std::get *>(cpools), entt, pos), ...); } } }; struct pool_data { std::unique_ptr> pool; + std::unique_ptr> (* clone)(const sparse_set &); + void (* remove)(basic_registry &, const Entity); ENTT_ID_TYPE runtime_type; }; @@ -6480,6 +6943,18 @@ class basic_registry { ++available; } + template + inline auto swap(int, pool_type *cpool, const Entity entt, const std::size_t pos) + -> decltype(std::swap(cpool->get(entt), cpool->raw()[pos]), void()) { + std::swap(cpool->get(entt), cpool->raw()[pos]); + cpool->swap(cpool->sparse_set::get(entt), pos); + } + + template + inline void swap(char, pool_type *cpool, const Entity entt, const std::size_t pos) { + cpool->swap(cpool->sparse_set::get(entt), pos); + } + template inline const auto * pool() const ENTT_NOEXCEPT { const auto ctype = type(); @@ -6491,7 +6966,7 @@ class basic_registry { return it == pools.cend() ? nullptr : static_cast *>(it->pool.get()); } else { - return (ctype < skip_family_pools && pools[ctype].pool) ? static_cast *>(pools[ctype].pool.get()) : nullptr; + return ctype < skip_family_pools ? static_cast *>(pools[ctype].pool.get()) : nullptr; } } @@ -6524,87 +6999,25 @@ class basic_registry { } if(!pdata->pool) { - pdata->pool = std::make_unique>(); - static_cast &>(*pdata->pool).owner = this; pdata->runtime_type = ctype; + pdata->pool = std::make_unique>(); + + pdata->clone = +[](const sparse_set &other) -> std::unique_ptr> { + if constexpr(std::is_copy_constructible_v>) { + return std::make_unique>(static_cast &>(other)); + } else { + return nullptr; + } + }; + + pdata->remove = +[](basic_registry ®istry, const Entity entt) { + registry.pool()->remove(registry, entt); + }; } return static_cast *>(pdata->pool.get()); } - template - auto * assure(get_t, exclude_t) { - static_assert(sizeof...(Owned) + sizeof...(Get) > 0); - static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1); - using group_type = group_handler, type_list, Owned...>; - - const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }; - const ENTT_ID_TYPE types[] = { type()..., type()..., type()... }; - group_type *curr = nullptr; - - if(auto it = std::find_if(groups.begin(), groups.end(), [&extent, &types](auto &&gdata) { - return std::equal(std::begin(extent), std::end(extent), gdata.extent) && gdata.is_same(types); - }); it != groups.cend()) - { - curr = static_cast(it->group.get()); - } - - if(!curr) { - ENTT_ASSERT(!(owned() || ...)); - - groups.push_back(group_data{ - { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }, - decltype(group_data::group){new group_type, +[](void *gptr) { delete static_cast(gptr); }}, - +[](const ENTT_ID_TYPE *other) { - const std::size_t ctypes[] = { type()..., type()..., type()... }; - return std::equal(std::begin(ctypes), std::end(ctypes), other); - } - }); - - const auto cpools = std::make_tuple(assure()..., assure()..., assure()...); - curr = static_cast(groups.back().group.get()); - - ((std::get *>(cpools)->group = curr), ...); - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...); - - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template discard_if<>>(curr), ...); - - (std::get *>(cpools)->on_destroy.sink().template connect<&group_type::template maybe_valid_if>(curr), ...); - (std::get *>(cpools)->on_construct.sink().template connect<&group_type::template discard_if>(curr), ...); - - const auto *cpool = std::min({ - static_cast *>(std::get *>(cpools))..., - static_cast *>(std::get *>(cpools))... - }, [](const auto *lhs, const auto *rhs) { - return lhs->size() < rhs->size(); - }); - - // we cannot iterate backwards because we want to leave behind valid entities in case of owned types - std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools](const auto entity) { - if((std::get *>(cpools)->has(entity) && ...) - && (std::get *>(cpools)->has(entity) && ...) - && !(std::get *>(cpools)->has(entity) || ...)) - { - if constexpr(sizeof...(Owned) == 0) { - curr->construct(entity); - } else { - const auto pos = curr->owned++; - (std::swap(std::get *>(cpools)->get(entity), std::get *>(cpools)->raw()[pos]), ...); - (std::get *>(cpools)->swap(std::get *>(cpools)->sparse_set::get(entity), pos), ...); - } - } - }); - } - - if constexpr(sizeof...(Owned) == 0) { - return static_cast *>(curr); - } else { - return &curr->owned; - } - } - public: /*! @brief Underlying entity identifier. */ using entity_type = typename traits_type::entity_type; @@ -6821,10 +7234,6 @@ public: /** * @brief Returns the actual version for an entity identifier. * - * In case entity identifers are stored around, this function can be used to - * know if they are still valid or if the entity has been destroyed and - * potentially recycled. - * * @warning * Attempting to use an entity that doesn't belong to the registry results * in undefined behavior. An entity belongs to the registry even if it has @@ -6864,8 +7273,7 @@ public: * just created otherwise. */ template - std::conditional_t> - create() { + decltype(auto) create() { entity_type entity; if(available) { @@ -6884,7 +7292,7 @@ public: if constexpr(sizeof...(Component) == 0) { return entity; } else { - return { entity, assign(entity)... }; + return std::tuple(entity))...>{entity, assign(entity)...}; } } @@ -6902,8 +7310,7 @@ public: * sorted the same of the entities otherwise. */ template - std::conditional_t> - create(It first, It last) { + auto create(It first, It last) { static_assert(std::is_convertible_v::value_type>); const auto length = size_type(std::distance(first, last)); const auto sz = std::min(available, length); @@ -6930,7 +7337,7 @@ public: }); if constexpr(sizeof...(Component) > 0) { - return { assure()->batch(first, last)... }; + return std::make_tuple(assure()->batch(*this, first, last)...); } } @@ -6961,7 +7368,7 @@ public: for(auto pos = pools.size(); pos; --pos) { if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->has(entity)) { - pdata.pool->destroy(entity); + pdata.remove(*this, entity); } }; @@ -6982,9 +7389,9 @@ public: for(auto pos = pools.size(); pos; --pos) { if(auto &pdata = pools[pos-1]; pdata.pool) { - std::for_each(first, last, [&pdata](const auto entity) { + std::for_each(first, last, [&pdata, this](const auto entity) { if(pdata.pool->has(entity)) { - pdata.pool->destroy(entity); + pdata.remove(*this, entity); } }); } @@ -7019,9 +7426,9 @@ public: * @return A reference to the newly created component. */ template - Component & assign(const entity_type entity, Args &&... args) { + decltype(auto) assign(const entity_type entity, [[maybe_unused]] Args &&... args) { ENTT_ASSERT(valid(entity)); - return assure()->construct(entity, std::forward(args)...); + return assure()->assign(*this, entity, std::forward(args)...); } /** @@ -7040,7 +7447,7 @@ public: template void remove(const entity_type entity) { ENTT_ASSERT(valid(entity)); - pool()->destroy(entity); + pool()->remove(*this, entity); } /** @@ -7077,23 +7484,25 @@ public: * @return References to the components owned by the entity. */ template - decltype(auto) get([[maybe_unused]] const entity_type entity) const ENTT_NOEXCEPT { + decltype(auto) get([[maybe_unused]] const entity_type entity) const { ENTT_ASSERT(valid(entity)); if constexpr(sizeof...(Component) == 1) { return (pool()->get(entity), ...); } else { - return std::tuple &...>{get(entity)...}; + return std::tuple(entity))...>{get(entity)...}; } } /*! @copydoc get */ template inline decltype(auto) get([[maybe_unused]] const entity_type entity) ENTT_NOEXCEPT { + ENTT_ASSERT(valid(entity)); + if constexpr(sizeof...(Component) == 1) { - return (const_cast(std::as_const(*this).template get(entity)), ...); + return (pool()->get(entity), ...); } else { - return std::tuple{get(entity)...}; + return std::tuple(entity))...>{get(entity)...}; } } @@ -7122,11 +7531,10 @@ public: * @return Reference to the component owned by the entity. */ template - Component & get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT { + decltype(auto) get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT { ENTT_ASSERT(valid(entity)); auto *cpool = assure(); - auto *comp = cpool->try_get(entity); - return comp ? *comp : cpool->construct(entity, std::forward(args)...); + return cpool->has(entity) ? cpool->get(entity) : cpool->assign(*this, entity, std::forward(args)...); } /** @@ -7184,8 +7592,9 @@ public: * @return A reference to the newly created component. */ template - Component & replace(const entity_type entity, Args &&... args) { - return pool()->replace(entity, std::forward(args)...); + decltype(auto) replace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); + return pool()->replace(*this, entity, std::forward(args)...); } /** @@ -7211,9 +7620,10 @@ public: * @return A reference to the newly created component. */ template - Component & assign_or_replace(const entity_type entity, Args &&... args) { + decltype(auto) assign_or_replace(const entity_type entity, Args &&... args) { + ENTT_ASSERT(valid(entity)); auto *cpool = assure(); - return cpool->has(entity) ? cpool->replace(entity, std::forward(args)...) : cpool->construct(entity, std::forward(args)...); + return cpool->has(entity) ? cpool->replace(*this, entity, std::forward(args)...) : cpool->assign(*this, entity, std::forward(args)...); } /** @@ -7233,6 +7643,11 @@ public: * Listeners are invoked **after** the component has been assigned to the * entity. The order of invocation of the listeners isn't guaranteed. * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned through signals. They can be caught only by copy or with + * const references. + * * @sa sink * * @tparam Component Type of component of which to get the sink. @@ -7259,6 +7674,11 @@ public: * Listeners are invoked **before** the component has been replaced. The * order of invocation of the listeners isn't guaranteed. * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned through signals. They can be caught only by copy or with + * const references. + * * @sa sink * * @tparam Component Type of component of which to get the sink. @@ -7286,6 +7706,11 @@ public: * Listeners are invoked **before** the component has been removed from the * entity. The order of invocation of the listeners isn't guaranteed. * + * @note + * Empty types aren't explicitly instantiated. Therefore, temporary objects + * are returned through signals. They can be caught only by copy or with + * const references. + * * @sa sink * * @tparam Component Type of component of which to get the sink. @@ -7409,7 +7834,7 @@ public: ENTT_ASSERT(valid(entity)); if(auto *cpool = assure(); cpool->has(entity)) { - cpool->destroy(entity); + cpool->remove(*this, entity); } } @@ -7427,10 +7852,8 @@ public: // no group set, otherwise the signal wouldn't be empty cpool->reset(); } else { - const sparse_set &base = *cpool; - - for(const auto entity: base) { - cpool->destroy(entity); + for(const auto entity: static_cast &>(*cpool)) { + cpool->remove(*this, entity); } } } @@ -7497,8 +7920,8 @@ public: ENTT_ASSERT(valid(entity)); bool orphan = true; - for(std::size_t i = {}, last = pools.size(); i < last && orphan; ++i) { - const auto &pdata = pools[i]; + for(std::size_t pos{}, last = pools.size(); pos < last && orphan; ++pos) { + const auto &pdata = pools[pos]; orphan = !(pdata.pool && pdata.pool->has(entity)); } @@ -7615,7 +8038,78 @@ public: */ template inline entt::basic_group, Owned...> group(get_t, exclude_t = {}) { - return { assure...>(entt::get...>, exclude...>), pool()..., pool()... }; + static_assert(sizeof...(Owned) + sizeof...(Get) > 0); + static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1); + + using handler_type = group_handler, type_list, Owned...>; + + const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }; + const ENTT_ID_TYPE types[] = { type()..., type()..., type()... }; + handler_type *curr = nullptr; + + if(auto it = std::find_if(groups.begin(), groups.end(), [&extent, &types](auto &&gdata) { + return std::equal(std::begin(extent), std::end(extent), gdata.extent) && gdata.is_same(types); + }); it != groups.cend()) + { + curr = static_cast(it->group.get()); + } + + if(!curr) { + ENTT_ASSERT(!(owned() || ...)); + + groups.push_back(group_data{ + { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }, + decltype(group_data::group){new handler_type{}, +[](void *gptr) { delete static_cast(gptr); }}, + +[](const ENTT_ID_TYPE *other) { + const std::size_t ctypes[] = { type()..., type()..., type()... }; + return std::equal(std::begin(ctypes), std::end(ctypes), other); + } + }); + + const auto cpools = std::make_tuple(assure()..., assure()..., assure()...); + curr = static_cast(groups.back().group.get()); + + ((std::get *>(cpools)->group = curr), ...); + (std::get *>(cpools)->on_construct.sink().template connect<&handler_type::template maybe_valid_if>(curr), ...); + (std::get *>(cpools)->on_destroy.sink().template connect<&handler_type::template discard_if<>>(curr), ...); + + (std::get *>(cpools)->on_construct.sink().template connect<&handler_type::template maybe_valid_if>(curr), ...); + (std::get *>(cpools)->on_destroy.sink().template connect<&handler_type::template discard_if<>>(curr), ...); + + (std::get *>(cpools)->on_destroy.sink().template connect<&handler_type::template maybe_valid_if>(curr), ...); + (std::get *>(cpools)->on_construct.sink().template connect<&handler_type::template discard_if>(curr), ...); + + const auto *cpool = std::min({ + static_cast *>(std::get *>(cpools))..., + static_cast *>(std::get *>(cpools))... + }, [](const auto *lhs, const auto *rhs) { + return lhs->size() < rhs->size(); + }); + + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools, this](const auto entity) { + if((std::get *>(cpools)->has(entity) && ...) + && (std::get *>(cpools)->has(entity) && ...) + && !(std::get *>(cpools)->has(entity) || ...)) + { + if constexpr(sizeof...(Owned) == 0) { + curr->construct(entity); + // suppress warnings + (void)this; + } else { + const auto pos = curr->owned++; + // useless this-> used to suppress a warning with clang + (this->swap(0, std::get *>(cpools), entity, pos), ...); + } + } + }); + } + + if constexpr(sizeof...(Owned) == 0) { + return { static_cast *>(curr), pool()... }; + } else { + return { &curr->owned, pool()... , pool()... }; + } } /*! @copydoc group */ @@ -7628,7 +8122,7 @@ public: /*! @copydoc group */ template inline entt::basic_group, Owned...> group(exclude_t = {}) { - return { assure...>(entt::get<>, exclude...>), pool()... }; + return group(entt::get<>, exclude); } /*! @copydoc group */ @@ -7712,9 +8206,10 @@ public: for(auto pos = pools.size(); pos; --pos) { if(auto &pdata = pools[pos-1]; pdata.pool && (!sizeof...(Component) || ... || (pdata.runtime_type == type()))) { auto &curr = other.pools[pos-1]; - curr.pool = pdata.pool->clone(); + curr.clone = pdata.clone; + curr.pool = curr.clone(*pdata.pool); curr.runtime_type = pdata.runtime_type; - ENTT_ASSERT(curr.pool); + ENTT_ASSERT(sizeof...(Component) == 0 || curr.pool); } } @@ -7843,6 +8338,23 @@ public: }), vars.end()); } + /** + * @brief Binds an object to the context of the registry. + * + * In case the context doesn't contain the given object, the parameters + * provided are used to construct it. + * + * @tparam Type Type of object to set. + * @tparam Args Types of arguments to use to construct the object. + * @param args Parameters to use to initialize the object. + * @return Reference to the object. + */ + template + Type & ctx_or_set(Args &&... args) { + auto *type = try_ctx(); + return type ? *type : set(std::forward(args)...); + } + /** * @brief Returns a pointer to an object in the context of the registry. * @tparam Type Type of object to get. @@ -7992,7 +8504,7 @@ struct basic_actor { * @return A reference to the newly created component. */ template - Component & assign(Args &&... args) { + decltype(auto) assign(Args &&... args) { return reg->template assign_or_replace(entt, std::forward(args)...); } @@ -8134,7 +8646,7 @@ private: /** - * @brief Deduction guideline. + * @brief Deduction guide. * * It allows to deduce the constness of a registry directly from the instance * provided to the constructor. @@ -8187,7 +8699,7 @@ private: /** - * @brief Deduction guideline. + * @brief Deduction guide. * * It allows to deduce the constness of a registry directly from the instance * provided to the constructor. @@ -8797,6 +9309,8 @@ private: // #include "entity/sparse_set.hpp" +// #include "entity/storage.hpp" + // #include "entity/view.hpp" // #include "locator/locator.hpp" @@ -8839,7 +9353,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -8998,7 +9512,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -9050,7 +9564,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -9160,9 +9674,21 @@ public: return helper(traits_type::offset, wrapper.str); } + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + inline static hash_type to_value(const char *str, std::size_t size) ENTT_NOEXCEPT { + ENTT_ID_TYPE partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /*! @brief Constructs an empty hashed string. */ constexpr hashed_string() ENTT_NOEXCEPT - : hash{}, str{nullptr} + : str{nullptr}, hash{} {} /** @@ -9181,17 +9707,16 @@ public: */ template constexpr hashed_string(const char (&curr)[N]) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, curr)}, str{curr} + : str{curr}, hash{helper(traits_type::offset, curr)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const char *`. - * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, wrapper.str)}, str{wrapper.str} + : str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)} {} /** @@ -9229,8 +9754,8 @@ public: } private: - hash_type hash; const char *str; + hash_type hash; }; @@ -9487,8 +10012,7 @@ auto find_if(Op op, const Node *curr) ENTT_NOEXCEPT { template auto find_if(Op op, const meta_type_node *node) ENTT_NOEXCEPT --> decltype(find_if(op, node->*Member)) -{ +-> decltype(find_if(op, node->*Member)) { decltype(find_if(op, node->*Member)) ret = nullptr; if(node) { @@ -9576,8 +10100,7 @@ class meta_any { template inline static auto compare(int, const Type &lhs, const Type &rhs) - -> decltype(lhs == rhs, bool{}) - { + -> decltype(lhs == rhs, bool{}) { return lhs == rhs; } @@ -10518,7 +11041,7 @@ public: */ template inline bool set(meta_handle handle, std::size_t index, Type &&value) const { - assert(index < node->type()->extent); + ENTT_ASSERT(index < node->type()->extent); return node->set(handle, index, std::forward(value)); } @@ -10546,7 +11069,7 @@ public: * @return A meta any containing the value of the underlying element. */ inline meta_any get(meta_handle handle, std::size_t index) const ENTT_NOEXCEPT { - assert(index < node->type()->extent); + ENTT_ASSERT(index < node->type()->extent); return node->get(handle, index); } @@ -11664,8 +12187,7 @@ class meta_factory { template auto unregister_all(int) - -> decltype((internal::meta_info::type->*Member)->prop, void()) - { + -> decltype((internal::meta_info::type->*Member)->prop, void()) { while(internal::meta_info::type->*Member) { auto node = internal::meta_info::type->*Member; internal::meta_info::type->*Member = node->next; @@ -12262,7 +12784,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -12955,7 +13477,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -13007,7 +13529,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -13117,9 +13639,21 @@ public: return helper(traits_type::offset, wrapper.str); } + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + inline static hash_type to_value(const char *str, std::size_t size) ENTT_NOEXCEPT { + ENTT_ID_TYPE partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /*! @brief Constructs an empty hashed string. */ constexpr hashed_string() ENTT_NOEXCEPT - : hash{}, str{nullptr} + : str{nullptr}, hash{} {} /** @@ -13138,17 +13672,16 @@ public: */ template constexpr hashed_string(const char (&curr)[N]) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, curr)}, str{curr} + : str{curr}, hash{helper(traits_type::offset, curr)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const char *`. - * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, wrapper.str)}, str{wrapper.str} + : str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)} {} /** @@ -13186,8 +13719,8 @@ public: } private: - hash_type hash; const char *str; + hash_type hash; }; @@ -13301,30 +13834,25 @@ public: return *resource; } - /** - * @brief Casts a handle and gets a reference to the managed resource. - * - * @warning - * The behavior is undefined if the handle doesn't contain a resource.
- * An assertion will abort the execution at runtime in debug mode if the - * handle is empty. - */ - inline operator const Resource &() const ENTT_NOEXCEPT { return get(); } + /*! @copydoc get */ + Resource & get() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).get()); + } - /** - * @brief Dereferences a handle to obtain the managed resource. - * - * @warning - * The behavior is undefined if the handle doesn't contain a resource.
- * An assertion will abort the execution at runtime in debug mode if the - * handle is empty. - * - * @return A reference to the managed resource. - */ + /*! @copydoc get */ + inline operator const Resource & () const ENTT_NOEXCEPT { return get(); } + + /*! @copydoc get */ + inline operator Resource & () ENTT_NOEXCEPT { return get(); } + + /*! @copydoc get */ inline const Resource & operator *() const ENTT_NOEXCEPT { return get(); } + /*! @copydoc get */ + inline Resource & operator *() ENTT_NOEXCEPT { return get(); } + /** - * @brief Gets a pointer to the managed resource from a handle. + * @brief Gets a pointer to the managed resource. * * @warning * The behavior is undefined if the handle doesn't contain a resource.
@@ -13339,6 +13867,11 @@ public: return resource.get(); } + /*! @copydoc operator-> */ + inline Resource * operator->() ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).operator->()); + } + /** * @brief Returns true if a handle contains a resource, false otherwise. * @return True if the handle contains a resource, false otherwise. @@ -13454,10 +13987,10 @@ public: resource_cache() = default; /*! @brief Default move constructor. */ - resource_cache(resource_cache &&) ENTT_NOEXCEPT = default; + resource_cache(resource_cache &&) = default; /*! @brief Default move assignment operator. @return This cache. */ - resource_cache & operator=(resource_cache &&) ENTT_NOEXCEPT = default; + resource_cache & operator=(resource_cache &&) = default; /** * @brief Number of resources managed by a cache. @@ -13666,7 +14199,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -13922,7 +14455,7 @@ bool operator!=(const delegate &lhs, const delegate /** - * @brief Deduction guideline. + * @brief Deduction guide. * * It allows to deduce the function type of the delegate directly from a * function provided to the constructor. @@ -13935,7 +14468,7 @@ delegate(connect_arg_t) ENTT_NOEXCEPT /** - * @brief Deduction guideline. + * @brief Deduction guide. * * It allows to deduce the function type of the delegate directly from a member * or a free function with payload provided to the constructor. @@ -14003,7 +14536,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -14096,7 +14629,7 @@ using maybe_atomic_t = Type; #ifndef ENTT_PAGE_SIZE #define ENTT_PAGE_SIZE 32768 -#endif +#endif // ENTT_PAGE_SIZE #ifndef ENTT_DISABLE_ASSERT @@ -14206,9 +14739,21 @@ public: return helper(traits_type::offset, wrapper.str); } + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifer. + * @param size Length of the string to hash. + * @return The numeric representation of the string. + */ + inline static hash_type to_value(const char *str, std::size_t size) ENTT_NOEXCEPT { + ENTT_ID_TYPE partial{traits_type::offset}; + while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } + return partial; + } + /*! @brief Constructs an empty hashed string. */ constexpr hashed_string() ENTT_NOEXCEPT - : hash{}, str{nullptr} + : str{nullptr}, hash{} {} /** @@ -14227,17 +14772,16 @@ public: */ template constexpr hashed_string(const char (&curr)[N]) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, curr)}, str{curr} + : str{curr}, hash{helper(traits_type::offset, curr)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const char *`. - * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT - : hash{helper(traits_type::offset, wrapper.str)}, str{wrapper.str} + : str{wrapper.str}, hash{helper(traits_type::offset, wrapper.str)} {} /** @@ -14275,8 +14819,8 @@ public: } private: - hash_type hash; const char *str; + hash_type hash; }; @@ -14311,9 +14855,97 @@ constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::si namespace entt { -/*! @brief A class to use to push around lists of types, nothing more. */ +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the given type list. + */ template -struct type_list {}; +struct type_list { + /*! @brief Unsigned integer type. */ + static constexpr auto size = sizeof...(Type); +}; + + +/*! @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::disjunction_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 Traits class used mainly to push things across boundaries. */ diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 9d393c369..aac698c5b 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -1,4 +1,4 @@ -#ifndef ENTT_ENTITY_REGISTRY_HPP +#ifndef ENTT_ENTITY_REGISTRY_HPP #define ENTT_ENTITY_REGISTRY_HPP