#ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include #include #include #include #include #include #include "../config/config.h" #include "../core/type_traits.hpp" #include "component.hpp" #include "entity.hpp" #include "fwd.hpp" #include "sparse_set.hpp" #include "storage.hpp" #include "utility.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class view_iterator final { using basic_common_type = basic_sparse_set::value_type>; [[nodiscard]] bool valid() const { const auto entt = *it; return Policy::accept(entt) && std::apply([entt](const auto *... curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt](const auto *... curr) { return (!curr->contains(entt) && ...); }, filter); } public: using iterator_type = It; using difference_type = typename std::iterator_traits::difference_type; using value_type = typename std::iterator_traits::value_type; using pointer = typename std::iterator_traits::pointer; using reference = typename std::iterator_traits::reference; using iterator_category = std::bidirectional_iterator_tag; view_iterator() ENTT_NOEXCEPT : first{}, last{}, it{}, pools{}, filter{} {} view_iterator(It from, It to, It curr, std::array all_of, std::array none_of) ENTT_NOEXCEPT : first{from}, last{to}, it{curr}, pools{all_of}, filter{none_of} { if(it != last && !valid()) { ++(*this); } } view_iterator & operator++() ENTT_NOEXCEPT { while(++it != last && !valid()); return *this; } view_iterator operator++(int) ENTT_NOEXCEPT { view_iterator orig = *this; return ++(*this), orig; } view_iterator & operator--() ENTT_NOEXCEPT { while(--it != first && !valid()); return *this; } view_iterator operator--(int) ENTT_NOEXCEPT { view_iterator orig = *this; return operator--(), orig; } [[nodiscard]] bool operator==(const view_iterator &other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const view_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] pointer operator->() const { return &*it; } [[nodiscard]] reference operator*() const { return *operator->(); } private: It first; It last; It it; std::array pools; std::array filter; }; } /** * Internal details not to be documented. * @endcond */ /*! @brief Stable storage policy, aimed at pointer stability. */ struct stable_storage_policy { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template [[nodiscard]] static constexpr bool accept(const Entity entity) ENTT_NOEXCEPT { return entity != tombstone; } /** * Internal details not to be documented. * @endcond */ }; /*! @brief Packed storage policy, aimed at faster linear iteration. */ struct packed_storage_policy { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template [[nodiscard]] static constexpr bool accept(const Entity) ENTT_NOEXCEPT { return true; } /** * 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 class basic_view_impl; /*! @brief View implementation dispatcher. */ template struct basic_view; /** * @brief Multi component view. * * Multi component views iterate over those entities that have at least all the * given components in their bags. 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 iterate. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * 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. * * @note * Views share references to the underlying data structures of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by views. * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Policy Common (stricter) storage policy. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the view. * @tparam Component Types of components iterated by the view. */ template class basic_view_impl, Component...> { using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Comp>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; iterable_iterator(It from, const basic_view_impl *parent) ENTT_NOEXCEPT : it{from}, view{parent} {} iterable_iterator & operator++() ENTT_NOEXCEPT { return ++it, *this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return std::tuple_cat(std::make_tuple(*it), view->get(*it)); } [[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } private: It it; const basic_view_impl *view; }; public: using iterator = iterable_iterator>; using reverse_iterator = iterable_iterator>; iterable(const basic_view_impl &parent) : view{parent} {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return { view.begin(), &view }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return { view.end(), &view }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return { view.rbegin(), &view }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return { view.rend(), &view }; } private: const basic_view_impl view; }; [[nodiscard]] const auto * candidate() const ENTT_NOEXCEPT { return (std::min)({ static_cast(std::get *>(pools))... }, [](const auto *lhs, const auto *rhs) { return lhs->size() < rhs->size(); }); } [[nodiscard]] auto pools_to_unchecked_array() const ENTT_NOEXCEPT { std::size_t pos{}; std::array other{}; (static_cast(std::get *>(pools) == view ? void() : void(other[pos++] = std::get *>(pools))), ...); return other; } [[nodiscard]] auto filter_to_array() const ENTT_NOEXCEPT { return std::array{std::get *>(filter)...}; } template [[nodiscard]] auto dispatch_get([[maybe_unused]] It &it, [[maybe_unused]] const Entity entt) const { if constexpr(std::is_same_v::value_type, typename storage_type::value_type>) { return std::forward_as_tuple(*it); } else { return get_as_tuple(*std::get *>(pools), entt); } } template void traverse(Func func) const { if constexpr(std::is_void_v *>(pools)->get({}))>) { for(const auto entt: static_cast(*std::get *>(pools))) { if(Policy::accept(entt) && ((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(filter)->contains(entt) && ...)) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { std::apply(func, get(entt)); } } } } else { auto it = std::get *>(pools)->begin(); for(const auto entt: static_cast(*std::get *>(pools))) { if(Policy::accept(entt) && ((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(filter)->contains(entt) && ...)) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(it, entt)...)); } else { std::apply(func, std::tuple_cat(dispatch_get(it, entt)...)); } } ++it; } } } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Bidirectional iterator type. */ using iterator = internal::view_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = internal::view_iterator; /*! @brief Iterable view type. */ using iterable_view = iterable; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view_impl() ENTT_NOEXCEPT : view{} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param component The storage for the types to iterate. * @param epool The storage for the types used to filter the view. */ basic_view_impl(storage_type &... component, const storage_type &... epool) ENTT_NOEXCEPT : pools{&component...}, filter{&epool...}, view{candidate()} {} /** * @brief Forces the type to use to drive iterations. * @tparam Comp Type of component to use to drive the iteration. */ template void use() const ENTT_NOEXCEPT { view = std::get *>(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 ENTT_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 { return iterator{view->begin(), view->end(), view->begin(), pools_to_unchecked_array(), filter_to_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 { return iterator{view->begin(), view->end(), view->end(), pools_to_unchecked_array(), filter_to_array()}; } /** * @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 { return reverse_iterator{view->rbegin(), view->rend(), view->rbegin(), pools_to_unchecked_array(), filter_to_array()}; } /** * @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 { return reverse_iterator{view->rbegin(), view->rend(), view->rend(), pools_to_unchecked_array(), filter_to_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 { 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 { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity 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 { const auto it = iterator{view->begin(), view->end(), view->find(entt), pools_to_unchecked_array(), filter_to_array()}; return (it != end() && *it == entt) ? it : end(); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return view != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return (std::get *>(pools)->contains(entt) && ...) && (!std::get *>(filter)->contains(entt) && ...); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the view * results in undefined behavior. * * @tparam Comp Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { ENTT_ASSERT(contains(entt), "View does not contain entity"); if constexpr(sizeof...(Comp) == 0) { return std::tuple_cat(get_as_tuple(*std::get *>(pools), entt)...); } else if constexpr(sizeof...(Comp) == 1) { return (std::get *>(pools)->get(entt), ...); } else { return std::tuple_cat(get_as_tuple(*std::get *>(pools), 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.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(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 void each(Func func) const { ((std::get *>(pools) == view ? traverse(std::move(func)) : void()), ...); } /** * @brief Iterates entities and components and applies the given function * object to them. * * The pool of the suggested component is used to lead the iterations. The * returned entities will therefore respect the order of the pool associated * with that type. * * @sa each * * @tparam Comp Type of component to use to drive the iteration. * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { use(); traverse(std::move(func)); } /** * @brief Returns an iterable object to use to _visit_ the view. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { return iterable_view{*this}; } /** * @brief Returns an iterable object to use to _visit_ the view. * * The pool of the suggested component is used to lead the iterations. The * returned elements will therefore respect the order of the pool associated * with that type. * * @sa each * * @tparam Comp Type of component to use to drive the iteration. * @return An iterable object to use to _visit_ the view. */ template [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { use(); return iterable_view{*this}; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam Id A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @return A more specific view. */ template friend auto operator|(const basic_view, CLhs...> &, const basic_view, CRhs...> &); private: const std::tuple *...> pools; const std::tuple *...> filter; mutable const basic_common_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 instances of the given component are created and assigned to entities. * * The entity currently pointed is modified (as an example, the given * component is removed from the entity to which the iterator points). * * 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. * * @note * Views share a reference to the underlying data structure of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by views. * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Component Type of component iterated by the view. */ template class basic_view_impl, Component> { using basic_common_type = basic_sparse_set; using storage_type = constness_as_t>::storage_type, Component>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; template iterable_iterator(It... from, Discard...) ENTT_NOEXCEPT : it{from...} {} iterable_iterator & operator++() ENTT_NOEXCEPT { return (++std::get(it), ...), *this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return { *std::get(it)... }; } [[nodiscard]] bool operator==(const iterable_iterator &other) const ENTT_NOEXCEPT { return std::get<0>(other.it) == std::get<0>(it); } [[nodiscard]] bool operator!=(const iterable_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } private: std::tuple it; }; public: using iterator = std::conditional_t< std::is_void_v().get({}))>, iterable_iterator, iterable_iterator().begin())> >; using reverse_iterator = std::conditional_t< std::is_void_v().get({}))>, iterable_iterator, iterable_iterator().rbegin())> >; iterable(storage_type &ref) : pool{&ref} {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return iterator{pool->basic_common_type::begin(), pool->begin()}; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return iterator{pool->basic_common_type::end(), pool->end()}; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return reverse_iterator{pool->basic_common_type::rbegin(), pool->rbegin()}; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return reverse_iterator{pool->basic_common_type::rend(), pool->rend()}; } private: storage_type * const pool; }; public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename basic_common_type::reverse_iterator; /*! @brief Iterable view type. */ using iterable_view = iterable; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view_impl() ENTT_NOEXCEPT : pools{}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view_impl(storage_type &ref) ENTT_NOEXCEPT : pools{&ref}, filter{} {} /** * @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 ENTT_NOEXCEPT { return std::get<0>(pools)->size(); } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return std::get<0>(pools)->empty(); } /** * @brief Direct access to the raw representation offered by the storage. * @return A pointer to the array of components. */ [[nodiscard]] auto raw() const ENTT_NOEXCEPT { return std::get<0>(pools)->raw(); } /** * @brief Direct access to the list of entities. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @return A pointer to the array of entities. */ [[nodiscard]] auto data() const ENTT_NOEXCEPT { return std::get<0>(pools)->data(); } /** * @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 ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::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 ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::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 ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::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 ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::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 { 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 { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity 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 { const auto it = std::get<0>(pools)->find(entt); return it != end() && *it == entt ? it : 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 Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return std::get<0>(pools) != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return std::get<0>(pools)->contains(entt); } /** * @brief Returns the component assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the view * results in undefined behavior. * * @tparam Comp Types of components to get. * @param entt A valid entity identifier. * @return The component assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt), "View does not contain entity"); if constexpr(sizeof...(Comp) == 0) { return get_as_tuple(*std::get<0>(pools), entt); } else { static_assert(std::is_same_v, "Invalid component type"); return std::get<0>(pools)->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.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Component &); * void(Component &); * @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 void each(Func func) const { if constexpr(std::is_void_v(pools)->get({}))>) { if constexpr(std::is_invocable_v) { for(auto pos = size(); pos; --pos) { func(); } } else { for(auto entity: *this) { func(entity); } } } else { if constexpr(is_applicable_v) { for(const auto pack: each()) { std::apply(func, pack); } } else { for(auto &&component: *std::get<0>(pools)) { func(component); } } } } /** * @brief Returns an iterable object to use to _visit_ the view. * * The iterable object returns tuples that contain the current entity and a * reference to its component if it's a non-empty one. The _constness_ of * the component is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { return iterable_view{*std::get<0>(pools)}; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam Id A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @return A more specific view. */ template friend auto operator|(const basic_view, CLhs...> &, const basic_view, CRhs...> &); private: const std::tuple pools; const std::tuple<> filter; }; /** * @brief View implementation dispatcher. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the view. * @tparam Component Types of components iterated by the view. */ template struct basic_view, Component...> : basic_view_impl>::in_place_delete...>, stable_storage_policy, packed_storage_policy>, Entity, exclude_t, Component...> { /*! @brief Most restrictive storage policy of all component types. */ using storage_policy = std::conditional_t>::in_place_delete...>, stable_storage_policy, packed_storage_policy>; using basic_view_impl, Component...>::basic_view_impl; }; /** * @brief Deduction guide. * @tparam Storage Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ template basic_view(Storage &... storage) -> basic_view, entt::exclude_t<>, constness_as_t...>; /** * @brief Combines two views in a _more specific_ one. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @param lhs A valid reference to the first view. * @param rhs A valid reference to the second view. * @return A more specific view. */ template [[nodiscard]] auto operator|(const basic_view, CLhs...> &lhs, const basic_view, CRhs...> &rhs) { using view_type = basic_view, CLhs..., CRhs...>; return std::apply([](auto *... storage) { return view_type{*storage...}; }, std::tuple_cat(lhs.pools, rhs.pools, lhs.filter, rhs.filter)); } } #endif