diff --git a/src/entt/entity/view.hpp b/src/entt/entity/view.hpp index bc180fe09..0178ffbaa 100644 --- a/src/entt/entity/view.hpp +++ b/src/entt/entity/view.hpp @@ -207,129 +207,61 @@ template class basic_view; /** - * @brief General purpose view. - * - * This view visits all entities that are at least in the given storage. During - * initialization, it also looks at the number of elements available for each - * storage and uses the smallest set in order to get a performance boost. - * - * @sa basic_view - * - * @tparam Get Types of storage iterated by the view. - * @tparam Exclude Types of storage used to filter the view. + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. */ -template -class basic_view, exclude_t> { - template - friend Result internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); +template +class basic_common_view { + template + friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); - using base_type = std::common_type_t; - using underlying_type = typename base_type::entity_type; - - template - static constexpr std::size_t index_of = type_list_index_v, type_list>; - - void opaque_check_set() noexcept { - std::apply([this, pos = 0u](const auto *...curr) mutable { ((curr == view ? void() : void(check[pos++] = curr)), ...); }, pools); - } - - void unchecked_refresh() noexcept { - view = std::get<0>(pools); - std::apply([this](auto *, auto *...other) { ((this->view = other->size() < this->view->size() ? other : this->view), ...); }, pools); - opaque_check_set(); - } - - template - [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { - if constexpr(Curr == Other) { - return std::forward_as_tuple(std::get(curr)...); - } else { - return std::get(pools)->get_as_tuple(std::get<0>(curr)); - } - } - - template - void each(Func &func, std::index_sequence) const { - for(const auto curr: std::get(pools)->each()) { - if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of(check.data(), check.size(), entt) && internal::none_of(filter.data(), filter.size(), entt)) { - if constexpr(is_applicable_v{}, std::declval().get({})))>) { - std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); - } else { - std::apply(func, std::tuple_cat(dispatch_get(curr)...)); - } - } - } - } - - template - void pick_and_each(Func &func, std::index_sequence seq) const { - ((std::get(pools) == view ? each(func, seq) : void()), ...); - } - -public: - /*! @brief Common type among all storage types. */ - using common_type = base_type; - /*! @brief Underlying entity identifier. */ - using entity_type = underlying_type; - /*! @brief Unsigned integer type. */ - using size_type = std::size_t; - /*! @brief Bidirectional iterator type. */ - using iterator = internal::view_iterator; - /*! @brief Iterable view type. */ - using iterable = iterable_adaptor>; - - /*! @brief Default constructor to use to create empty, invalid views. */ - basic_view() noexcept - : pools{}, - filter{}, - check{}, - view{} {} - - /** - * @brief Constructs a view from a set of storage classes. - * @param value The storage for the types to iterate. - * @param excl The storage for the types used to filter the view. - */ - basic_view(Get &...value, Exclude &...excl) noexcept - : pools{&value...}, - filter{&excl...}, - check{}, - view{} { - unchecked_refresh(); - } - - /** - * @brief Constructs a view from a set of storage classes. - * @param value The storage for the types to iterate. - * @param excl The storage for the types used to filter the view. - */ - basic_view(std::tuple value, std::tuple excl = {}) noexcept - : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} - - /** - * @brief Forces a view to use a given component to drive iterations - * @tparam Type Type of component to use to drive iterations. - */ - template - void use() noexcept { - use>(); - } - - /** - * @brief Forces a view to use a given component to drive iterations - * @tparam Index Index of the component to use to drive iterations. - */ - template - void use() noexcept { +protected: + void use(const std::size_t pos) noexcept { if(view) { - view = std::get(pools); + view = pools[pos]; opaque_check_set(); } } + void opaque_check_set() noexcept { + for(size_type pos{}, curr{}; pos < Get; ++pos) { + if(pools[pos] != view) { + check[curr++] = pools[pos]; + } + } + } + + void unchecked_refresh() noexcept { + view = pools[0u]; + + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < view->size()) { + view = pools[pos]; + } + } + + opaque_check_set(); + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::view_iterator; + /*! @brief Updates the internal leading view if required. */ void refresh() noexcept { - if(view || std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) { + size_type pos = (view != nullptr) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { unchecked_refresh(); } } @@ -342,57 +274,6 @@ public: return view; } - /** - * @brief Returns the storage for a given component type, if any. - * @tparam Type Type of component of which to return the storage. - * @return The storage for the given component type. - */ - template - [[nodiscard]] auto *storage() const noexcept { - return storage>(); - } - - /** - * @brief Returns the storage for a given index, if any. - * @tparam Index Index of the storage to return. - * @return The storage for the given index. - */ - template - [[nodiscard]] auto *storage() const noexcept { - if constexpr(Index < sizeof...(Get)) { - return std::get(pools); - } else { - using type = type_list_element_t>; - return static_cast(const_cast *>(filter[Index - sizeof...(Get)])); - } - } - - /** - * @brief Assigns a storage to a view. - * @tparam Type Type of storage to assign to the view. - * @param elem A storage to assign to the view. - */ - template - void storage(Type &elem) noexcept { - storage>(elem); - } - - /** - * @brief Assigns a storage to a view. - * @tparam Index Index of the storage to assign to the view. - * @tparam Type Type of storage to assign to the view. - * @param elem A storage to assign to the view. - */ - template - void storage(Type &elem) noexcept { - if constexpr(Index < sizeof...(Get)) { - std::get(pools) = &elem; - refresh(); - } else { - std::get(filter) = &elem; - } - } - /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. @@ -456,15 +337,6 @@ public: return contains(entt) ? iterator{view->find(entt), view->end(), check, filter} : end(); } - /** - * @brief Returns the components assigned to the given entity. - * @param entt A valid identifier. - * @return The components assigned to the given entity. - */ - [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { - return get(entt); - } - /** * @brief Checks if a view is fully initialized. * @return True if the view is fully initialized, false otherwise. @@ -487,6 +359,181 @@ public: return false; } +protected: + std::array pools{}; + std::array filter{}; + std::array check{}; + const common_type *view{}; +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t>: public basic_common_view, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view, sizeof...(Get), sizeof...(Exclude)>; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + auto storage(std::index_sequence) const noexcept { + return std::make_tuple(storage()...); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return storage()->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of(this->check.data(), sizeof...(Get), entt) && internal::none_of(this->filter.data(), sizeof...(Exclude), entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + ((storage() == this->handle() ? each(func, seq) : void()), ...); + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Bidirectional iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{} { + this->pools = {&value...}; + this->filter = {&excl...}; + this->unchecked_refresh(); + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Type Type of component to use to drive iterations. + */ + template + void use() noexcept { + use>(); + } + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Index Index of the component to use to drive iterations. + */ + template + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + + if constexpr(Index < sizeof...(Get)) { + return static_cast(const_cast *>(this->pools[Index])); + } else { + return static_cast(const_cast *>(this->filter[Index - sizeof...(Get)])); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v> &>, "Unexpected type"); + + if constexpr(Index < sizeof...(Get)) { + this->pools[Index] = &elem; + this->refresh(); + } else { + this->filter[Index - sizeof...(Get)] = &elem; + } + } + + /** + * @brief Returns the components assigned to the given entity. + * @param entt A valid identifier. + * @return The components assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return get(entt); + } + /** * @brief Returns the components assigned to the given entity. * @tparam Type Type of the component to get. @@ -508,11 +555,11 @@ public: template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Index) == 0) { - return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, storage(std::index_sequence_for{})); } else if constexpr(sizeof...(Index) == 1) { - return (std::get(pools)->get(entt), ...); + return (storage()->get(entt), ...); } else { - return std::tuple_cat(std::get(pools)->get_as_tuple(entt)...); + return std::tuple_cat(storage()->get_as_tuple(entt)...); } } @@ -533,7 +580,9 @@ public: */ template void each(Func func) const { - view ? pick_and_each(func, std::index_sequence_for{}) : void(); + if(this->handle() != nullptr) { + pick_and_each(func, std::index_sequence_for{}); + } } /** @@ -546,7 +595,8 @@ public: * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { - return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; + const auto as_pools = storage(std::index_sequence_for{}); + return {internal::extended_view_iterator{this->begin(), as_pools}, internal::extended_view_iterator{this->end(), as_pools}}; } /** @@ -561,12 +611,6 @@ public: return internal::view_pack, exclude_t>>( *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); } - -private: - std::tuple pools; - std::array filter; - std::array check; - const common_type *view; }; /**