Class basic_view< Entity, exclude_t< Exclude... >, Component... >

Synopsis

#include <src/entt/entity/view.hpp>

template<typename Entity, typename... Exclude, typename... Component>
class basic_view<Entity, exclude_t<Exclude...>, Component...> final

Description

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.

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.
Template Parameters

Entity - A valid entity type (see entt_traits for more details).

Exclude - Types of components used to filter the view.

Component - Types of components iterated by the view.

Methods

backReturns the last entity of the view, if any.
basic_viewConstructs a multi-type view from a set of storage classes.
beginReturns an iterator to the first entity of the view.
containsChecks if a view contains an entity.
each OverloadIterates entities and components and applies the given function object to them.
each OverloadReturns an iterable object to use to visit the view.
endReturns an iterator that is past the last entity of the view.
findFinds an entity.
frontReturns the first entity of the view, if any.
getReturns the components assigned to the given entity.
rbeginReturns an iterator to the first entity of the reversed view.
rendReturns an iterator that is past the last entity of the reversed view.
size_hintEstimates the number of entities iterated by the view.
useForces the type to use to drive iterations.

Source

Lines 66-524 in src/entt/entity/view.hpp.

template<typename Entity, typename... Exclude, typename... Component>
class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
    template<typename Comp>
    using storage_type = constness_as_t<typename storage_traits<Entity, std::remove_const_t<Comp>>::storage_type, Comp>;

    using unchecked_type = std::array<const basic_sparse_set<Entity> *, (sizeof...(Component) - 1)>;

    template<typename It>
    class view_iterator final {
        friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;

        view_iterator(It from, It to, It curr, unchecked_type other, const std::tuple<const storage_type<Exclude> *...> &ignore) ENTT_NOEXCEPT
            : first{from},
              last{to},
              it{curr},
              unchecked{other},
              filter{ignore}
        {
            if(it != last && !valid()) {
                ++(*this);
            }
        }

        [[nodiscard]] bool valid() const {
            const auto entt = *it;

            return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt](const basic_sparse_set<Entity> *curr) { return curr->contains(entt); })
                && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...);
        }

    public:
        using difference_type = typename std::iterator_traits<It>::difference_type;
        using value_type = typename std::iterator_traits<It>::value_type;
        using pointer = typename std::iterator_traits<It>::pointer;
        using reference = typename std::iterator_traits<It>::reference;
        using iterator_category = std::bidirectional_iterator_tag;

        view_iterator() ENTT_NOEXCEPT = default;

        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;
        unchecked_type unchecked;
        std::tuple<const storage_type<Exclude> *...> filter;
    };

    class iterable_view final {
        friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;

        template<typename It>
        class iterable_view_iterator final {
            friend class iterable_view;

            iterable_view_iterator(It from, const basic_view &parent) ENTT_NOEXCEPT
                : it{from},
                  view{parent}
            {}

        public:
            using difference_type = std::ptrdiff_t;
            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_view>().get({})));
            using pointer = void;
            using reference = value_type;
            using iterator_category = std::input_iterator_tag;

            iterable_view_iterator & operator++() ENTT_NOEXCEPT {
                return ++it, *this;
            }

            iterable_view_iterator operator++(int) ENTT_NOEXCEPT {
                iterable_view_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_view_iterator &other) const ENTT_NOEXCEPT {
                return other.it == it;
            }

            [[nodiscard]] bool operator!=(const iterable_view_iterator &other) const ENTT_NOEXCEPT {
                return !(*this == other);
            }

        private:
            It it;
            const basic_view view;
        };

        iterable_view(const basic_view &parent)
            : view{parent}
        {}

    public:
        using iterator = iterable_view_iterator<view_iterator<typename basic_sparse_set<Entity>::iterator>>;
        using reverse_iterator = iterable_view_iterator<view_iterator<typename basic_sparse_set<Entity>::reverse_iterator>>;

        [[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 view;
    };

    [[nodiscard]] const basic_sparse_set<Entity> * candidate() const ENTT_NOEXCEPT {
        return (std::min)({ static_cast<const basic_sparse_set<entity_type> *>(std::get<storage_type<Component> *>(pools))... }, [](const auto *lhs, const auto *rhs) {
            return lhs->size() < rhs->size();
        });
    }

    [[nodiscard]] unchecked_type unchecked(const basic_sparse_set<Entity> *cpool) const {
        std::size_t pos{};
        unchecked_type other{};
        (static_cast<void>(std::get<storage_type<Component> *>(pools) == cpool ? nullptr : (other[pos] = std::get<storage_type<Component> *>(pools), other[pos++])), ...);
        return other;
    }

    template<typename Comp, typename It>
    [[nodiscard]] auto dispatch_get([[maybe_unused]] It &it, [[maybe_unused]] const Entity entt) const {
        if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, typename storage_type<Comp>::value_type>) {
            return std::forward_as_tuple(*it);
        } else {
            return get_as_tuple(*std::get<storage_type<Comp> *>(pools), entt);
        }
    }

    template<typename Comp, typename Func>
    void traverse(Func func) const {
        if constexpr(std::is_same_v<typename storage_type<Comp>::storage_category, empty_storage_tag>) {
            for(const auto entt: static_cast<const basic_sparse_set<entity_type> &>(*std::get<storage_type<Comp> *>(pools))) {
                if(((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
                    && !(std::get<const storage_type<Exclude> *>(filter)->contains(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), get(entt)));
                    } else {
                        std::apply(func, get(entt));
                    }
                }
            }
        } else {
            auto it = std::get<storage_type<Comp> *>(pools)->begin();

            for(const auto entt: static_cast<const basic_sparse_set<entity_type> &>(*std::get<storage_type<Comp> *>(pools))) {
                if(((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
                    && !(std::get<const storage_type<Exclude> *>(filter)->contains(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<Component>(it, entt)...));
                    } else {
                        std::apply(func, std::tuple_cat(dispatch_get<Component>(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 = view_iterator<typename basic_sparse_set<entity_type>::iterator>;
    /*! @brief Reverse iterator type. */
    using reverse_iterator = view_iterator<typename basic_sparse_set<entity_type>::reverse_iterator>;

    /**
     * @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(storage_type<Component> &... component, const storage_type<Exclude> &... 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<typename Comp>
    void use() const ENTT_NOEXCEPT {
        view = std::get<storage_type<Comp> *>(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 (std::min)({ std::get<storage_type<Component> *>(pools)->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 { view->begin(), view->end(), view->begin(), unchecked(view), filter };
    }

    /**
     * @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 { view->begin(), view->end(), view->end(), unchecked(view), filter };
    }

    /**
     * @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 { view->rbegin(), view->rend(), view->rbegin(), unchecked(view), filter };
    }

    /**
     * @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 { view->rbegin(), view->rend(), view->rend(), unchecked(view), filter };
    }

    /**
     * @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 {
        iterator it{view->begin(), view->end(), view->find(entt), unchecked(view), filter};
        return (it != end() && *it == entt) ? it : end();
    }

    /**
     * @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<storage_type<Component> *>(pools)->contains(entt) && ...) && !(std::get<const storage_type<Exclude> *>(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<typename... Comp>
    [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const {
        ENTT_ASSERT(contains(entt));

        if constexpr(sizeof...(Comp) == 0) {
            return std::tuple_cat(get_as_tuple(*std::get<storage_type<Component> *>(pools), entt)...);
        } else if constexpr(sizeof...(Comp) == 1) {
            return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
        } else {
            return std::tuple_cat(get_as_tuple(*std::get<storage_type<Comp> *>(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.<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
     *
     * @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 {
        ((std::get<storage_type<Component> *>(pools) == view ? traverse<Component>(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.<br/>
     * It is no longer guaranteed that the performance is the best possible, but
     * there will be greater control over the order of iteration.
     *
     * @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<typename Comp, typename Func>
    void each(Func func) const {
        use<Comp>();
        traverse<Comp>(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.<br/>
     * It is no longer guaranteed that the performance is the best possible, but
     * there will be greater control over the order of iteration.
     *
     * @sa each
     *
     * @tparam Comp Type of component to use to drive the iteration.
     * @return An iterable object to use to _visit_ the view.
     */
    template<typename Comp>
    [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT {
        use<Comp>();
        return iterable_view{*this};
    }

private:
    const std::tuple<storage_type<Component> *...> pools;
    const std::tuple<const storage_type<Exclude> *...> filter;
    mutable const basic_sparse_set<entity_type>* view;
};





Add Discussion

Log in to comment