Browse Source

view: shared implementation for common views

Michele Caini 2 years ago
parent
commit
735db1e6af
1 changed files with 197 additions and 153 deletions
  1. 197 153
      src/entt/entity/view.hpp

+ 197 - 153
src/entt/entity/view.hpp

@@ -206,6 +206,166 @@ template<typename... Lhs, typename... Rhs>
 template<typename, typename, typename>
 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<typename Type, std::size_t Get, std::size_t Exclude>
+class basic_common_view {
+    template<typename Return, typename View, typename Other, std::size_t... VGet, std::size_t... VExclude, std::size_t... OGet, std::size_t... OExclude>
+    friend Return internal::view_pack(const View &, const Other &, std::index_sequence<VGet...>, std::index_sequence<VExclude...>, std::index_sequence<OGet...>, std::index_sequence<OExclude...>);
+
+protected:
+    void use(const std::size_t pos) noexcept {
+        if(view) {
+            view = pools[pos];
+            opaque_check_set();
+        }
+    }
+
+    void opaque_check_set() noexcept {
+        for(size_type pos{}, curr{}; pos < Get; ++pos) {
+            if(pools[pos] != view) {
+                check[curr++] = pools[pos];
+            }
+        }
+    }
+
+    void unchecked_refresh() noexcept {
+        view = pools[0u];
+
+        for(size_type pos{1u}; pos < Get; ++pos) {
+            if(pools[pos]->size() < view->size()) {
+                view = pools[pos];
+            }
+        }
+
+        opaque_check_set();
+    }
+
+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 Bidirectional iterator type. */
+    using iterator = internal::view_iterator<common_type, Get - 1u, Exclude>;
+
+    /*! @brief Updates the internal leading view if required. */
+    void refresh() noexcept {
+        size_type pos = (view != nullptr) * 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 view;
+    }
+
+    /**
+     * @brief Estimates the number of entities iterated by the view.
+     * @return Estimated number of entities iterated by the view.
+     */
+    [[nodiscard]] size_type size_hint() const noexcept {
+        return view ? view->size() : size_type{};
+    }
+
+    /**
+     * @brief Returns an iterator to the first entity of the view.
+     *
+     * If the view is empty, the returned iterator will be equal to `end()`.
+     *
+     * @return An iterator to the first entity of the view.
+     */
+    [[nodiscard]] iterator begin() const noexcept {
+        return view ? iterator{view->begin(0), view->end(0), check, filter} : iterator{};
+    }
+
+    /**
+     * @brief Returns an iterator that is past the last entity of the view.
+     * @return An iterator to the entity following the last entity of the view.
+     */
+    [[nodiscard]] iterator end() const noexcept {
+        return view ? iterator{view->end(0), view->end(0), check, filter} : iterator{};
+    }
+
+    /**
+     * @brief Returns the first entity of the view, if any.
+     * @return The first entity of the view if one exists, the null entity
+     * otherwise.
+     */
+    [[nodiscard]] entity_type front() const noexcept {
+        const auto it = begin();
+        return it != end() ? *it : null;
+    }
+
+    /**
+     * @brief Returns the last entity of the view, if any.
+     * @return The last entity of the view if one exists, the null entity
+     * otherwise.
+     */
+    [[nodiscard]] entity_type back() const noexcept {
+        if(view) {
+            auto it = view->rbegin(0);
+            const auto last = view->rend(0);
+            for(; it != last && !contains(*it); ++it) {}
+            return it == last ? null : *it;
+        }
+
+        return null;
+    }
+
+    /**
+     * @brief Finds an entity.
+     * @param entt A valid identifier.
+     * @return An iterator to the given entity if it's found, past the end
+     * iterator otherwise.
+     */
+    [[nodiscard]] iterator find(const entity_type entt) const noexcept {
+        return contains(entt) ? iterator{view->find(entt), view->end(), check, filter} : 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 (view != nullptr) && internal::fully_initialized(filter.data(), filter.size());
+    }
+
+    /**
+     * @brief Checks if a view contains an entity.
+     * @param entt A valid identifier.
+     * @return True if the view contains the given entity, false otherwise.
+     */
+    [[nodiscard]] bool contains(const entity_type entt) const noexcept {
+        if(view) {
+            const auto idx = view->find(entt).index();
+            return (!(idx < 0 || idx > view->begin(0).index())) && internal::all_of(check.data(), check.size(), entt) && internal::none_of(filter.data(), filter.size(), entt);
+        }
+
+        return false;
+    }
+
+protected:
+    std::array<const common_type *, Get> pools{};
+    std::array<const common_type *, Exclude> filter{};
+    std::array<const common_type *, Get - 1u> check{};
+    const common_type *view{};
+};
+
 /**
  * @brief General purpose view.
  *
@@ -219,39 +379,30 @@ class basic_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...>> {
-    template<typename Result, typename View, typename Other, std::size_t... VGet, std::size_t... VExclude, std::size_t... OGet, std::size_t... OExclude>
-    friend Result internal::view_pack(const View &, const Other &, std::index_sequence<VGet...>, std::index_sequence<VExclude...>, std::index_sequence<OGet...>, std::index_sequence<OExclude...>);
-
-    using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
-    using underlying_type = typename base_type::entity_type;
+class basic_view<get_t<Get...>, exclude_t<Exclude...>>: public basic_common_view<std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>, sizeof...(Get), sizeof...(Exclude)> {
+    using base_type = basic_common_view<std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>, sizeof...(Get), sizeof...(Exclude)>;
 
     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..., typename Exclude::value_type...>>;
 
-    void opaque_check_set() noexcept {
-        std::apply([this, pos = 0u](const auto *...curr) mutable { ((curr == view ? void() : void(check[pos++] = curr)), ...); }, pools);
-    }
-
-    void unchecked_refresh() noexcept {
-        view = std::get<0>(pools);
-        std::apply([this](auto *, auto *...other) { ((this->view = other->size() < this->view->size() ? other : this->view), ...); }, pools);
-        opaque_check_set();
+    template<std::size_t... Index>
+    auto storage(std::index_sequence<Index...>) const noexcept {
+        return std::make_tuple(storage<Index>()...);
     }
 
     template<std::size_t Curr, std::size_t Other, typename... Args>
-    [[nodiscard]] auto dispatch_get(const std::tuple<underlying_type, Args...> &curr) const {
+    [[nodiscard]] auto dispatch_get(const std::tuple<typename base_type::entity_type, Args...> &curr) const {
         if constexpr(Curr == Other) {
             return std::forward_as_tuple(std::get<Args>(curr)...);
         } else {
-            return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr));
+            return storage<Other>()->get_as_tuple(std::get<0>(curr));
         }
     }
 
     template<std::size_t Curr, typename Func, std::size_t... Index>
     void each(Func &func, std::index_sequence<Index...>) const {
-        for(const auto curr: std::get<Curr>(pools)->each()) {
-            if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of(check.data(), check.size(), entt) && internal::none_of(filter.data(), filter.size(), entt)) {
+        for(const auto curr: storage<Curr>()->each()) {
+            if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of(this->check.data(), sizeof...(Get), entt) && internal::none_of(this->filter.data(), sizeof...(Exclude), 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 {
@@ -263,27 +414,24 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>> {
 
     template<typename Func, std::size_t... Index>
     void pick_and_each(Func &func, std::index_sequence<Index...> seq) const {
-        ((std::get<Index>(pools) == view ? each<Index>(func, seq) : void()), ...);
+        ((storage<Index>() == this->handle() ? each<Index>(func, seq) : void()), ...);
     }
 
 public:
     /*! @brief Common type among all storage types. */
-    using common_type = base_type;
+    using common_type = typename base_type::common_type;
     /*! @brief Underlying entity identifier. */
-    using entity_type = underlying_type;
+    using entity_type = typename base_type::entity_type;
     /*! @brief Unsigned integer type. */
-    using size_type = std::size_t;
+    using size_type = typename base_type::size_type;
     /*! @brief Bidirectional iterator type. */
-    using iterator = internal::view_iterator<common_type, sizeof...(Get) - 1u, sizeof...(Exclude)>;
+    using iterator = typename base_type::iterator;
     /*! @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{},
-          check{},
-          view{} {}
+        : base_type{} {}
 
     /**
      * @brief Constructs a view from a set of storage classes.
@@ -291,11 +439,10 @@ public:
      * @param excl The storage for the types used to filter the view.
      */
     basic_view(Get &...value, Exclude &...excl) noexcept
-        : pools{&value...},
-          filter{&excl...},
-          check{},
-          view{} {
-        unchecked_refresh();
+        : base_type{} {
+        this->pools = {&value...};
+        this->filter = {&excl...};
+        this->unchecked_refresh();
     }
 
     /**
@@ -321,25 +468,7 @@ public:
      */
     template<std::size_t Index>
     void use() noexcept {
-        if(view) {
-            view = std::get<Index>(pools);
-            opaque_check_set();
-        }
-    }
-
-    /*! @brief Updates the internal leading view if required. */
-    void refresh() noexcept {
-        if(view || std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) {
-            unchecked_refresh();
-        }
-    }
-
-    /**
-     * @brief Returns the leading storage of a view, if any.
-     * @return The leading storage of the view.
-     */
-    [[nodiscard]] const common_type *handle() const noexcept {
-        return view;
+        base_type::use(Index);
     }
 
     /**
@@ -359,11 +488,12 @@ public:
      */
     template<std::size_t Index>
     [[nodiscard]] auto *storage() const noexcept {
+        using type = type_list_element_t<Index, type_list<Get..., Exclude...>>;
+
         if constexpr(Index < sizeof...(Get)) {
-            return std::get<Index>(pools);
+            return static_cast<type *>(const_cast<constness_as_t<common_type, type> *>(this->pools[Index]));
         } else {
-            using type = type_list_element_t<Index - sizeof...(Get), type_list<Exclude...>>;
-            return static_cast<type *>(const_cast<constness_as_t<common_type, type> *>(filter[Index - sizeof...(Get)]));
+            return static_cast<type *>(const_cast<constness_as_t<common_type, type> *>(this->filter[Index - sizeof...(Get)]));
         }
     }
 
@@ -385,77 +515,16 @@ public:
      */
     template<std::size_t Index, typename Type>
     void storage(Type &elem) noexcept {
+        static_assert(std::is_convertible_v<Type &, type_list_element_t<Index, type_list<Get..., Exclude...>> &>, "Unexpected type");
+
         if constexpr(Index < sizeof...(Get)) {
-            std::get<Index>(pools) = &elem;
-            refresh();
+            this->pools[Index] = &elem;
+            this->refresh();
         } else {
-            std::get<Index - sizeof...(Get)>(filter) = &elem;
+            this->filter[Index - sizeof...(Get)] = &elem;
         }
     }
 
-    /**
-     * @brief Estimates the number of entities iterated by the view.
-     * @return Estimated number of entities iterated by the view.
-     */
-    [[nodiscard]] size_type size_hint() const noexcept {
-        return view ? view->size() : size_type{};
-    }
-
-    /**
-     * @brief Returns an iterator to the first entity of the view.
-     *
-     * If the view is empty, the returned iterator will be equal to `end()`.
-     *
-     * @return An iterator to the first entity of the view.
-     */
-    [[nodiscard]] iterator begin() const noexcept {
-        return view ? iterator{view->begin(0), view->end(0), check, filter} : iterator{};
-    }
-
-    /**
-     * @brief Returns an iterator that is past the last entity of the view.
-     * @return An iterator to the entity following the last entity of the view.
-     */
-    [[nodiscard]] iterator end() const noexcept {
-        return view ? iterator{view->end(0), view->end(0), check, filter} : iterator{};
-    }
-
-    /**
-     * @brief Returns the first entity of the view, if any.
-     * @return The first entity of the view if one exists, the null entity
-     * otherwise.
-     */
-    [[nodiscard]] entity_type front() const noexcept {
-        const auto it = begin();
-        return it != end() ? *it : null;
-    }
-
-    /**
-     * @brief Returns the last entity of the view, if any.
-     * @return The last entity of the view if one exists, the null entity
-     * otherwise.
-     */
-    [[nodiscard]] entity_type back() const noexcept {
-        if(view) {
-            auto it = view->rbegin(0);
-            const auto last = view->rend(0);
-            for(; it != last && !contains(*it); ++it) {}
-            return it == last ? null : *it;
-        }
-
-        return null;
-    }
-
-    /**
-     * @brief Finds an entity.
-     * @param entt A valid identifier.
-     * @return An iterator to the given entity if it's found, past the end
-     * iterator otherwise.
-     */
-    [[nodiscard]] iterator find(const entity_type entt) const noexcept {
-        return contains(entt) ? iterator{view->find(entt), view->end(), check, filter} : end();
-    }
-
     /**
      * @brief Returns the components assigned to the given entity.
      * @param entt A valid identifier.
@@ -465,28 +534,6 @@ public:
         return get(entt);
     }
 
-    /**
-     * @brief Checks if a view is fully initialized.
-     * @return True if the view is fully initialized, false otherwise.
-     */
-    [[nodiscard]] explicit operator bool() const noexcept {
-        return (view != nullptr) && internal::fully_initialized(filter.data(), filter.size());
-    }
-
-    /**
-     * @brief Checks if a view contains an entity.
-     * @param entt A valid identifier.
-     * @return True if the view contains the given entity, false otherwise.
-     */
-    [[nodiscard]] bool contains(const entity_type entt) const noexcept {
-        if(view) {
-            const auto idx = view->find(entt).index();
-            return (!(idx < 0 || idx > view->begin(0).index())) && internal::all_of(check.data(), check.size(), entt) && internal::none_of(filter.data(), filter.size(), entt);
-        }
-
-        return false;
-    }
-
     /**
      * @brief Returns the components assigned to the given entity.
      * @tparam Type Type of the component to get.
@@ -508,11 +555,11 @@ public:
     template<std::size_t... Index>
     [[nodiscard]] decltype(auto) get(const entity_type entt) const {
         if constexpr(sizeof...(Index) == 0) {
-            return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools);
+            return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, storage(std::index_sequence_for<Get...>{}));
         } else if constexpr(sizeof...(Index) == 1) {
-            return (std::get<Index>(pools)->get(entt), ...);
+            return (storage<Index>()->get(entt), ...);
         } else {
-            return std::tuple_cat(std::get<Index>(pools)->get_as_tuple(entt)...);
+            return std::tuple_cat(storage<Index>()->get_as_tuple(entt)...);
         }
     }
 
@@ -533,7 +580,9 @@ public:
      */
     template<typename Func>
     void each(Func func) const {
-        view ? pick_and_each(func, std::index_sequence_for<Get...>{}) : void();
+        if(this->handle() != nullptr) {
+            pick_and_each(func, std::index_sequence_for<Get...>{});
+        }
     }
 
     /**
@@ -546,7 +595,8 @@ public:
      * @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}};
+        const auto as_pools = storage(std::index_sequence_for<Get...>{});
+        return {internal::extended_view_iterator{this->begin(), as_pools}, internal::extended_view_iterator{this->end(), as_pools}};
     }
 
     /**
@@ -561,12 +611,6 @@ public:
         return internal::view_pack<basic_view<get_t<Get..., OGet...>, exclude_t<Exclude..., OExclude...>>>(
             *this, other, std::index_sequence_for<Get...>{}, std::index_sequence_for<Exclude...>{}, std::index_sequence_for<OGet...>{}, std::index_sequence_for<OExclude...>{});
     }
-
-private:
-    std::tuple<Get *...> pools;
-    std::array<const common_type *, sizeof...(Exclude)> filter;
-    std::array<const common_type *, sizeof...(Get) - 1u> check;
-    const common_type *view;
 };
 
 /**