#ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include <array> #include <iterator> #include <tuple> #include <type_traits> #include <utility> #include "../config/config.h" #include "../core/iterator.hpp" #include "../core/type_traits.hpp" #include "component.hpp" #include "entity.hpp" #include "fwd.hpp" #include "sparse_set.hpp" #include "storage.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template<typename Type, std::size_t Get, std::size_t Exclude> class view_iterator final { using iterator_type = typename Type::const_iterator; [[nodiscard]] bool valid() const noexcept { return ((Get != 0u) || (*it != tombstone)) && std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); } 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<const Type *, Get> all_of, std::array<const Type *, Exclude> none_of) noexcept : it{curr}, last{to}, pools{all_of}, filter{none_of} { while(it != last && !valid()) { ++it; } } view_iterator &operator++() noexcept { while(++it != last && !valid()) {} 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<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> friend constexpr bool operator==(const view_iterator<LhsType, LhsArgs...> &, const view_iterator<RhsType, RhsArgs...> &) noexcept; private: iterator_type it; iterator_type last; std::array<const Type *, Get> pools; std::array<const Type *, Exclude> filter; }; template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> [[nodiscard]] constexpr bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) noexcept { return lhs.it == rhs.it; } template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> [[nodiscard]] constexpr bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) noexcept { return !(lhs == rhs); } template<typename It, typename... Type> struct extended_view_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Type>().get_as_tuple({})...)); using pointer = input_iterator_pointer<value_type>; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr extended_view_iterator() : it{}, pools{} {} extended_view_iterator(It from, std::tuple<Type *...> storage) : it{from}, pools{storage} {} 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*(); } template<typename... Lhs, typename... Rhs> friend bool constexpr operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) noexcept; private: It it; std::tuple<Type *...> pools; }; template<typename... Lhs, typename... Rhs> [[nodiscard]] constexpr bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) noexcept { return lhs.it == rhs.it; } template<typename... Lhs, typename... Rhs> [[nodiscard]] constexpr bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &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. */ template<typename, typename, typename> class basic_view; /** * @brief Multi component 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. * * @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 pools iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. */ template<typename... Get, typename... Exclude> class basic_view<get_t<Get...>, exclude_t<Exclude...>> { using underlying_type = std::common_type_t<typename Get::entity_type..., typename Exclude::entity_type...>; using basic_common_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>; template<typename, typename, typename> friend class basic_view; template<typename Type> static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type...>>; [[nodiscard]] auto opaque_check_set() const noexcept { std::array<const base_type *, sizeof...(Get) - 1u> other{}; std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); return other; } [[nodiscard]] auto filter_as_array() const noexcept { return std::apply([](const auto *...curr) { return std::array<const base_type *, sizeof...(Exclude)>{curr...}; }, filter); } template<std::size_t Curr, std::size_t Other, typename... Args> [[nodiscard]] auto dispatch_get(const std::tuple<underlying_type, Args...> &curr) const { if constexpr(Curr == Other) { return std::forward_as_tuple(std::get<Args>(curr)...); } else { return storage<Other>().get_as_tuple(std::get<0>(curr)); } } [[nodiscard]] auto reject(const underlying_type entt) const noexcept { return std::apply([entt](const auto *...curr) { return (curr->contains(entt) || ...); }, filter); } template<std::size_t Curr, typename Func, std::size_t... Index> void each(Func &func, std::index_sequence<Index...>) const { for(const auto curr: storage<Curr>().each()) { if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || storage<Index>().contains(entt)) && ...) && !reject(entt)) { if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Curr, Index>(curr)...)); } else { std::apply(func, std::tuple_cat(dispatch_get<Curr, Index>(curr)...)); } } } } template<typename Func, std::size_t... Index> void pick_and_each(Func &func, std::index_sequence<Index...> seq) const { ((&storage<Index>() == view ? each<Index>(func, seq) : void()), ...); } public: /*! @brief Underlying entity identifier. */ using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Bidirectional iterator type. */ using iterator = internal::view_iterator<base_type, sizeof...(Get) - 1u, sizeof...(Exclude)>; /*! @brief Iterable view type. */ using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, Get...>>; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : pools{}, filter{}, view{} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. * @param exclude The storage for the types used to filter the view. */ basic_view(Get &...value, Exclude &...exclude) noexcept : pools{&value...}, filter{&exclude...}, view{[](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }(&value...)} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. * @param exclude The storage for the types used to filter the view. */ basic_view(std::tuple<Get &...> value, std::tuple<Exclude &...> exclude = {}) noexcept : pools{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, value)}, filter{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, exclude)}, view{std::apply([](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }, pools)} {} /** * @brief Creates a new view driven by a given component in its iterations. * @tparam Type Type of component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ template<typename Type> [[nodiscard]] basic_view use() const noexcept { return use<index_of<Type>>(); } /** * @brief Creates a new view driven by a given component in its iterations. * @tparam Index Index of the component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ template<std::size_t Index> [[nodiscard]] basic_view use() const noexcept { basic_view other{*this}; other.view = &storage<Index>(); return other; } /** * @brief Updates the internal leading view if required. * @return A newly created and internally optimized view. */ [[nodiscard]] basic_view refresh() const noexcept { return std::apply([](auto *...elem) { return basic_view{*elem...}; }, std::tuple_cat(pools, filter)); } /** * @brief Returns the leading storage of a view. * @return The leading storage of the view. */ [[nodiscard]] const base_type &handle() const noexcept { return *view; } /** * @brief Returns the storage for a given component type. * @tparam Comp Type of component of which to return the storage. * @return The storage for the given component type. */ template<typename Type> [[nodiscard]] decltype(auto) storage() const noexcept { return storage<index_of<Type>>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template<std::size_t Index> [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get<Index>(pools); } /** * @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->size(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points 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 iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()}; } /** * @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 { auto it = view->rbegin(); for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} return it == view->rend() ? null : *it; } /** * @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_as_array()} : 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 properly initialized. * @return True if the view is properly 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 std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); } /** * @brief Returns the components assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template<typename... Type> [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); } else if constexpr(sizeof...(Type) == 1) { return (storage<index_of<Type>>().get(entt), ...); } else { return std::tuple_cat(storage<index_of<Type>>().get_as_tuple(entt)...); } } /** * @brief Returns the components assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam First Index of a component to get. * @tparam Other Indexes of other components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template<std::size_t First, std::size_t... Other> [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Other) == 0) { return storage<First>().get(entt); } else { return std::tuple_cat(storage<First>().get_as_tuple(entt), storage<Other>().get_as_tuple(entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.<br/> * The signature of the function must be equivalent to one of the following * forms: * * @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<typename Func> void each(Func func) const { pick_and_each(func, std::index_sequence_for<Get...>{}); } /** * @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 (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<typename... OGet, typename... OExclude> [[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept { return std::make_from_tuple<basic_view<get_t<Get..., OGet...>, exclude_t<Exclude..., OExclude...>>>( std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, filter, other.filter))); } private: std::tuple<Get *...> pools; std::tuple<Exclude *...> filter; const base_type *view; }; /** * @brief Single component 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. * * @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 pool iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Type of storage iterated by the view. */ template<typename Get> class basic_view<get_t<Get>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<typename Get::value_type>::in_place_delete>>> { template<typename, typename, typename> friend class basic_view; public: /*! @brief Underlying entity identifier. */ using entity_type = typename Get::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = typename Get::base_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<Get>().each()); /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : pools{}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view(Get &ref) noexcept : pools{&ref}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view(std::tuple<Get &> ref, std::tuple<> = {}) noexcept : pools{&std::get<0>(ref)}, filter{} {} /** * @brief Returns the leading storage of a view. * @return The leading storage of the view. */ [[nodiscard]] const base_type &handle() const noexcept { return storage(); } /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template<typename Type = typename Get::value_type> [[nodiscard]] decltype(auto) storage() const noexcept { static_assert(std::is_same_v<std::remove_const_t<Type>, typename Get::value_type>, "Invalid component type"); return storage<0>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template<std::size_t Index> [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get<Index>(pools); } /** * @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 handle().size(); } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return handle().empty(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points 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().begin(); } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { return handle().end(); } /** * @brief Returns an iterator to the first entity of the reversed view. * * The returned iterator points 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 handle().rbegin(); } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * * The returned iterator points to the entity following the last entity of * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const noexcept { return handle().rend(); } /** * @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 : *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 : *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 contains(entt) ? handle().find(entt) : end(); } /** * @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 properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return std::get<0>(pools) != 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 handle().contains(entt); } /** * @brief Returns the component assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam Type Type or index of the component to get. * @param entt A valid identifier. * @return The component assigned to the entity. */ template<typename... Type> [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return storage().get_as_tuple(entt); } else { static_assert((std::is_same_v<std::remove_const_t<Type>, typename Get::value_type> && ...), "Invalid component type"); return storage().get(entt); } } /*! @copydoc get */ template<std::size_t Index> [[nodiscard]] decltype(auto) get(const entity_type entt) const { return storage().get(entt); } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a reference to the component if it's a non-empty one. * The _constness_ of the component is as requested.<br/> * The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &); * void(typename Type &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template<typename Func> void each(Func func) const { if constexpr(is_applicable_v<Func, decltype(*each().begin())>) { for(const auto pack: each()) { std::apply(func, pack); } } else if constexpr(ignore_as_empty_v<typename Get::value_type>) { for(size_type pos{}, last = size(); pos < last; ++pos) { func(); } } else { for(auto &&component: storage()) { func(component); } } } /** * @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().each(); } /** * @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<typename... OGet, typename... OExclude> [[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept { return std::make_from_tuple<basic_view<get_t<Get, OGet...>, exclude_t<OExclude...>>>( std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, other.filter))); } private: std::tuple<Get *> pools; std::tuple<> filter; }; /** * @brief Deduction guide. * @tparam Type Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ template<typename... Type> basic_view(Type &...storage) -> basic_view<get_t<Type...>, 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<typename... Get, typename... Exclude> basic_view(std::tuple<Get &...>, std::tuple<Exclude &...> = {}) -> basic_view<get_t<Get...>, exclude_t<Exclude...>>; } // namespace entt #endif