ソースを参照

group: added reverse iterators, rbegin and rend to iterable groups

Michele Caini 5 年 前
コミット
d255889c5c
4 ファイル変更112 行追加149 行削除
  1. 7 3
      TODO
  2. 78 115
      src/entt/entity/group.hpp
  3. 24 28
      test/entt/entity/group.cpp
  4. 3 3
      test/entt/entity/view.cpp

+ 7 - 3
TODO

@@ -8,20 +8,24 @@
 * static reflection, hint: template<> meta_type_t<Type>: meta_descriptor<name, func..., props..., etc...> (see #342)
 * update documentation for meta, it contains less than half of the actual feature
 
+* custom pools example:
+  - multi instance
+  - tables
+  - enable/disable component
+  - spatial query
+  - ...
+
 WIP:
 * split basic_view and view_t like sfinae-friendly class
 * add view-like functionalities to the view_pack
 * HP: write documentation for custom storages and views!!
 * view pack: plain function as an alias for operator|, reverse iterators, rbegin and rend
-* use extended get to remove is_eto_eligible checks from views and groups
 * pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
 * make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use
 * add observer functions aside observer class
-* introduce the component iterators for non-contiguous collections of entities (multi component views, observers, user defined collections)
 * snapshot: support for range-based archives
 * update snapshot documentation to describe alternatives
 * page size 0 -> page less mode
-* add ::reach and rev iterators to proxy objects for faster and unsafe iterations
 * add example: 64 bit ids with 32 bits reserved for users' purposes
 * composable views and "faster views", deprecate non-owning groups
 * add meta dynamic cast (search base for T in parent, we have the meta type already)

+ 78 - 115
src/entt/entity/group.hpp

@@ -74,28 +74,20 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> final {
     class iterable_group {
         friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>>;
 
+        template<typename It>
         class iterable_group_iterator {
             friend class iterable_group;
 
-            using it_type = typename basic_sparse_set<Entity>::iterator;
-            using ref_type = decltype(std::tuple_cat(std::declval<std::conditional_t<is_eto_eligible_v<Get>, std::tuple<>, std::tuple<pool_type<Get> *>>>()...));
-
-            iterable_group_iterator(it_type from, ref_type ref) ENTT_NOEXCEPT
+            iterable_group_iterator(It from, const basic_group &parent) ENTT_NOEXCEPT
                 : it{from},
-                  pools{ref}
+                  group{parent}
             {}
 
         public:
             using difference_type = std::ptrdiff_t;
-            using value_type = decltype(std::tuple_cat(
-                std::declval<std::tuple<Entity>>(),
-                std::declval<std::conditional_t<is_eto_eligible_v<Get>, std::tuple<>, std::tuple<Get>>>()...
-            ));
+            using value_type = decltype(std::tuple_cat(std::tuple<typename std::iterator_traits<It>::value_type>{}, std::declval<basic_group>().get({})));
             using pointer = void;
-            using reference = decltype(std::tuple_cat(
-                std::declval<std::tuple<Entity>>(),
-                std::declval<std::conditional_t<is_eto_eligible_v<Get>, std::tuple<>, std::tuple<Get &>>>()...
-            ));
+            using reference = value_type;
             using iterator_category = std::input_iterator_tag;
 
             iterable_group_iterator & operator++() ENTT_NOEXCEPT {
@@ -108,7 +100,7 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> final {
             }
 
             [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
-                return std::apply([entt = *it](auto *... cpool) { return reference{entt, cpool->get(entt)...}; }, pools);
+                return std::tuple_cat(std::make_tuple(*it), group.get(*it));
             }
 
             [[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
@@ -120,41 +112,36 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> final {
             }
 
         private:
-            it_type it;
-            ref_type pools;
+            It it;
+            const basic_group group;
         };
 
-        iterable_group(const basic_sparse_set<Entity> &ref, std::tuple<pool_type<Get> *...> gpools)
-            : handler{&ref},
-              pools{gpools}
+        iterable_group(const basic_group &parent)
+            : group{parent}
         {}
 
     public:
-        using iterator = iterable_group_iterator;
+        using iterator = iterable_group_iterator<typename basic_sparse_set<Entity>::iterator>;
+        using reverse_iterator = iterable_group_iterator<typename basic_sparse_set<Entity>::reverse_iterator>;
 
         [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-            return iterable_group_iterator{handler->begin(), std::tuple_cat([](auto *cpool) {
-                if constexpr(is_eto_eligible_v<typename std::remove_reference_t<decltype(*cpool)>::value_type>) {
-                    return std::tuple{};
-                } else {
-                    return std::make_tuple(cpool);
-                }
-            }(std::get<pool_type<Get> *>(pools))...)};
+            return iterator{group.begin(), group};
         }
 
         [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-            return iterable_group_iterator{handler->end(), std::tuple_cat([](auto *cpool) {
-                if constexpr(is_eto_eligible_v<typename std::remove_reference_t<decltype(*cpool)>::value_type>) {
-                    return std::tuple{};
-                } else {
-                    return std::make_tuple(cpool);
-                }
-            }(std::get<pool_type<Get> *>(pools))...)};
+            return iterator{group.end(), group};
+        }
+
+        [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+            return reverse_iterator{group.rbegin(), group};
+        }
+
+        [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+            return reverse_iterator{group.rend(), group};
         }
 
     private:
-        const basic_sparse_set<Entity> *handler;
-        std::tuple<pool_type<Get> *...> pools;
+        const basic_group group;
     };
 
     basic_group(basic_sparse_set<Entity> &ref, pool_type<Get> &... gpool) ENTT_NOEXCEPT
@@ -478,7 +465,7 @@ public:
      * @return An iterable object to use to _visit_ the group.
      */
     [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
-        return iterable_group{*handler, pools};
+        return iterable_group{*this};
     }
 
     /**
@@ -620,36 +607,28 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final
     class iterable_group {
         friend class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...>;
 
+        template<bool Reverse>
         class iterable_group_iterator {
             friend class iterable_group;
 
-            using it_type = typename basic_sparse_set<Entity>::iterator;
-            using owned_type = decltype(std::tuple_cat(std::declval<std::conditional_t<is_eto_eligible_v<Owned>, std::tuple<>, std::tuple<component_iterator<Owned>>>>()...));
-            using get_type = decltype(std::tuple_cat(std::declval<std::conditional_t<is_eto_eligible_v<Get>, std::tuple<>, std::tuple<pool_type<Get> *>>>()...));
-
-            iterable_group_iterator(it_type from, owned_type oref, get_type gref) ENTT_NOEXCEPT
-                : it{from},
-                  owned{oref},
-                  get{gref}
+            iterable_group_iterator(const std::size_t idx, const basic_group &parent) ENTT_NOEXCEPT
+                : index{idx},
+                  group{parent}
             {}
 
         public:
             using difference_type = std::ptrdiff_t;
-            using value_type = decltype(std::tuple_cat(
-                std::declval<std::tuple<Entity>>(),
-                std::declval<std::conditional_t<is_eto_eligible_v<Owned>, std::tuple<>, std::tuple<Owned>>>()...,
-                std::declval<std::conditional_t<is_eto_eligible_v<Get>, std::tuple<>, std::tuple<Get>>>()...
-            ));
+            using value_type = decltype(std::tuple_cat(std::tuple<Entity>{}, std::declval<basic_group>().get({})));
             using pointer = void;
-            using reference = decltype(std::tuple_cat(
-                std::declval<std::tuple<Entity>>(),
-                std::declval<std::conditional_t<is_eto_eligible_v<Owned>, std::tuple<>, std::tuple<Owned &>>>()...,
-                std::declval<std::conditional_t<is_eto_eligible_v<Get>, std::tuple<>, std::tuple<Get &>>>()...
-            ));
+            using reference = value_type;
             using iterator_category = std::input_iterator_tag;
 
             iterable_group_iterator & operator++() ENTT_NOEXCEPT {
-                return ++it, std::apply([](auto &&... curr) { (++curr, ...); }, owned), *this;
+                if constexpr(Reverse) {
+                    return ++index, *this;
+                } else {
+                    return --index, *this;
+                }
             }
 
             iterable_group_iterator operator++(int) ENTT_NOEXCEPT {
@@ -658,15 +637,17 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final
             }
 
             [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
-                return std::tuple_cat(
-                    std::make_tuple(*it),
-                    std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr...); }, owned),
-                    std::apply([entt = *it](auto &&... curr) { return std::forward_as_tuple(curr->get(entt)...); }, get)
-                );
+                if constexpr(Reverse) {
+                    const auto entt = group.data()[index];
+                    return std::tuple_cat(std::make_tuple(entt), group.get(entt, index));
+                } else {
+                    const auto entt = group.data()[index - 1u];
+                    return std::tuple_cat(std::make_tuple(entt), group.get(entt, index - 1u));
+                }
             }
 
             [[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
-                return other.it == it;
+                return other.index == index;
             }
 
             [[nodiscard]] bool operator!=(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
@@ -674,62 +655,36 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final
             }
 
         private:
-            it_type it;
-            owned_type owned;
-            get_type get;
+            std::size_t index;
+            const basic_group group;
         };
 
-        iterable_group(std::tuple<pool_type<Owned> *..., pool_type<Get> *...> cpools, const std::size_t &extent)
-            : pools{cpools},
-              length{&extent}
+        iterable_group(const basic_group &parent)
+            : group{parent}
         {}
 
     public:
-        using iterator = iterable_group_iterator;
+        using iterator = iterable_group_iterator<false>;
+        using reverse_iterator = iterable_group_iterator<true>;
 
         [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
-            return iterable_group_iterator{
-                std::get<0>(pools)->basic_sparse_set<Entity>::end() - *length,
-                std::tuple_cat([length = *length](auto *cpool) {
-                    if constexpr(is_eto_eligible_v<typename std::remove_reference_t<decltype(*cpool)>::value_type>) {
-                        return std::tuple{};
-                    } else {
-                        return std::make_tuple(cpool->end() - length);
-                    }
-                }(std::get<pool_type<Owned> *>(pools))...),
-                std::tuple_cat([](auto *cpool) {
-                    if constexpr(is_eto_eligible_v<typename std::remove_reference_t<decltype(*cpool)>::value_type>) {
-                        return std::tuple{};
-                    } else {
-                        return std::make_tuple(cpool);
-                    }
-                }(std::get<pool_type<Get> *>(pools))...)
-            };
+            return iterator{group.size(), group};
         }
 
         [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
-            return iterable_group_iterator{
-                std::get<0>(pools)->basic_sparse_set<Entity>::end(),
-                std::tuple_cat([](auto *cpool) {
-                    if constexpr(is_eto_eligible_v<typename std::remove_reference_t<decltype(*cpool)>::value_type>) {
-                        return std::tuple{};
-                    } else {
-                        return std::make_tuple(cpool->end());
-                    }
-                }(std::get<pool_type<Owned> *>(pools))...),
-                std::tuple_cat([](auto *cpool) {
-                    if constexpr(is_eto_eligible_v<typename std::remove_reference_t<decltype(*cpool)>::value_type>) {
-                        return std::tuple{};
-                    } else {
-                        return std::make_tuple(cpool);
-                    }
-                }(std::get<pool_type<Get> *>(pools))...)
-            };
+            return iterator{{}, group};
+        }
+
+        [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT {
+            return reverse_iterator{{}, group};
+        }
+
+        [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT {
+            return reverse_iterator{group.size(), group};
         }
 
     private:
-        const std::tuple<pool_type<Owned> *..., pool_type<Get> *...> pools;
-        const std::size_t *length;
+        const basic_group group;
     };
 
     basic_group(const std::size_t &extent, pool_type<Owned> &... opool, pool_type<Get> &... gpool) ENTT_NOEXCEPT
@@ -737,6 +692,22 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final
           length{&extent}
     {}
 
+    [[nodiscard]] decltype(auto) get(const Entity entt, const std::size_t index) const {
+        auto filter = [entt, index](auto *cpool) {
+            using value_type = typename std::remove_reference_t<decltype(*cpool)>::value_type;
+        
+            if constexpr(is_eto_eligible_v<value_type>) {
+                return std::tuple{};
+            } else if constexpr((std::is_same_v<value_type, std::remove_const_t<Owned>> || ...)) {
+                return std::forward_as_tuple(cpool->raw()[index]);
+            } else {
+                return std::forward_as_tuple(cpool->get(entt));
+            }
+        };
+        
+        return std::tuple_cat(filter(std::get<pool_type<Owned> *>(pools))..., filter(std::get<pool_type<Get> *>(pools))...);
+    }
+
     template<typename Func, typename... Strong, typename... Weak>
     void traverse(Func func, type_list<Strong...>, type_list<Weak...>) const {
         [[maybe_unused]] auto it = std::make_tuple((std::get<pool_type<Strong> *>(pools)->end() - *length)...);
@@ -997,15 +968,7 @@ public:
         ENTT_ASSERT(contains(entt));
 
         if constexpr(sizeof...(Component) == 0) {
-            auto filter = [entt](auto *cpool) {
-                if constexpr(is_eto_eligible_v<typename std::remove_reference_t<decltype(*cpool)>::value_type>) {
-                    return std::tuple{};
-                } else {
-                    return std::forward_as_tuple(cpool->get(entt));
-                }
-            };
-
-            return std::tuple_cat(filter(std::get<pool_type<Owned> *>(pools))..., filter(std::get<pool_type<Get> *>(pools))...);
+            return get(entt, std::get<0>(pools)->index(entt));
         } else if constexpr(sizeof...(Component) == 1) {
             return (std::get<pool_type<Component> *>(pools)->get(entt), ...);
         } else {
@@ -1056,7 +1019,7 @@ public:
      * @return An iterable object to use to _visit_ the group.
      */
     [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT {
-        return iterable_group{pools, *length};
+        return iterable_group{*this};
     }
 
     /**

+ 24 - 28
test/entt/entity/group.cpp

@@ -151,36 +151,34 @@ TEST(NonOwningGroup, Each) {
     auto group = registry.group(entt::get<int, char>);
 
     const auto e0 = registry.create();
-    registry.emplace<int>(e0);
+    registry.emplace<int>(e0, 0);
     registry.emplace<char>(e0);
 
     const auto e1 = registry.create();
-    registry.emplace<int>(e1);
+    registry.emplace<int>(e1, 1);
     registry.emplace<char>(e1);
 
     auto cgroup = std::as_const(registry).group(entt::get<const int, const char>);
     std::size_t cnt = 0;
 
+    for(auto first = cgroup.each().rbegin(), last = cgroup.each().rend(); first != last; ++first) {
+        static_assert(std::is_same_v<decltype(*first), std::tuple<entt::entity, const int &, const char &>>);
+        ASSERT_EQ(std::get<1>(*first), cnt++);
+    }
+
     group.each([&cnt](auto, int &, char &) { ++cnt; });
     group.each([&cnt](int &, char &) { ++cnt; });
 
-    for(auto &&[entt, iv, cv]: group.each()) {
-        static_assert(std::is_same_v<decltype(entt), entt::entity>);
-        static_assert(std::is_same_v<decltype(iv), int &>);
-        static_assert(std::is_same_v<decltype(cv), char &>);
-        ++cnt;
-    }
-
     ASSERT_EQ(cnt, std::size_t{6});
 
-    cgroup.each([&cnt](auto, const int &, const char &) { --cnt; });
     cgroup.each([&cnt](const int &, const char &) { --cnt; });
+    cgroup.each([&cnt](auto, const int &, const char &) { --cnt; });
 
-    for(auto &&[entt, iv, cv]: cgroup.each()) {
+    for(auto &&[entt, iv, cv]: group.each()) {
         static_assert(std::is_same_v<decltype(entt), entt::entity>);
-        static_assert(std::is_same_v<decltype(iv), const int &>);
-        static_assert(std::is_same_v<decltype(cv), const char &>);
-        --cnt;
+        static_assert(std::is_same_v<decltype(iv), int &>);
+        static_assert(std::is_same_v<decltype(cv), char &>);
+        ASSERT_EQ(iv, --cnt);
     }
 
     ASSERT_EQ(cnt, std::size_t{0});
@@ -723,36 +721,34 @@ TEST(OwningGroup, Each) {
     auto group = registry.group<int>(entt::get<char>);
 
     const auto e0 = registry.create();
-    registry.emplace<int>(e0);
+    registry.emplace<int>(e0, 0);
     registry.emplace<char>(e0);
 
     const auto e1 = registry.create();
-    registry.emplace<int>(e1);
+    registry.emplace<int>(e1, 1);
     registry.emplace<char>(e1);
 
     auto cgroup = std::as_const(registry).group<const int>(entt::get<const char>);
     std::size_t cnt = 0;
 
+    for(auto first = cgroup.each().rbegin(), last = cgroup.each().rend(); first != last; ++first) {
+        static_assert(std::is_same_v<decltype(*first), std::tuple<entt::entity, const int &, const char &>>);
+        ASSERT_EQ(std::get<1>(*first), cnt++);
+    }
+
     group.each([&cnt](auto, int &, char &) { ++cnt; });
     group.each([&cnt](int &, char &) { ++cnt; });
 
-    for(auto &&[entt, iv, cv]: group.each()) {
-        static_assert(std::is_same_v<decltype(entt), entt::entity>);
-        static_assert(std::is_same_v<decltype(iv), int &>);
-        static_assert(std::is_same_v<decltype(cv), char &>);
-        ++cnt;
-    }
-
     ASSERT_EQ(cnt, std::size_t{6});
 
-    cgroup.each([&cnt](auto, const int &, const char &) { --cnt; });
     cgroup.each([&cnt](const int &, const char &) { --cnt; });
+    cgroup.each([&cnt](auto, const int &, const char &) { --cnt; });
 
-    for(auto &&[entt, iv, cv]: cgroup.each()) {
+    for(auto &&[entt, iv, cv]: group.each()) {
         static_assert(std::is_same_v<decltype(entt), entt::entity>);
-        static_assert(std::is_same_v<decltype(iv), const int &>);
-        static_assert(std::is_same_v<decltype(cv), const char &>);
-        --cnt;
+        static_assert(std::is_same_v<decltype(iv), int &>);
+        static_assert(std::is_same_v<decltype(cv), char &>);
+        ASSERT_EQ(iv, --cnt);
     }
 
     ASSERT_EQ(cnt, std::size_t{0});

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

@@ -469,10 +469,10 @@ TEST(MultiComponentView, Each) {
     cview.each([&cnt](const int &, const char &) { --cnt; });
     cview.each([&cnt](auto, const int &, const char &) { --cnt; });
 
-    for(auto &&[entt, iv, cv]: cview.each()) {
+    for(auto &&[entt, iv, cv]: view.each()) {
         static_assert(std::is_same_v<decltype(entt), entt::entity>);
-        static_assert(std::is_same_v<decltype(iv), const int &>);
-        static_assert(std::is_same_v<decltype(cv), const char &>);
+        static_assert(std::is_same_v<decltype(iv), int &>);
+        static_assert(std::is_same_v<decltype(cv), char &>);
         ASSERT_EQ(iv, --cnt);
     }