#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 * Internal details not to be documented. */ namespace internal { template [[nodiscard]] auto all_of(const Type *elem, const std::size_t len, const Entity entt) noexcept { std::size_t pos{}; for(; pos < len && elem[pos]->contains(entt); ++pos) {} return pos == len; } template [[nodiscard]] auto none_of(const Type *elem, const std::size_t len, const Entity entt) noexcept { std::size_t pos{}; for(; pos < len && !(elem[pos] && elem[pos]->contains(entt)); ++pos) {} return pos == len; } template [[nodiscard]] auto fully_initialized(const Type *elem, const std::size_t len) noexcept { std::size_t pos{}; for(; pos < len && elem[pos] != nullptr; ++pos) {} return pos == len; } template [[nodiscard]] auto view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { Type 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 != 0u) || (entt != tombstone)) && (all_of(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{} {} view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl) noexcept : it{curr}, last{to}, pools{value}, filter{excl} { 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; }; 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 /** * Internal details not to be documented. * @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 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> { template friend auto 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>; [[nodiscard]] auto opaque_check_set() const noexcept { std::array other{}; std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); return other; } 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); } 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)) && ((Curr == Index || std::get(pools)->contains(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{}, 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...}, 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 { if(view) { view = std::get(pools); } } /*! @brief Updates the internal leading view if required. */ void refresh() noexcept { if(view || std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) { 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 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. */ [[nodiscard]] size_type size_hint() const noexcept { return view ? view->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 view ? iterator{view->begin(0), view->end(0), opaque_check_set(), 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 view ? iterator{view->end(0), view->end(0), opaque_check_set(), 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(view) { auto it = view->rbegin(0); const auto last = view->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{view->find(entt), view->end(), opaque_check_set(), 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 std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools) && internal::fully_initialized(filter.data(), filter.size()); } /** * @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(view) { auto check = opaque_check_set(); const auto idx = view->find(entt).index(); return (!(idx < 0 || idx > view->begin(0).index())) && internal::all_of(check.data(), check.size(), entt) && internal::none_of(filter.data(), filter.size(), entt); } return false; } /** * @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)...); }, pools); } else if constexpr(sizeof...(Index) == 1) { return (std::get(pools)->get(entt), ...); } else { return std::tuple_cat(std::get(pools)->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 { view ? pick_and_each(func, std::index_sequence_for{}) : void(); } /** * @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 { return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), 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{}); } private: std::tuple pools; std::array filter; const common_type *view; }; /** * @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: /*! @brief Common type among all storage types. */ using common_type = typename Get::base_type; /*! @brief Underlying entity identifier. */ using entity_type = typename Get::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 Iterable view type. */ using iterable = decltype(std::declval().each()); /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : view{} {} /** * @brief Constructs a view from a storage class. * @param value The storage for the type to iterate. */ basic_view(Get &value) noexcept : view{&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 leading storage of a view, if any. * @return The leading storage of the view. */ [[nodiscard]] const common_type *handle() const noexcept { return storage(); } /** * @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 view; } /** * @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 view; } /** * @brief Assigns a storage to a view. * @param elem A storage to assign to the view. */ void storage(Get &elem) noexcept { view = &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"); view = &elem; } /** * @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 view ? view->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 !view || view->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 view ? view->common_type::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 view ? view->common_type::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 view ? view->common_type::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 view ? view->common_type::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 : *view->common_type::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 : *view->common_type::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 view ? view->find(entt) : iterator{}; } /** * @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. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @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 Checks if a view is fully initialized. * @return True if the view is fully initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return (view != 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 view && view->contains(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 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(view) { if constexpr(is_applicable_veach().begin())>) { for(const auto pack: view->each()) { std::apply(func, pack); } } else if constexpr(std::is_invocable_vbegin())>) { for(auto &&component: *view) { func(component); } } else { for(size_type pos = view->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 { return storage() ? storage()->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{}); } private: Get *view; }; /** * @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