#ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include #include #include #include #include #include "../config/config.h" #include "../core/iterator.hpp" #include "../core/type_traits.hpp" #include "entity.hpp" #include "fwd.hpp" namespace entt { /*! @cond TURN_OFF_DOXYGEN */ namespace internal { template [[nodiscard]] bool all_of_but(const std::size_t index, const Type *const *it, const std::size_t len, const Entity entt) noexcept { std::size_t pos{}; for(; (pos != index) && it[pos]->contains(entt); ++pos) {} if(pos == index) { for(++pos; (pos != len) && it[pos]->contains(entt); ++pos) {} } return pos == len; } template [[nodiscard]] bool none_of(const Type *const *it, const std::size_t len, const Entity entt) noexcept { std::size_t pos{}; for(; (pos != len) && !(it[pos] && it[pos]->contains(entt)); ++pos) {} return pos == len; } template [[nodiscard]] bool fully_initialized(const Type *const *it, const std::size_t len) noexcept { std::size_t pos{}; for(; (pos != len) && it[pos]; ++pos) {} return pos == len; } template [[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { Result elem{}; // friend-initialization, avoid multiple calls to refresh elem.pools = {view.template storage()..., other.template storage()...}; elem.filter = {view.template storage()..., other.template storage()...}; elem.refresh(); return elem; } template class view_iterator final { using iterator_type = typename Type::const_iterator; [[nodiscard]] bool valid(const typename iterator_type::value_type entt) const noexcept { return ((Get != 1u) || (entt != tombstone)) && all_of_but(index, pools.data(), Get, entt) && none_of(filter.data(), Exclude, entt); } public: using value_type = typename iterator_type::value_type; using pointer = typename iterator_type::pointer; using reference = typename iterator_type::reference; using difference_type = typename iterator_type::difference_type; using iterator_category = std::forward_iterator_tag; constexpr view_iterator() noexcept : it{}, last{}, pools{}, filter{}, index{} {} view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl, const std::size_t idx) noexcept : it{curr}, last{to}, pools{value}, filter{excl}, index{idx} { while(it != last && !valid(*it)) { ++it; } } view_iterator &operator++() noexcept { while(++it != last && !valid(*it)) {} return *this; } view_iterator operator++(int) noexcept { view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] pointer operator->() const noexcept { return &*it; } [[nodiscard]] reference operator*() const noexcept { return *operator->(); } template friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; private: iterator_type it; iterator_type last; std::array pools; std::array filter; std::size_t index; }; template [[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { return !(lhs == rhs); } template struct extended_view_iterator final { using iterator_type = It; using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; using iterator_concept = std::forward_iterator_tag; constexpr extended_view_iterator() : it{}, pools{} {} extended_view_iterator(iterator_type from, std::tuple value) : it{from}, pools{value} {} extended_view_iterator &operator++() noexcept { return ++it, *this; } extended_view_iterator operator++(int) noexcept { extended_view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const noexcept { return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); } [[nodiscard]] pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr iterator_type base() const noexcept { return it; } template friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; private: It it; std::tuple pools; }; template [[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /*! @endcond */ /** * @brief View implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. * * @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 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_common_view { template friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); protected: /*! @cond TURN_OFF_DOXYGEN */ basic_common_view() noexcept = default; basic_common_view(std::array value, std::array excl) noexcept : pools{value}, filter{excl}, leading{}, index{Get} { unchecked_refresh(); } void use(const std::size_t pos) noexcept { if(leading) { index = pos; leading = pools[index]; } } void unchecked_refresh() noexcept { index = 0u; for(size_type pos{1u}; pos < Get; ++pos) { if(pools[pos]->size() < pools[index]->size()) { index = pos; } } leading = pools[index]; } /*! @endcond */ 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 { size_type pos = (leading != nullptr) * Get; for(; pos < Get && pools[pos] != nullptr; ++pos) {} if(pos == Get) { unchecked_refresh(); } } /** * @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 leading; } /** * @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 leading ? leading->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 leading ? iterator{leading->begin(0), leading->end(0), pools, filter, index} : 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 leading ? iterator{leading->end(0), leading->end(0), pools, filter, index} : 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(leading) { auto it = leading->rbegin(0); const auto last = leading->rend(0); for(; it != last && !contains(*it); ++it) {} return it == last ? 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{leading->find(entt), leading->end(), pools, filter, index} : end(); } /** * @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 leading && internal::fully_initialized(filter.data(), Exclude); } /** * @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 { if(leading) { const auto idx = leading->find(entt).index(); return (!(idx < 0 || idx > leading->begin(0).index())) && internal::all_of_but(index, pools.data(), Get, entt) && internal::none_of(filter.data(), Exclude, entt); } return false; } protected: /*! @cond TURN_OFF_DOXYGEN */ std::array pools{}; std::array filter{}; const common_type *leading{}; size_type index{Get}; /*! @endcond */ }; /** * @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_but(this->index, this->pools.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() == base_type::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{{&value...}, {&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 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; base_type::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. * @tparam Other Other types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { return get, index_of...>(entt); } /** * @brief Returns the components assigned to the given entity. * @tparam Index Indexes of the components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Index) == 0) { 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 (storage()->get(entt), ...); } else { return std::tuple_cat(storage()->get_as_tuple(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(base_type::handle() != nullptr) { pick_and_each(func, std::index_sequence_for{}); } } /** * @brief Returns an iterable object to use to _visit_ a view. * * The iterable object returns a tuple that contains the current entity and * a set of references to its non-empty components. The _constness_ of the * components is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { const auto as_pools = storage(std::index_sequence_for{}); return {internal::extended_view_iterator{base_type::begin(), as_pools}, internal::extended_view_iterator{base_type::end(), as_pools}}; } /** * @brief Combines two views in a _more specific_ one. * @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, exclude_t>>( *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); } }; /** * @brief Basic storage view implementation. * @warning For internal use only, backward compatibility not guaranteed. * @tparam Type Common type among all storage types. */ template class basic_storage_view { protected: /*! @cond TURN_OFF_DOXYGEN */ basic_storage_view() noexcept = default; basic_storage_view(const Type *value) noexcept : leading{value} {} /*! @endcond */ public: /*! @brief Common type among all storage types. */ using common_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = typename common_type::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename common_type::reverse_iterator; /** * @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 leading; } /** * @brief Returns the number of entities that have the given component. * @return Number of entities that have the given component. */ [[nodiscard]] size_type size() const noexcept { return leading ? leading->size() : size_type{}; } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return !leading || leading->empty(); } /** * @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 leading ? leading->begin() : 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 leading ? leading->end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed view. * * If the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { return leading ? leading->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const noexcept { return leading ? leading->rend() : reverse_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 { return empty() ? null : *leading->begin(); } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { return empty() ? null : *leading->rbegin(); } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { return leading ? leading->find(entt) : iterator{}; } /** * @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 (leading != nullptr); } /** * @brief Checks if a view contains an entity. * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return leading && leading->contains(entt); } protected: /*! @cond TURN_OFF_DOXYGEN */ const common_type *leading{}; /*! @endcond */ }; /** * @brief Storage view specialization. * * This specialization offers a boost in terms of performance. It can access the * underlying data structure directly and avoid superfluous checks. * * @sa basic_view * * @tparam Get Type of storage iterated by the view. */ template class basic_view, exclude_t<>, std::void_t>>: public basic_storage_view { using base_type = basic_storage_view; 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 Random access iterator type. */ using iterator = typename base_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable view type. */ using iterable = decltype(std::declval().each()); /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : base_type{} {} /** * @brief Constructs a view from a storage class. * @param value The storage for the type to iterate. */ basic_view(Get &value) noexcept : base_type{&value} { } /** * @brief Constructs a view from a storage class. * @param value The storage for the type to iterate. */ basic_view(std::tuple value, std::tuple<> = {}) noexcept : basic_view{std::get<0>(value)} {} /** * @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 { static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); return storage<0>(); } /** * @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 { static_assert(Index == 0u, "Index out of bounds"); return static_cast(const_cast *>(this->leading)); } /** * @brief Assigns a storage to a view. * @param elem A storage to assign to the view. */ void storage(Get &elem) noexcept { storage<0>(elem); } /** * @brief Assigns a storage to a view. * @tparam Index Index of the storage to assign to the view. * @param elem A storage to assign to the view. */ template void storage(Get &elem) noexcept { static_assert(Index == 0u, "Index out of bounds"); this->leading = &elem; } /** * @brief Returns the component assigned to the given entity. * @param entt A valid identifier. * @return The component assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { return storage()->get(entt); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type operator[](const size_type pos) const { return base_type::begin()[pos]; } /** * @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 storage()->get_as_tuple(entt); } else { return storage()->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(typename 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 *elem = storage(); elem) { if constexpr(is_applicable_veach().begin())>) { for(const auto pack: elem->each()) { std::apply(func, pack); } } else if constexpr(std::is_invocable_vbegin())>) { for(auto &&component: *elem) { func(component); } } else { for(size_type pos = elem->size(); pos; --pos) { func(); } } } } /** * @brief Returns an iterable object to use to _visit_ a view. * * The iterable object returns a tuple that contains the current entity and * a reference to its component if it's a non-empty one. The _constness_ of * the component is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { auto *elem = storage(); return elem ? elem->each() : iterable{}; } /** * @brief Combines two views in a _more specific_ one. * @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, exclude_t>>( *this, other, std::index_sequence_for{}, std::index_sequence_for<>{}, std::index_sequence_for{}, std::index_sequence_for{}); } }; /** * @brief Deduction guide. * @tparam Type Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ template basic_view(Type &...storage) -> basic_view, exclude_t<>>; /** * @brief Deduction guide. * @tparam Get Types of components iterated by the view. * @tparam Exclude Types of components used to filter the view. */ template basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; } // namespace entt #endif