diff --git a/src/entt/entity/view.hpp b/src/entt/entity/view.hpp index 834fd6aa1..3a265833f 100644 --- a/src/entt/entity/view.hpp +++ b/src/entt/entity/view.hpp @@ -192,29 +192,30 @@ template * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, components are + * added or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. */ template class basic_view; /** - * @brief Multi component view. + * @brief General purpose view. * - * Multi component views iterate over those entities that are at least in the - * given storage. During initialization, a multi component view looks at the - * number of entities available for each component and uses the smallest set in - * order to get a performance boost when iterating. + * 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. * - * @b Important - * - * Iterators aren't invalidated if: - * - * * New elements are added to the storage. - * * The entity currently pointed is modified (for example, components are added - * or removed from it). - * * The entity currently pointed is destroyed. - * - * In all other cases, modifying the storage iterated by the view in any way - * invalidates all the iterators. + * @sa basic_view * * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. @@ -559,23 +560,304 @@ private: }; /** - * @brief Single component view specialization. + * @brief Single type view specialization. * - * Single component views are specialized in order to get a boost in terms of - * performance. This kind of views can access the underlying data structure - * directly and avoid superfluous checks. + * This specialization is a refinement of the general purpose view and offers a + * slightly revised interface to suit individual storage models. * - * @b Important + * @sa basic_view * - * Iterators aren't invalidated if: + * @tparam Get Type of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t> { + template + friend class basic_view; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + +public: + /*! @brief Common type among all storage types. */ + using common_type = std::common_type_t; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_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{} {} + + /** + * @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...} { + } + + /** + * @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 Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return storage<0>(); + } + + /** + * @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 { + return std::get(std::tuple_cat(pools, internal::filter_as_tuple(filter, std::index_sequence_for{}))); + } + + /** + * @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 == 0u) { + std::get(pools) = &elem; + } else { + std::get(filter) = &elem; + } + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept { + return handle() ? handle()->size() : size_type{}; + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return handle() ? iterator{handle()->begin(), handle()->end(), std::array{}, filter} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return handle() ? iterator{handle()->end(), handle()->end(), std::array{}, filter} : iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(auto *view = handle(); view) { + auto it = view->rbegin(); + for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} + return it == view->rend() ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{handle()->find(entt), handle()->end(), std::array{}, 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. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return handle() && internal::fully_initialized(filter); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return handle() && handle()->contains(entt) && internal::none_of(filter, entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Elem Type of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return get<0>(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Index Index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return std::get<0>(pools)->get_as_tuple(entt); + } else { + return std::get(pools)->get(entt); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(auto *view = storage<0>(); view) { + for(auto curr: view->each()) { + if(const auto entt = std::get<0>(curr); (!Get::traits_type::in_place_delete || (entt != tombstone)) && internal::none_of(filter, entt)) { + if constexpr(is_applicable_v) { + std::apply(func, curr); + } else { + func(std::get<1>(curr)); + } + } + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + if(auto *view = storage<0>(); view) { + auto elem = view->each(); + iterator from{elem.begin().base(), elem.end().base(), std::array{}, filter}; + iterator to{elem.end().base(), elem.end().base(), std::array{}, filter}; + return iterable{internal::extended_view_iterator{from, pools}, internal::extended_view_iterator{to, pools}}; + } + + return {}; + } + + /** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack( + std::tuple_cat(pools, other.pools), + std::tuple_cat(internal::filter_as_tuple(filter, std::index_sequence_for{}), internal::filter_as_tuple(other.filter, std::index_sequence_for{})), + std::index_sequence_for{}); + } + +private: + std::tuple pools; + std::array filter; +}; + +/** + * @brief Storage view specialization. * - * * New elements are added to the storage. - * * The entity currently pointed is modified (for example, components are added - * or removed from it). - * * The entity currently pointed is destroyed. + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. * - * In all other cases, modifying the storage iterated by the view in any way - * invalidates all the iterators. + * @sa basic_view * * @tparam Get Type of storage iterated by the view. */