diff --git a/src/entt/entity/group.hpp b/src/entt/entity/group.hpp index ff7c4dbcf..4ad738d8f 100644 --- a/src/entt/entity/group.hpp +++ b/src/entt/entity/group.hpp @@ -17,325 +17,6 @@ namespace entt { -/** - * @brief Group range. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error, but for a few reasonable cases. - */ -template -class group_range; - - -/** - * @brief Non-owning group range. - * - * Iterable object to use to _visit_ a group. - * - * @sa group - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Exclude Types of components used to filter the group. - * @tparam Get Type of components observed by the group. - */ -template -class group_range, get_t> { - /*! @brief A group is allowed to create ranges. */ - friend class basic_group, get_t>; - - // I could have used std::conditional_t ... - template - struct pool { using type = storage; }; - - // ... if only MSVC didn't have a bug ... - template - struct pool { using type = const storage>; }; - - // ... that forces me to do the same in a worse way! :( - template - using pool_type = typename pool::type; - - class range_iterator { - friend class group_range, entt::get_t>; - - using it_type = typename sparse_set::iterator; - using pool_type = decltype(std::tuple_cat(std::declval, std::tuple *>>>()...)); - - range_iterator(it_type from, pool_type ref) ENTT_NOEXCEPT - : it{from}, - pools{ref} - {} - - public: - using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple>>()... - )); - using pointer = void; - using reference = value_type; - using iterator_category = std::input_iterator_tag; - - range_iterator() ENTT_NOEXCEPT = default; - - range_iterator & operator++() ENTT_NOEXCEPT { - return ++it, *this; - } - - range_iterator operator++(int) ENTT_NOEXCEPT { - range_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools); - } - - [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { - return other.it == it; - } - - [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - private: - it_type it; - pool_type pools; - }; - - group_range(const sparse_set &ref, std::tuple *...> gpools) - : handler{&ref}, - pools{gpools} - {} - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Input iterator type. */ - using iterator = range_iterator; - - /** - * @brief Returns an iterator to the first element. - * - * The returned iterator points to the first element. If the range is empty, - * the returned iterator will be equal to `end()`. - * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the first element. - */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return iterator{handler->begin(), std::tuple_cat([](auto *cpool) { - if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...)}; - } - - /** - * @brief Returns an iterator that is past the last element. - * - * The returned iterator points to the element following the last element. - * Attempting to dereference the returned iterator results in undefined - * behavior. - * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the element following the last element that has - * the given components. - */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return iterator{handler->end(), std::tuple_cat([](auto *cpool) { - if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...)}; - } - -private: - const sparse_set *handler; - std::tuple *...> pools; -}; - - -/** - * @brief Owning group range specialization. - * - * Iterable object to use to _visit_ a group. - * - * @sa group - * - * @tparam Entity A valid entity type (see entt_traits for more details). - * @tparam Exclude Types of components used to filter the group. - * @tparam Get Types of components observed by the group. - * @tparam Owned Types of components owned by the group. - */ -template -class group_range, get_t, Owned...> { - /*! @brief A group is allowed to create ranges. */ - friend class basic_group, get_t, Owned...>; - - // I could have used std::conditional_t ... - template - struct pool { using type = storage; }; - - // ... if only MSVC didn't have a bug ... - template - struct pool { using type = const storage>; }; - - // ... that forces me to do the same in a worse way! :( - template - using pool_type = typename pool::type; - - class range_iterator { - friend class group_range, entt::get_t, Owned...>; - - using it_type = typename sparse_set::iterator; - using owned_type = decltype(std::tuple_cat(std::declval, std::tuple>().begin())>>>()...)); - using get_type = decltype(std::tuple_cat(std::declval, std::tuple *>>>()...)); - - range_iterator(it_type from, owned_type oref, get_type gref) ENTT_NOEXCEPT - : it{from}, - owned{oref}, - get{gref} - {} - - public: - using difference_type = std::ptrdiff_t; - using value_type = decltype(std::tuple_cat( - std::declval>(), - std::declval, std::tuple>>()..., - std::declval, std::tuple>>()... - )); - using pointer = void; - using reference = value_type; - using iterator_category = std::input_iterator_tag; - - range_iterator() ENTT_NOEXCEPT = default; - - range_iterator & operator++() ENTT_NOEXCEPT { - return ++it, std::apply([](auto &&... curr) { (++curr, ...); }, owned), *this; - } - - range_iterator operator++(int) ENTT_NOEXCEPT { - range_iterator orig = *this; - return ++(*this), orig; - } - - [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { - return std::tuple_cat( - std::make_tuple(*it), - std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr...); }, owned), - std::apply([entt = *it](auto &&... curr) { return std::forward_as_tuple(curr->get(entt)...); }, get) - ); - } - - [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { - return other.it == it; - } - - [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - private: - it_type it; - owned_type owned; - get_type get; - }; - - group_range(std::tuple *..., pool_type *...> cpools, const std::size_t &extent) - : pools{cpools}, - length{&extent} - {} - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Input iterator type. */ - using iterator = range_iterator; - - /** - * @brief Returns an iterator to the first element. - * - * The returned iterator points to the first element. If the range is empty, - * the returned iterator will be equal to `end()`. - * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the first element. - */ - [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { - return iterator{ - std::get<0>(pools)->sparse_set::end() - *length, - std::tuple_cat([this](auto *cpool) { - if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool->end() - *length); - } - }(std::get *>(pools))...), - std::tuple_cat([](auto *cpool) { - if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...) - }; - } - - /** - * @brief Returns an iterator that is past the last element. - * - * The returned iterator points to the element following the last element. - * Attempting to dereference the returned iterator results in undefined - * behavior. - * - * @note - * Iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the element following the last element that has - * the given components. - */ - [[nodiscard]] iterator end() const ENTT_NOEXCEPT { - return iterator{ - std::get<0>(pools)->sparse_set::end(), - std::tuple_cat([](auto *cpool) { - if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool->end()); - } - }(std::get *>(pools))...), - std::tuple_cat([](auto *cpool) { - if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { - return std::make_tuple(); - } else { - return std::make_tuple(cpool); - } - }(std::get *>(pools))...) - }; - } - -private: - const std::tuple *..., pool_type *...> pools; - const std::size_t *length; -}; - - /** * @brief Group. * @@ -399,6 +80,89 @@ class basic_group, get_t> { template using pool_type = typename pool::type; + class group_range { + friend class basic_group, get_t>; + + class range_iterator { + friend class group_range; + + using it_type = typename sparse_set::iterator; + using pool_type = decltype(std::tuple_cat(std::declval, std::tuple *>>>()...)); + + range_iterator(it_type from, pool_type ref) ENTT_NOEXCEPT + : it{from}, + pools{ref} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple>>()... + )); + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + range_iterator() ENTT_NOEXCEPT = default; + + range_iterator & operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + range_iterator operator++(int) ENTT_NOEXCEPT { + range_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools); + } + + [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + it_type it; + pool_type pools; + }; + + group_range(const sparse_set &ref, std::tuple *...> gpools) + : handler{&ref}, + pools{gpools} + {} + + public: + [[nodiscard]] auto begin() const ENTT_NOEXCEPT { + return range_iterator{handler->begin(), std::tuple_cat([](auto *cpool) { + if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...)}; + } + + [[nodiscard]] auto end() const ENTT_NOEXCEPT { + return range_iterator{handler->end(), std::tuple_cat([](auto *cpool) { + if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...)}; + } + + private: + const sparse_set *handler; + std::tuple *...> pools; + }; + basic_group(sparse_set &ref, pool_type &... gpool) ENTT_NOEXCEPT : handler{&ref}, pools{&gpool...} @@ -678,8 +442,8 @@ public: * * @return An iterable object to use to _visit_ the group. */ - group_range, get_t> each() const { - return { *handler, pools }; + [[nodiscard]] auto each() const ENTT_NOEXCEPT { + return group_range{*handler, pools}; } /** @@ -827,6 +591,117 @@ class basic_group, get_t, Owned...> { template using component_iterator = decltype(std::declval>().begin()); + class group_range { + friend class basic_group, get_t, Owned...>; + + class range_iterator { + friend class group_range; + + using it_type = typename sparse_set::iterator; + using owned_type = decltype(std::tuple_cat(std::declval, std::tuple>>>()...)); + using get_type = decltype(std::tuple_cat(std::declval, std::tuple *>>>()...)); + + range_iterator(it_type from, owned_type oref, get_type gref) ENTT_NOEXCEPT + : it{from}, + owned{oref}, + get{gref} + {} + + public: + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat( + std::declval>(), + std::declval, std::tuple>>()..., + std::declval, std::tuple>>()... + )); + using pointer = void; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + range_iterator() ENTT_NOEXCEPT = default; + + range_iterator & operator++() ENTT_NOEXCEPT { + return ++it, std::apply([](auto &&... curr) { (++curr, ...); }, owned), *this; + } + + range_iterator operator++(int) ENTT_NOEXCEPT { + range_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return std::tuple_cat( + std::make_tuple(*it), + std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr...); }, owned), + std::apply([entt = *it](auto &&... curr) { return std::forward_as_tuple(curr->get(entt)...); }, get) + ); + } + + [[nodiscard]] bool operator==(const range_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const range_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + it_type it; + owned_type owned; + get_type get; + }; + + group_range(std::tuple *..., pool_type *...> cpools, const std::size_t &extent) + : pools{cpools}, + length{&extent} + {} + + public: + [[nodiscard]] auto begin() const ENTT_NOEXCEPT { + return range_iterator{ + std::get<0>(pools)->sparse_set::end() - *length, + std::tuple_cat([this](auto *cpool) { + if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool->end() - *length); + } + }(std::get *>(pools))...), + std::tuple_cat([](auto *cpool) { + if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...) + }; + } + + [[nodiscard]] auto end() const ENTT_NOEXCEPT { + return range_iterator{ + std::get<0>(pools)->sparse_set::end(), + std::tuple_cat([](auto *cpool) { + if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool->end()); + } + }(std::get *>(pools))...), + std::tuple_cat([](auto *cpool) { + if constexpr(ENTT_IS_EMPTY(typename std::decay_t::object_type)) { + return std::make_tuple(); + } else { + return std::make_tuple(cpool); + } + }(std::get *>(pools))...) + }; + } + + private: + const std::tuple *..., pool_type *...> pools; + const std::size_t *length; + }; + basic_group(const std::size_t &extent, pool_type &... opool, pool_type &... gpool) ENTT_NOEXCEPT : pools{&opool..., &gpool...}, length{&extent} @@ -1108,8 +983,8 @@ public: * * @return An iterable object to use to _visit_ the group. */ - group_range, get_t, Owned...> each() const { - return { pools, *length }; + [[nodiscard]] auto each() const ENTT_NOEXCEPT { + return group_range{pools, *length}; } /**