#ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include #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(It first, const It last, const Entity entt) noexcept { for(; (first != last) && (*first)->contains(entt); ++first) {} return first == last; } template [[nodiscard]] bool none_of(It first, const It last, const Entity entt) noexcept { for(; (first != last) && !(*first && (*first)->contains(entt)); ++first) {} return first == last; } template [[nodiscard]] bool fully_initialized(It first, const It last) noexcept { for(; (first != last) && *first; ++first) {} return first == last; } 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 { template friend class extended_view_iterator; using iterator_type = typename Type::const_iterator; using iterator_traits = std::iterator_traits; [[nodiscard]] bool valid(const typename iterator_traits::value_type entt) const noexcept { return ((Get != 1u) || (entt != tombstone)) && internal::all_of(pools.begin(), pools.begin() + index, entt) && internal::all_of(pools.begin() + index + 1, pools.end(), entt) && internal::none_of(filter.begin(), filter.end(), entt); } void seek_next() { for(constexpr iterator_type sentinel{}; it != sentinel && !valid(*it); ++it) {} } public: using value_type = typename iterator_traits::value_type; using pointer = typename iterator_traits::pointer; using reference = typename iterator_traits::reference; using difference_type = typename iterator_traits::difference_type; using iterator_category = std::forward_iterator_tag; constexpr view_iterator() noexcept : it{}, pools{}, filter{}, index{} {} view_iterator(iterator_type first, std::array value, std::array excl, const std::size_t idx) noexcept : it{first}, pools{value}, filter{excl}, index{idx} { seek_next(); } view_iterator &operator++() noexcept { ++it; seek_next(); 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; 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 class extended_view_iterator final { template [[nodiscard]] auto dereference(std::index_sequence) const noexcept { return std::tuple_cat(std::make_tuple(*it), static_cast(const_cast *>(std::get(it.pools)))->get_as_tuple(*it)...); } public: using iterator_type = It; 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 difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; using iterator_concept = std::forward_iterator_tag; constexpr extended_view_iterator() : it{} {} extended_view_iterator(iterator_type from) : it{from} {} 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 dereference(std::index_sequence_for{}); } [[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; }; 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, elements 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); [[nodiscard]] auto offset() const noexcept { ENTT_ASSERT(index != Get, "Invalid view"); return (pools[index]->policy() == deletion_policy::swap_only) ? pools[index]->free_list() : pools[index]->size(); } void unchecked_refresh() noexcept { index = 0u; if constexpr(Get > 1u) { for(size_type pos{1u}; pos < Get; ++pos) { if(pools[pos]->size() < pools[index]->size()) { index = pos; } } } } protected: /*! @cond TURN_OFF_DOXYGEN */ basic_common_view() noexcept = default; basic_common_view(std::array value, std::array excl) noexcept : pools{value}, filter{excl}, index{Get} { unchecked_refresh(); } [[nodiscard]] const Type *pool_at(const std::size_t pos) const noexcept { return pools[pos]; } [[nodiscard]] const Type *storage(const std::size_t pos) const noexcept { return (pos < Get) ? pools[pos] : filter[pos - Get]; } void storage(const std::size_t pos, const Type *elem) noexcept { if(pos < Get) { pools[pos] = elem; refresh(); } else { filter[pos - Get] = elem; } } [[nodiscard]] bool none_of(const typename Type::entity_type entt) const noexcept { return internal::none_of(filter.begin(), filter.end(), entt); } void use(const std::size_t pos) noexcept { index = (index != Get) ? pos : Get; } /*! @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 Forward iterator type. */ using iterator = internal::view_iterator; /*! @brief Updates the internal leading view if required. */ void refresh() noexcept { size_type pos = (index != Get) * 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 (index != Get) ? pools[index] : nullptr; } /** * @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 (index != Get) ? pools[index]->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 (index != Get) ? iterator{pools[index]->end() - static_cast(offset()), 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 (index != Get) ? iterator{pools[index]->end(), 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(index != Get) { auto it = pools[index]->rbegin(); const auto last = it + static_cast(offset()); 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{pools[index]->find(entt), 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 (index != Get) && internal::fully_initialized(filter.begin(), filter.end()); } /** * @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 (index != Get) && internal::all_of(pools.begin(), pools.end(), entt) && internal::none_of(filter.begin(), filter.end(), entt) && pools[index]->index(entt) < offset(); } private: std::array pools{}; std::array filter{}; size_type index{Get}; }; /** * @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, std::enable_if_t<(sizeof...(Get) + sizeof...(Exclude) > 1)>> : 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 [[nodiscard]] auto get(const typename base_type::entity_type entt, std::index_sequence) const noexcept { return std::tuple_cat(storage()->get_as_tuple(entt)...); } 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 { static constexpr bool tombstone_check_required = ((sizeof...(Get) == 1u) && ... && (Get::storage_policy == deletion_policy::in_place)); for(const auto curr: storage()->each()) { if(const auto entt = std::get<0>(curr); (!tombstone_check_required || (entt != tombstone)) && ((Curr == Index || base_type::pool_at(Index)->contains(entt)) && ...) && base_type::none_of(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 { if(const auto *view = base_type::handle(); view != nullptr) { ((view == base_type::pool_at(Index) ? 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 Forward 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 element to drive iterations * @tparam Type Type of element to use to drive iterations. */ template void use() noexcept { use>(); } /** * @brief Forces a view to use a given element to drive iterations * @tparam Index Index of the element to use to drive iterations. */ template void use() noexcept { base_type::use(Index); } /** * @brief Returns the storage for a given element type, if any. * @tparam Type Type of element of which to return the storage. * @return The storage for the given element 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>; return static_cast(const_cast *>(base_type::storage(Index))); } /** * @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"); base_type::storage(Index, &elem); } /** * @brief Returns the elements assigned to the given entity. * @param entt A valid identifier. * @return The elements assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { return get(entt); } /** * @brief Returns the elements assigned to the given entity. * @tparam Type Type of the element to get. * @tparam Other Other types of elements to get. * @param entt A valid identifier. * @return The elements assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { return get, index_of...>(entt); } /** * @brief Returns the elements assigned to the given entity. * @tparam Index Indexes of the elements to get. * @param entt A valid identifier. * @return The elements assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Index) == 0) { return get(entt, 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 elements 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 { 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 elements. The _constness_ of the * elements is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { return iterable{base_type::begin(), base_type::end()}; } /** * @brief Combines two views in a _more specific_ one. * @tparam OGet Element 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. * @tparam Policy Storage policy. */ template class basic_storage_view { protected: /*! @cond TURN_OFF_DOXYGEN */ basic_storage_view() noexcept = default; basic_storage_view(const Type *value) noexcept : leading{value} { ENTT_ASSERT(leading->policy() == Policy, "Unexpected storage policy"); } /*! @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 = std::conditional_t, typename common_type::iterator>; /*! @brief Reverse iterator type. */ using reverse_iterator = std::conditional_t; /** * @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 element. * @tparam Pol Dummy template parameter used for sfinae purposes only. * @return Number of entities that have the given element. */ template [[nodiscard]] std::enable_if_t size() const noexcept { if constexpr(Policy == deletion_policy::swap_and_pop) { return leading ? leading->size() : size_type{}; } else { static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); return leading ? leading->free_list() : size_type{}; } } /** * @brief Estimates the number of entities iterated by the view. * @tparam Pol Dummy template parameter used for sfinae purposes only. * @return Estimated number of entities iterated by the view. */ template [[nodiscard]] std::enable_if_t size_hint() const noexcept { return leading ? leading->size() : size_type{}; } /** * @brief Checks whether a view is empty. * @tparam Pol Dummy template parameter used for sfinae purposes only. * @return True if the view is empty, false otherwise. */ template [[nodiscard]] std::enable_if_t empty() const noexcept { if constexpr(Policy == deletion_policy::swap_and_pop) { return !leading || leading->empty(); } else { static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); return !leading || (leading->free_list() == 0u); } } /** * @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 { if constexpr(Policy == deletion_policy::swap_and_pop) { return leading ? leading->begin() : iterator{}; } else if constexpr(Policy == deletion_policy::swap_only) { return leading ? (leading->end() - leading->free_list()) : iterator{}; } else { static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); return leading ? iterator{leading->begin(), {leading}, {}, 0u} : 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 { if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { return leading ? leading->end() : iterator{}; } else { static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); return leading ? iterator{leading->end(), {leading}, {}, 0u} : 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()`. * * @tparam Pol Dummy template parameter used for sfinae purposes only. * @return An iterator to the first entity of the reversed view. */ template [[nodiscard]] std::enable_if_t rbegin() const noexcept { return leading ? leading->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * @tparam Pol Dummy template parameter used for sfinae purposes only. * @return An iterator to the entity following the last entity of the * reversed view. */ template [[nodiscard]] std::enable_if_t rend() const noexcept { if constexpr(Policy == deletion_policy::swap_and_pop) { return leading ? leading->rend() : reverse_iterator{}; } else { static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); return leading ? (leading->rbegin() + leading->free_list()) : 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 { if constexpr(Policy == deletion_policy::swap_and_pop) { return empty() ? null : *leading->begin(); } else if constexpr(Policy == deletion_policy::swap_only) { return empty() ? null : *(leading->end() - leading->free_list()); } else { static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); const auto it = begin(); return (it == end()) ? null : *it; } } /** * @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 constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { return empty() ? null : *leading->rbegin(); } else { static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); if(leading) { auto it = leading->rbegin(); const auto last = leading->rend(); for(; (it != last) && (*it == tombstone); ++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 { if constexpr(Policy == deletion_policy::swap_and_pop) { return leading ? leading->find(entt) : iterator{}; } else if constexpr(Policy == deletion_policy::swap_only) { const auto it = leading ? leading->find(entt) : iterator{}; return leading && (static_cast(it.index()) < leading->free_list()) ? it : iterator{}; } else { const auto it = leading ? leading->find(entt) : typename common_type::iterator{}; return iterator{it, {leading}, {}, 0u}; } } /** * @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 { if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::in_place) { return leading && leading->contains(entt); } else { static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); return leading && leading->contains(entt) && (leading->index(entt) < leading->free_list()); } } private: const common_type *leading{}; }; /** * @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<>> : 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 Reverse iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable view type. */ using iterable = std::conditional_t>, 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 element type, if any. * @tparam Type Type of element of which to return the storage. * @return The storage for the given element type. */ template [[nodiscard]] auto *storage() const noexcept { static_assert(std::is_same_v, typename Get::element_type>, "Invalid element 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 *>(base_type::handle())); } /** * @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 = basic_view{elem}; } /** * @brief Returns a pointer to the underlying storage. * @return A pointer to the underlying storage. */ [[nodiscard]] Get *operator->() const noexcept { return storage(); } /** * @brief Returns the element assigned to the given entity. * @param entt A valid identifier. * @return The element assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { return storage()->get(entt); } /** * @brief Returns the element assigned to the given entity. * @tparam Elem Type of the element to get. * @param entt A valid identifier. * @return The element assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { static_assert(std::is_same_v, typename Get::element_type>, "Invalid element type"); return get<0>(entt); } /** * @brief Returns the element assigned to the given entity. * @tparam Index Index of the element to get. * @param entt A valid identifier. * @return The element 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 elements 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 constexpr(is_applicable_v{}, std::declval().get({})))>) { for(const auto pack: each()) { std::apply(func, pack); } } else if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { if constexpr(std::is_void_v) { for(size_type pos = base_type::size(); pos; --pos) { func(); } } else { if(const auto len = base_type::size(); len != 0u) { for(auto last = storage()->end(), first = last - len; first != last; ++first) { func(*first); } } } } else { static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); for(const auto pack: each()) { std::apply([&func](const auto, auto &&...elem) { func(std::forward(elem)...); }, pack); } } } /** * @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 element if it's a non-empty one. The _constness_ of * the element is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { return base_type::handle() ? storage()->each() : iterable{}; } else { static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); return iterable{base_type::begin(), base_type::end()}; } } /** * @brief Combines two views in a _more specific_ one. * @tparam OGet Element 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 elements iterated by the view. * @tparam Exclude Types of elements used to filter the view. */ template basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; } // namespace entt #endif