Selaa lähdekoodia

storage/view/group:
* added extended_storage_iterator and storage<T...>::each function to return an extended iterable object
* use extended_storage_iterator and iterable_adaptor everywhere to avoid code duplication

Michele Caini 4 vuotta sitten
vanhempi
commit
c5cb1d9bcc

+ 82 - 122
src/entt/entity/group.hpp

@@ -71,68 +71,46 @@ class basic_group<Entity, owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final
 
     using basic_common_type = std::common_type_t<typename storage_type<Get>::base_type...>;
 
-    class iterable final {
-        struct iterable_iterator final {
-            using difference_type = std::ptrdiff_t;
-            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
-            using pointer = input_iterator_pointer<value_type>;
-            using reference = value_type;
-            using iterator_category = std::input_iterator_tag;
-
-            template<typename... Args>
-            iterable_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
-                : it{from},
-                  pools{args} {}
-
-            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 {
-                const auto entt = *it;
-                return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
-            }
-
-            [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
-                return operator*();
-            }
-
-            [[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);
-            }
+    struct extended_group_iterator final {
+        using difference_type = std::ptrdiff_t;
+        using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
+        using pointer = input_iterator_pointer<value_type>;
+        using reference = value_type;
+        using iterator_category = std::input_iterator_tag;
+
+        extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<storage_type<Get> *...> &args) ENTT_NOEXCEPT
+            : it{from},
+              pools{args} {}
+
+        extended_group_iterator &operator++() ENTT_NOEXCEPT {
+            return ++it, *this;
+        }
 
-        private:
-            typename basic_common_type::iterator it;
-            std::tuple<storage_type<Get> *...> pools;
-        };
+        extended_group_iterator operator++(int) ENTT_NOEXCEPT {
+            extended_group_iterator orig = *this;
+            return ++(*this), orig;
+        }
 
-    public:
-        using iterator = iterable_iterator;
+        [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+            const auto entt = *it;
+            return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+        }
 
-        iterable(basic_common_type *const ref, const std::tuple<storage_type<Get> *...> &cpools)
-            : handler{ref},
-              pools{cpools} {}
+        [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+            return operator*();
+        }
 
-        [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-            return handler ? iterator{handler->begin(), pools} : iterator{{}, pools};
+        [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+            return other.it == it;
         }
 
-        [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-            return handler ? iterator{handler->end(), pools} : iterator{{}, pools};
+        [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+            return !(*this == other);
         }
 
     private:
-        basic_common_type *const handler;
-        const std::tuple<storage_type<Get> *...> pools;
+        typename basic_common_type::iterator it;
+        std::tuple<storage_type<Get> *...> pools;
     };
 
     basic_group(basic_common_type &ref, storage_type<Get> &...gpool) ENTT_NOEXCEPT
@@ -151,7 +129,7 @@ public:
     /*! @brief Reversed iterator type. */
     using reverse_iterator = typename base_type::reverse_iterator;
     /*! @brief Iterable group type. */
-    using iterable_group = iterable;
+    using iterable = iterable_adaptor<extended_group_iterator>;
 
     /*! @brief Default constructor to use to create empty, invalid groups. */
     basic_group() ENTT_NOEXCEPT
@@ -401,8 +379,9 @@ public:
      *
      * @return An iterable object to use to _visit_ the group.
      */
-    [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
-        return iterable_group{handler, pools};
+    [[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+        return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}}
+                       : iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}};
     }
 
     /**
@@ -545,78 +524,55 @@ class basic_group<Entity, owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...
 
     using basic_common_type = std::common_type_t<typename storage_type<Owned>::base_type..., typename storage_type<Get>::base_type...>;
 
-    class iterable final {
-        template<typename>
-        struct iterable_iterator;
-
-        template<typename... OIt>
-        struct iterable_iterator<type_list<OIt...>> final {
-            using difference_type = std::ptrdiff_t;
-            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
-            using pointer = input_iterator_pointer<value_type>;
-            using reference = value_type;
-            using iterator_category = std::input_iterator_tag;
-
-            template<typename... Other>
-            iterable_iterator(typename basic_common_type::iterator from, const std::tuple<Other...> &other, const std::tuple<storage_type<Get> *...> &cpools) ENTT_NOEXCEPT
-                : it{from},
-                  owned{std::get<OIt>(other)...},
-                  get{cpools} {}
-
-            iterable_iterator &operator++() ENTT_NOEXCEPT {
-                return ++it, (++std::get<OIt>(owned), ...), *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),
-                    std::forward_as_tuple(*std::get<OIt>(owned)...),
-                    std::get<storage_type<Get> *>(get)->get_as_tuple(*it)...);
-            }
-
-            [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
-                return operator*();
-            }
-
-            [[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);
-            }
+    template<typename>
+    struct extended_group_iterator;
+
+    template<typename... OIt>
+    struct extended_group_iterator<type_list<OIt...>> final {
+        using difference_type = std::ptrdiff_t;
+        using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
+        using pointer = input_iterator_pointer<value_type>;
+        using reference = value_type;
+        using iterator_category = std::input_iterator_tag;
+
+        template<typename... Other>
+        extended_group_iterator(typename basic_common_type::iterator from, const std::tuple<Other...> &other, const std::tuple<storage_type<Get> *...> &cpools) ENTT_NOEXCEPT
+            : it{from},
+              owned{std::get<OIt>(other)...},
+              get{cpools} {}
+
+        extended_group_iterator &operator++() ENTT_NOEXCEPT {
+            return ++it, (++std::get<OIt>(owned), ...), *this;
+        }
 
-        private:
-            typename basic_common_type::iterator it;
-            std::tuple<OIt...> owned;
-            std::tuple<storage_type<Get> *...> get;
-        };
+        extended_group_iterator operator++(int) ENTT_NOEXCEPT {
+            extended_group_iterator orig = *this;
+            return ++(*this), orig;
+        }
 
-    public:
-        using iterator = iterable_iterator<type_list_cat_t<std::conditional_t<ignore_as_empty_v<std::remove_const_t<Owned>>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>>;
+        [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+            return std::tuple_cat(
+                std::make_tuple(*it),
+                std::forward_as_tuple(*std::get<OIt>(owned)...),
+                std::get<storage_type<Get> *>(get)->get_as_tuple(*it)...);
+        }
 
-        iterable(std::tuple<storage_type<Owned> *..., storage_type<Get> *...> cpools, const std::size_t *const extent)
-            : pools{cpools},
-              length{extent} {}
+        [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+            return operator*();
+        }
 
-        [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-            auto it = length ? (std::get<0>(pools)->basic_common_type::end() - *length) : typename basic_common_type::iterator{};
-            return iterator{std::move(it), std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
+        [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+            return other.it == it;
         }
 
-        [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-            auto it = length ? std::get<0>(pools)->basic_common_type::end() : typename basic_common_type::iterator{};
-            return iterator{std::move(it), std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
+        [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT {
+            return !(*this == other);
         }
 
     private:
-        const std::tuple<storage_type<Owned> *..., storage_type<Get> *...> pools;
-        const std::size_t *const length;
+        typename basic_common_type::iterator it;
+        std::tuple<OIt...> owned;
+        std::tuple<storage_type<Get> *...> get;
     };
 
     basic_group(const std::size_t &extent, storage_type<Owned> &...opool, storage_type<Get> &...gpool) ENTT_NOEXCEPT
@@ -635,7 +591,7 @@ public:
     /*! @brief Reversed iterator type. */
     using reverse_iterator = typename base_type::reverse_iterator;
     /*! @brief Iterable group type. */
-    using iterable_group = iterable;
+    using iterable = iterable_adaptor<extended_group_iterator<type_list_cat_t<std::conditional_t<ignore_as_empty_v<std::remove_const_t<Owned>>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>>>;
 
     /*! @brief Default constructor to use to create empty, invalid groups. */
     basic_group() ENTT_NOEXCEPT
@@ -861,8 +817,12 @@ public:
      *
      * @return An iterable object to use to _visit_ the group.
      */
-    [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
-        return iterable_group{pools, length};
+    [[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+        using extended_iterator_type = typename iterable::iterator;
+        iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{};
+        auto from = extended_iterator_type{last - *length, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end() - *length)...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
+        auto to = extended_iterator_type{last, std::make_tuple((std::get<storage_type<Owned> *>(pools)->end())...), std::make_tuple(std::get<storage_type<Get> *>(pools)...)};
+        return {std::move(from), std::move(to)};
     }
 
     /**

+ 99 - 11
src/entt/entity/storage.hpp

@@ -12,6 +12,7 @@
 #include "../core/algorithm.hpp"
 #include "../core/any.hpp"
 #include "../core/compressed_pair.hpp"
+#include "../core/iterator.hpp"
 #include "../core/memory.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
@@ -155,6 +156,63 @@ template<typename CLhs, typename CRhs>
     return !(lhs < rhs);
 }
 
+template<typename It, typename... Other>
+class extended_storage_iterator final {
+    template<typename Iter, typename... Args>
+    friend class extended_storage_iterator;
+
+public:
+    using iterator_type = It;
+    using difference_type = typename std::iterator_traits<It>::difference_type;
+    using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
+    using pointer = input_iterator_pointer<value_type>;
+    using reference = value_type;
+    using iterator_category = std::input_iterator_tag;
+
+    extended_storage_iterator() ENTT_NOEXCEPT = default;
+
+    extended_storage_iterator(It base, Other... other) ENTT_NOEXCEPT
+        : it{base, other...} {}
+
+    template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
+    extended_storage_iterator(const extended_storage_iterator<It, Args...> &other) ENTT_NOEXCEPT
+        : it{other.it} {}
+
+    extended_storage_iterator &operator++() ENTT_NOEXCEPT {
+        return ++std::get<It>(it), (++std::get<Other>(it), ...), *this;
+    }
+
+    extended_storage_iterator operator++(int) ENTT_NOEXCEPT {
+        extended_storage_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+        return operator*();
+    }
+
+    [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+        return {*std::get<It>(it), *std::get<Other>(it)...};
+    }
+
+    [[nodiscard]] iterator_type base() const ENTT_NOEXCEPT {
+        return std::get<It>(it);
+    }
+
+private:
+    std::tuple<It, Other...> it;
+};
+
+template<typename... CLhs, typename... CRhs>
+[[nodiscard]] bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
+    return lhs.base() == rhs.base();
+}
+
+template<typename... CLhs, typename... CRhs>
+[[nodiscard]] bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) ENTT_NOEXCEPT {
+    return !(lhs == rhs);
+}
+
 } // namespace internal
 
 /**
@@ -165,15 +223,6 @@ template<typename CLhs, typename CRhs>
 /**
  * @brief Basic storage implementation.
  *
- * This class is a refinement of a sparse set that associates an object to an
- * entity. The main purpose of this class is to extend sparse sets to store
- * components in a registry. It guarantees fast access both to the elements and
- * to the entities.
- *
- * @note
- * Entities and objects have the same order.
- *
- * @note
  * Internal data structures arrange elements to maximize performance. There are
  * no guarantees that objects are returned in the insertion order when iterate
  * a storage. Do not make assumption on the order in any case.
@@ -182,8 +231,6 @@ template<typename CLhs, typename CRhs>
  * Empty types aren't explicitly instantiated. Therefore, many of the functions
  * normally available for non-empty types will not be available for empty ones.
  *
- * @sa sparse_set<Entity>
- *
  * @tparam Entity A valid entity type (see entt_traits for more details).
  * @tparam Type Type of objects assigned to the entities.
  * @tparam Allocator Type of allocator used to manage memory and elements.
@@ -379,6 +426,10 @@ public:
     using reverse_iterator = std::reverse_iterator<iterator>;
     /*! @brief Constant reverse iterator type. */
     using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+    /*! @brief Extended iterable storage proxy. */
+    using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>;
+    /*! @brief Constant extended iterable storage proxy. */
+    using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>;
 
     /*! @brief Default constructor. */
     basic_storage()
@@ -705,6 +756,23 @@ public:
         consume_range(std::move(first), std::move(last), [&from]() -> decltype(auto) { return *(from++); });
     }
 
+    /**
+     * @brief Returns an iterable object to use to _visit_ the storage.
+     *
+     * The iterable object returns a tuple that contains the current entity and
+     * a reference to its component.
+     *
+     * @return An iterable object to use to _visit_ the storage.
+     */
+    [[nodiscard]] iterable each() ENTT_NOEXCEPT {
+        return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
+    }
+
+    /*! @copydoc each */
+    [[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
+        return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
+    }
+
 private:
     compressed_pair<container_type, alloc> packed;
 };
@@ -727,6 +795,10 @@ public:
     using entity_type = Entity;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
+    /*! @brief Extended iterable storage proxy. */
+    using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
+    /*! @brief Constant extended iterable storage proxy. */
+    using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>;
 
     /*! @brief Default constructor. */
     basic_storage()
@@ -844,6 +916,22 @@ public:
             emplace(*first);
         }
     }
+
+    /**
+     * @brief Returns an iterable object to use to _visit_ the storage.
+     *
+     * The iterable object returns a tuple that contains the current entity.
+     *
+     * @return An iterable object to use to _visit_ the storage.
+     */
+    [[nodiscard]] iterable each() ENTT_NOEXCEPT {
+        return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
+    }
+
+    /*! @copydoc each */
+    [[nodiscard]] const_iterable each() const ENTT_NOEXCEPT {
+        return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
+    }
 };
 
 /**

+ 74 - 146
src/entt/entity/view.hpp

@@ -26,133 +26,6 @@ namespace entt {
 
 namespace internal {
 
-template<typename Storage>
-class iterable_storage final {
-    using base_type = typename Storage::base_type;
-
-    template<typename... It>
-    struct iterable_storage_iterator final {
-        using difference_type = std::ptrdiff_t;
-        using value_type = decltype(std::tuple_cat(std::tuple<typename Storage::entity_type>{}, std::declval<decltype(std::declval<Storage>().get_as_tuple({}))>()));
-        using pointer = input_iterator_pointer<value_type>;
-        using reference = value_type;
-        using iterator_category = std::input_iterator_tag;
-
-        template<typename... Discard>
-        iterable_storage_iterator(It... from, Discard...) ENTT_NOEXCEPT
-            : it{from...} {}
-
-        iterable_storage_iterator &operator++() ENTT_NOEXCEPT {
-            return (++std::get<It>(it), ...), *this;
-        }
-
-        iterable_storage_iterator operator++(int) ENTT_NOEXCEPT {
-            iterable_storage_iterator orig = *this;
-            return ++(*this), orig;
-        }
-
-        [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
-            return {*std::get<It>(it)...};
-        }
-
-        [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
-            return operator*();
-        }
-
-        [[nodiscard]] bool operator==(const iterable_storage_iterator &other) const ENTT_NOEXCEPT {
-            return std::get<0>(other.it) == std::get<0>(it);
-        }
-
-        [[nodiscard]] bool operator!=(const iterable_storage_iterator &other) const ENTT_NOEXCEPT {
-            return !(*this == other);
-        }
-
-    private:
-        std::tuple<It...> it;
-    };
-
-public:
-    using iterator = std::conditional_t<
-        ignore_as_empty_v<typename Storage::value_type>,
-        iterable_storage_iterator<typename base_type::iterator>,
-        iterable_storage_iterator<typename base_type::iterator, decltype(std::declval<Storage>().begin())>>;
-
-    iterable_storage(Storage &ref)
-        : pool{&ref} {}
-
-    [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-        return iterator{pool->base_type::begin(), pool->begin()};
-    }
-
-    [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-        return iterator{pool->base_type::end(), pool->end()};
-    }
-
-private:
-    Storage *const pool;
-};
-
-template<typename View>
-class iterable_view final {
-    struct iterable_view_iterator final {
-        using difference_type = std::ptrdiff_t;
-        using value_type = decltype(std::tuple_cat(std::tuple<typename View::entity_type>{}, std::declval<View>().get({})));
-        using pointer = input_iterator_pointer<value_type>;
-        using reference = value_type;
-        using iterator_category = std::input_iterator_tag;
-
-        iterable_view_iterator(typename View::iterator from, const View *parent) ENTT_NOEXCEPT
-            : it{from},
-              view{parent} {}
-
-        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]] pointer operator->() const ENTT_NOEXCEPT {
-            return operator*();
-        }
-
-        [[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:
-        typename View::iterator it;
-        const View *view;
-    };
-
-public:
-    using iterator = iterable_view_iterator;
-
-    iterable_view(const View &parent)
-        : view{parent} {}
-
-    [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-        return {view.begin(), &view};
-    }
-
-    [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-        return {view.end(), &view};
-    }
-
-private:
-    const View view;
-};
-
 template<typename Type, std::size_t Component, std::size_t Exclude>
 class view_iterator final {
     [[nodiscard]] bool valid() const {
@@ -195,14 +68,6 @@ public:
         return ++(*this), 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;
     }
@@ -211,6 +76,10 @@ public:
         return *operator->();
     }
 
+    [[nodiscard]] iterator_type base() const ENTT_NOEXCEPT {
+        return it;
+    }
+
 private:
     iterator_type it;
     iterator_type last;
@@ -218,6 +87,65 @@ private:
     std::array<const Type *, Exclude> filter;
 };
 
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+[[nodiscard]] bool operator==(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
+    return lhs.base() == rhs.base();
+}
+
+template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs>
+[[nodiscard]] bool operator!=(const view_iterator<LhsType, LhsArgs...> &lhs, const view_iterator<RhsType, RhsArgs...> &rhs) ENTT_NOEXCEPT {
+    return !(lhs == rhs);
+}
+
+template<typename It, typename... Storage>
+struct extended_view_iterator final {
+    using iterator_type = It;
+    using difference_type = std::ptrdiff_t;
+    using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Storage>().get_as_tuple({})...));
+    using pointer = input_iterator_pointer<value_type>;
+    using reference = value_type;
+    using iterator_category = std::input_iterator_tag;
+
+    extended_view_iterator(It from, std::tuple<Storage *...> storage) ENTT_NOEXCEPT
+        : it{from},
+          pools{storage} {}
+
+    extended_view_iterator &operator++() ENTT_NOEXCEPT {
+        return ++it, *this;
+    }
+
+    extended_view_iterator operator++(int) ENTT_NOEXCEPT {
+        extended_view_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
+        return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools);
+    }
+
+    [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
+        return operator*();
+    }
+
+    [[nodiscard]] iterator_type base() const ENTT_NOEXCEPT {
+        return it;
+    }
+
+private:
+    It it;
+    std::tuple<Storage *...> pools;
+};
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] bool operator==(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
+    return lhs.base() == rhs.base();
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] bool operator!=(const extended_view_iterator<Lhs...> &lhs, const extended_view_iterator<Rhs...> &rhs) ENTT_NOEXCEPT {
+    return !(lhs == rhs);
+}
+
 } // namespace internal
 
 /**
@@ -285,7 +213,7 @@ class basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>> {
 
     template<std::size_t Comp, typename Func, std::size_t... Index>
     void each(Func func, std::index_sequence<Index...>) const {
-        for(const auto curr: internal::iterable_storage{*std::get<Comp>(pools)}) {
+        for(const auto curr: std::get<Comp>(pools)->each()) {
             const auto entt = std::get<0>(curr);
 
             if(((sizeof...(Component) != 1u) || (entt != tombstone))
@@ -315,7 +243,7 @@ public:
     /*! @brief Bidirectional iterator type. */
     using iterator = internal::view_iterator<base_type, sizeof...(Component) - 1u, sizeof...(Exclude)>;
     /*! @brief Iterable view type. */
-    using iterable_view = internal::iterable_view<basic_view>;
+    using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, storage_type<Component>...>>;
 
     /*! @brief Default constructor to use to create empty, invalid views. */
     basic_view() ENTT_NOEXCEPT
@@ -549,14 +477,14 @@ public:
     /**
      * @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
+     * The iterable object returns a tuple that contains the current entity and
+     * a set of references to its non-empty components. The _constness_ of the
      * components is as requested.
      *
      * @return An iterable object to use to _visit_ the view.
      */
-    [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT {
-        return iterable_view{*this};
+    [[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+        return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}};
     }
 
     /**
@@ -623,7 +551,7 @@ public:
     /*! @brief Reversed iterator type. */
     using reverse_iterator = typename base_type::reverse_iterator;
     /*! @brief Iterable view type. */
-    using iterable_view = internal::iterable_storage<storage_type>;
+    using iterable = decltype(std::declval<storage_type>().each());
 
     /*! @brief Default constructor to use to create empty, invalid views. */
     basic_view() ENTT_NOEXCEPT
@@ -874,14 +802,14 @@ public:
     /**
      * @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 iterable object returns a tuple that contains the current entity and
+     * a reference to its component if it's a non-empty one. The _constness_ of
      * the component is as requested.
      *
      * @return An iterable object to use to _visit_ the view.
      */
-    [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT {
-        return iterable_view{*std::get<0>(pools)};
+    [[nodiscard]] iterable each() const ENTT_NOEXCEPT {
+        return std::get<0>(pools)->each();
     }
 
     /**

+ 6 - 6
test/entt/entity/group.cpp

@@ -183,15 +183,15 @@ TEST(NonOwningGroup, Each) {
     auto group = registry.group(entt::get<int, char>);
     auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>);
 
-    auto iterable = group.each();
-    auto citerable = cgroup.each();
-
     registry.emplace<int>(entity[0u], 0);
     registry.emplace<char>(entity[0u], 0);
 
     registry.emplace<int>(entity[1u], 1);
     registry.emplace<char>(entity[1u], 1);
 
+    auto iterable = group.each();
+    auto citerable = cgroup.each();
+
     ASSERT_NE(citerable.begin(), citerable.end());
     ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));
 
@@ -826,15 +826,15 @@ TEST(OwningGroup, Each) {
     auto group = registry.group<int>(entt::get<char>);
     auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>);
 
-    auto iterable = group.each();
-    auto citerable = cgroup.each();
-
     registry.emplace<int>(entity[0u], 0);
     registry.emplace<char>(entity[0u], 0);
 
     registry.emplace<int>(entity[1u], 1);
     registry.emplace<char>(entity[1u], 1);
 
+    auto iterable = group.each();
+    auto citerable = cgroup.each();
+
     ASSERT_NE(citerable.begin(), citerable.end());
     ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));
 

+ 134 - 0
test/entt/entity/storage.cpp

@@ -1037,6 +1037,140 @@ TEST(Storage, IteratorConversion) {
     ASSERT_NE(++cit, it);
 }
 
+TEST(Storage, Iterable) {
+    using iterator = typename entt::storage<boxed_int>::iterable::iterator;
+
+    static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, boxed_int &>>);
+    static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, boxed_int &>>);
+    static_assert(std::is_same_v<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity, boxed_int &>>>);
+    static_assert(std::is_same_v<typename iterator::reference, typename iterator::value_type>);
+
+    entt::storage<boxed_int> pool;
+    pool.emplace(entt::entity{1}, 99);
+    pool.emplace(entt::entity{3}, 42);
+    auto iterable = pool.each();
+
+    iterator end{iterable.begin()};
+    iterator begin{};
+    begin = iterable.end();
+    std::swap(begin, end);
+
+    ASSERT_EQ(begin, iterable.begin());
+    ASSERT_EQ(end, iterable.end());
+    ASSERT_NE(begin, end);
+
+    ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
+    ASSERT_EQ(std::get<1>(*begin.operator->().operator->()), boxed_int{42});
+    ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
+    ASSERT_EQ(std::get<1>(*begin), boxed_int{42});
+
+    ASSERT_EQ(begin++, iterable.begin());
+    ASSERT_EQ(++begin, iterable.end());
+
+    for(auto [entity, element]: iterable) {
+        static_assert(std::is_same_v<decltype(entity), entt::entity>);
+        static_assert(std::is_same_v<decltype(element), boxed_int &>);
+        ASSERT_TRUE(entity != entt::entity{1} || element == boxed_int{99});
+        ASSERT_TRUE(entity != entt::entity{3} || element == boxed_int{42});
+    }
+}
+
+TEST(Storage, ConstIterable) {
+    using iterator = typename entt::storage<boxed_int>::const_iterable::const_iterator;
+
+    static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, const boxed_int &>>);
+    static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity, const boxed_int &>>);
+    static_assert(std::is_same_v<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity, const boxed_int &>>>);
+    static_assert(std::is_same_v<typename iterator::reference, typename iterator::value_type>);
+
+    entt::storage<boxed_int> pool;
+    pool.emplace(entt::entity{1}, 99);
+    pool.emplace(entt::entity{3}, 42);
+    auto iterable = std::as_const(pool).each();
+
+    iterator end{iterable.cbegin()};
+    iterator begin{};
+    begin = iterable.cend();
+    std::swap(begin, end);
+
+    ASSERT_EQ(begin, iterable.cbegin());
+    ASSERT_EQ(end, iterable.cend());
+    ASSERT_NE(begin, end);
+
+    ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
+    ASSERT_EQ(std::get<1>(*begin.operator->().operator->()), boxed_int{42});
+    ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
+    ASSERT_EQ(std::get<1>(*begin), boxed_int{42});
+
+    ASSERT_EQ(begin++, iterable.cbegin());
+    ASSERT_EQ(++begin, iterable.cend());
+
+    for(auto [entity, element]: iterable) {
+        static_assert(std::is_same_v<decltype(entity), entt::entity>);
+        static_assert(std::is_same_v<decltype(element), const boxed_int &>);
+        ASSERT_TRUE(entity != entt::entity{1} || element == boxed_int{99});
+        ASSERT_TRUE(entity != entt::entity{3} || element == boxed_int{42});
+    }
+}
+
+TEST(Storage, IterableIteratorConversion) {
+    entt::storage<boxed_int> pool;
+    pool.emplace(entt::entity{3}, 42);
+
+    typename entt::storage<boxed_int>::iterable::iterator it = pool.each().begin();
+    typename entt::storage<boxed_int>::const_iterable::const_iterator cit = it;
+    
+    static_assert(std::is_same_v<decltype(*it), std::tuple<entt::entity, boxed_int &>>);
+    static_assert(std::is_same_v<decltype(*cit), std::tuple<entt::entity, const boxed_int &>>);
+
+    ASSERT_EQ(it, cit);
+    ASSERT_NE(++cit, it);
+}
+
+TEST(Storage, EmptyTypeIterable) {
+    using iterator = typename entt::storage<empty_stable_type>::iterable::iterator;
+
+    static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity>>);
+    static_assert(std::is_same_v<iterator::value_type, std::tuple<entt::entity>>);
+    static_assert(std::is_same_v<typename iterator::pointer, entt::input_iterator_pointer<std::tuple<entt::entity>>>);
+    static_assert(std::is_same_v<typename iterator::reference, typename iterator::value_type>);
+
+    entt::storage<empty_stable_type> pool;
+    pool.emplace(entt::entity{1});
+    pool.emplace(entt::entity{3});
+    auto iterable = pool.each();
+
+    iterator end{iterable.begin()};
+    iterator begin{};
+    begin = iterable.end();
+    std::swap(begin, end);
+
+    ASSERT_EQ(begin, iterable.begin());
+    ASSERT_EQ(end, iterable.end());
+    ASSERT_NE(begin, end);
+
+    ASSERT_EQ(std::get<0>(*begin.operator->().operator->()), entt::entity{3});
+    ASSERT_EQ(std::get<0>(*begin), entt::entity{3});
+
+    ASSERT_EQ(begin++, iterable.begin());
+    ASSERT_EQ(++begin, iterable.end());
+
+    for(auto [entity]: iterable) {
+        static_assert(std::is_same_v<decltype(entity), entt::entity>);
+        ASSERT_TRUE(entity == entt::entity{1} || entity == entt::entity{3});
+    }
+}
+
+TEST(Storage, IterableAlgorithmCompatibility) {
+    entt::storage<boxed_int> pool;
+    pool.emplace(entt::entity{3}, 42);
+
+    const auto iterable = pool.each();
+    const auto it = std::find_if(iterable.begin(), iterable.end(), [](auto args) { return std::get<0>(args) == entt::entity{3}; });
+
+    ASSERT_EQ(std::get<0>(*it), entt::entity{3});
+}
+
 TEST(Storage, Raw) {
     entt::storage<int> pool;
 

+ 6 - 6
test/entt/entity/view.cpp

@@ -176,12 +176,12 @@ TEST(SingleComponentView, Each) {
     auto view = registry.view<int>();
     auto cview = std::as_const(registry).view<const int>();
 
-    auto iterable = view.each();
-    auto citerable = cview.each();
-
     registry.emplace<int>(entity[0u], 0);
     registry.emplace<int>(entity[1u], 1);
 
+    auto iterable = view.each();
+    auto citerable = cview.each();
+
     ASSERT_NE(citerable.begin(), citerable.end());
     ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));
 
@@ -677,15 +677,15 @@ TEST(MultiComponentView, Each) {
     auto view = registry.view<int, char>();
     auto cview = std::as_const(registry).view<const int, const char>();
 
-    auto iterable = view.each();
-    auto citerable = cview.each();
-
     registry.emplace<int>(entity[0u], 0);
     registry.emplace<char>(entity[0u], 0);
 
     registry.emplace<int>(entity[1u], 1);
     registry.emplace<char>(entity[1u], 1);
 
+    auto iterable = view.each();
+    auto citerable = cview.each();
+
     ASSERT_NE(citerable.begin(), citerable.end());
     ASSERT_NO_THROW(iterable.begin()->operator=(*iterable.begin()));