Просмотр исходного кода

view/group: all iterators are at least bidirectional (close #370)

Michele Caini 6 лет назад
Родитель
Сommit
5a1af60357

+ 3 - 3
docs/md/entity.md

@@ -1734,9 +1734,9 @@ expedients.
 
 ## Iterators
 
-A special mention is needed for the iterators returned by the views and the
-groups. Most of the time they meet the requirements of random access iterators,
-in all cases they meet at least the requirements of forward iterators.<br/>
+A special mention is needed for the iterators returned by views and groups. Most
+of the times they meet the requirements of random access iterators, in all cases
+they meet at least the requirements of bidirectional iterators.<br/>
 In other terms, they are suitable for use with the parallel algorithms of the
 standard library. If it's not clear, this is a great thing.
 

+ 27 - 21
src/entt/entity/runtime_view.hpp

@@ -64,20 +64,20 @@ class basic_runtime_view {
     class iterator {
         friend class basic_runtime_view<Entity>;
 
-        iterator(underlying_iterator_type first, underlying_iterator_type last, const sparse_set<Entity> * const *others, const sparse_set<Entity> * const *length) ENTT_NOEXCEPT
-            : begin{first},
-              end{last},
-              from{others},
-              to{length}
+        using direct_type = std::vector<const sparse_set<Entity> *>;
+
+        iterator(const direct_type *all, underlying_iterator_type curr) ENTT_NOEXCEPT
+            : pools{all},
+              it{curr}
         {
-            if(begin != end && !valid()) {
+            if(it != (*pools)[0]->end() && !valid()) {
                 ++(*this);
             }
         }
 
         bool valid() const {
-            return std::all_of(from, to, [entt = *begin](const auto *view) {
-                return view->has(entt);
+            return std::all_of(pools->begin()++, pools->end(), [entt = *it](const auto *curr) {
+                return curr->has(entt);
             });
         }
 
@@ -86,12 +86,13 @@ class basic_runtime_view {
         using value_type = typename underlying_iterator_type::value_type;
         using pointer = typename underlying_iterator_type::pointer;
         using reference = typename underlying_iterator_type::reference;
-        using iterator_category = std::forward_iterator_tag;
+        using iterator_category = std::bidirectional_iterator_tag;
 
         iterator() ENTT_NOEXCEPT = default;
 
         iterator & operator++() {
-            return (++begin != end && !valid()) ? ++(*this) : *this;
+            while(++it != (*pools)[0]->end() && !valid());
+            return *this;
         }
 
         iterator operator++(int) {
@@ -99,8 +100,18 @@ class basic_runtime_view {
             return ++(*this), orig;
         }
 
+        iterator & operator--() ENTT_NOEXCEPT {
+            while(--it != (*pools)[0]->begin() && !valid());
+            return *this;
+        }
+
+        iterator operator--(int) ENTT_NOEXCEPT {
+            iterator orig = *this;
+            return --(*this), orig;
+        }
+
         bool operator==(const iterator &other) const ENTT_NOEXCEPT {
-            return other.begin == begin;
+            return other.it == it;
         }
 
         bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
@@ -108,7 +119,7 @@ class basic_runtime_view {
         }
 
         pointer operator->() const {
-            return begin.operator->();
+            return it.operator->();
         }
 
         reference operator*() const {
@@ -116,10 +127,8 @@ class basic_runtime_view {
         }
 
     private:
-        underlying_iterator_type begin;
-        underlying_iterator_type end;
-        const sparse_set<Entity> * const *from;
-        const sparse_set<Entity> * const *to;
+        const direct_type *pools;
+        underlying_iterator_type it;
     };
 
     basic_runtime_view(std::vector<const sparse_set<Entity> *> others) ENTT_NOEXCEPT
@@ -179,9 +188,7 @@ public:
         iterator_type it{};
 
         if(valid()) {
-            const auto &pool = *pools.front();
-            const auto * const *data = pools.data();
-            it = { pool.begin(), pool.end(), data + 1, data + pools.size() };
+            it = { &pools, pools[0]->begin() };
         }
 
         return it;
@@ -206,8 +213,7 @@ public:
         iterator_type it{};
 
         if(valid()) {
-            const auto &pool = *pools.front();
-            it = { pool.end(), pool.end(), nullptr, nullptr };
+            it = { &pools, pools[0]->end() };
         }
 
         return it;

+ 3 - 3
src/entt/entity/sparse_set.hpp

@@ -55,10 +55,10 @@ class sparse_set {
     class iterator {
         friend class sparse_set<Entity>;
 
-        using direct_type = const std::vector<Entity>;
+        using direct_type = std::vector<Entity>;
         using index_type = typename traits_type::difference_type;
 
-        iterator(direct_type *ref, const index_type idx) ENTT_NOEXCEPT
+        iterator(const direct_type *ref, const index_type idx) ENTT_NOEXCEPT
             : direct{ref}, index{idx}
         {}
 
@@ -149,7 +149,7 @@ class sparse_set {
         }
 
     private:
-        direct_type *direct;
+        const direct_type *direct;
         index_type index;
     };
 

+ 28 - 16
src/entt/entity/view.hpp

@@ -81,24 +81,25 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
     using underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
     using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
     using filter_type = std::array<const sparse_set<Entity> *, sizeof...(Exclude)>;
+    using traits_type = entt_traits<std::underlying_type_t<Entity>>;
 
     class iterator {
         friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
 
-        iterator(underlying_iterator_type first, underlying_iterator_type last, unchecked_type other, filter_type ignore) ENTT_NOEXCEPT
-            : begin{first},
-              end{last},
+        iterator(const sparse_set<Entity> *candidate, unchecked_type other, filter_type ignore, underlying_iterator_type curr) ENTT_NOEXCEPT
+            : view{candidate},
               unchecked{other},
-              filter{ignore}
+              filter{ignore},
+              it{curr}
         {
-            if(begin != end && !valid()) {
+            if(it != view->end() && !valid()) {
                 ++(*this);
             }
         }
 
         bool valid() const {
-            return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); })
-                    && std::none_of(filter.cbegin(), filter.cend(), [this](const sparse_set<Entity> *view) { return view->has(*begin); });
+            return std::all_of(unchecked.cbegin(), unchecked.cend(), [entt = *it](const sparse_set<Entity> *curr) { return curr->has(entt); })
+                    && std::none_of(filter.cbegin(), filter.cend(), [entt = *it](const sparse_set<Entity> *curr) { return curr->has(entt); });
         }
 
     public:
@@ -106,12 +107,13 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         using value_type = typename underlying_iterator_type::value_type;
         using pointer = typename underlying_iterator_type::pointer;
         using reference = typename underlying_iterator_type::reference;
-        using iterator_category = std::forward_iterator_tag;
+        using iterator_category = std::bidirectional_iterator_tag;
 
         iterator() ENTT_NOEXCEPT = default;
 
         iterator & operator++() {
-            return (++begin != end && !valid()) ? ++(*this) : *this;
+            while(++it != view->end() && !valid());
+            return *this;
         }
 
         iterator operator++(int) {
@@ -119,8 +121,18 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
             return ++(*this), orig;
         }
 
+        iterator & operator--() ENTT_NOEXCEPT {
+            while(--it != view->begin() && !valid());
+            return *this;
+        }
+
+        iterator operator--(int) ENTT_NOEXCEPT {
+            iterator orig = *this;
+            return --(*this), orig;
+        }
+
         bool operator==(const iterator &other) const ENTT_NOEXCEPT {
-            return other.begin == begin;
+            return other.it == it;
         }
 
         bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
@@ -128,7 +140,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
 
         pointer operator->() const {
-            return begin.operator->();
+            return it.operator->();
         }
 
         reference operator*() const {
@@ -136,10 +148,10 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
 
     private:
-        underlying_iterator_type begin;
-        underlying_iterator_type end;
+        const sparse_set<Entity> *view;
         unchecked_type unchecked;
         filter_type filter;
+        underlying_iterator_type it;
     };
 
     // we could use pool_type<Component> *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug)
@@ -305,7 +317,7 @@ public:
     iterator_type begin() const {
         const auto *view = candidate();
         const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
-        return iterator_type{view->begin(), view->end(), unchecked(view), ignore};
+        return iterator_type{view, unchecked(view), ignore, view->begin()};
     }
 
     /**
@@ -326,7 +338,7 @@ public:
     iterator_type end() const {
         const auto *view = candidate();
         const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
-        return iterator_type{view->end(), view->end(), unchecked(view), ignore};
+        return iterator_type{view, unchecked(view), ignore, view->end()};
     }
 
     /**
@@ -338,7 +350,7 @@ public:
     iterator_type find(const entity_type entt) const {
         const auto *view = candidate();
         const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
-        iterator_type it{view->find(entt), view->end(), unchecked(view), ignore};
+        iterator_type it{view, unchecked(view), ignore, view->find(entt)};
         return (it != end() && *it == entt) ? it : end();
     }
 

+ 12 - 6
test/entt/entity/runtime_view.cpp

@@ -26,13 +26,13 @@ TEST(RuntimeView, Functionalities) {
 
     registry.assign<char>(e1);
 
-    auto it = registry.runtime_view(std::begin(types), std::end(types)).begin();
+    auto it = view.begin();
 
     ASSERT_EQ(*it, e1);
-    ASSERT_EQ(++it, (registry.runtime_view(std::begin(types), std::end(types)).end()));
+    ASSERT_EQ(++it, (view.end()));
 
-    ASSERT_NO_THROW((registry.runtime_view(std::begin(types), std::end(types)).begin()++));
-    ASSERT_NO_THROW((++registry.runtime_view(std::begin(types), std::end(types)).begin()));
+    ASSERT_NO_THROW((view.begin()++));
+    ASSERT_NO_THROW((++view.begin()));
 
     ASSERT_NE(view.begin(), view.end());
     ASSERT_EQ(view.size(), decltype(view.size()){1});
@@ -67,8 +67,14 @@ TEST(RuntimeView, Iterator) {
     ASSERT_EQ(end, view.end());
     ASSERT_NE(begin, end);
 
-    ASSERT_EQ(view.begin()++, view.begin());
-    ASSERT_EQ(++view.begin(), view.end());
+    ASSERT_EQ(begin++, view.begin());
+    ASSERT_EQ(begin--, view.end());
+
+    ASSERT_EQ(++begin, view.end());
+    ASSERT_EQ(--begin, view.begin());
+
+    ASSERT_EQ(*begin, entity);
+    ASSERT_EQ(*begin.operator->(), entity);
 }
 
 TEST(RuntimeView, Contains) {

+ 8 - 2
test/entt/entity/view.cpp

@@ -286,8 +286,14 @@ TEST(MultiComponentView, Iterator) {
     ASSERT_EQ(end, view.end());
     ASSERT_NE(begin, end);
 
-    ASSERT_EQ(view.begin()++, view.begin());
-    ASSERT_EQ(++view.begin(), view.end());
+    ASSERT_EQ(begin++, view.begin());
+    ASSERT_EQ(begin--, view.end());
+
+    ASSERT_EQ(++begin, view.end());
+    ASSERT_EQ(--begin, view.begin());
+
+    ASSERT_EQ(*begin, entity);
+    ASSERT_EQ(*begin.operator->(), entity);
 }
 
 TEST(MultiComponentView, Contains) {