Browse Source

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

Michele Caini 6 years ago
parent
commit
5a1af60357

+ 3 - 3
docs/md/entity.md

@@ -1734,9 +1734,9 @@ expedients.
 
 
 ## Iterators
 ## 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
 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.
 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 {
     class iterator {
         friend class basic_runtime_view<Entity>;
         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);
                 ++(*this);
             }
             }
         }
         }
 
 
         bool valid() const {
         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 value_type = typename underlying_iterator_type::value_type;
         using pointer = typename underlying_iterator_type::pointer;
         using pointer = typename underlying_iterator_type::pointer;
         using reference = typename underlying_iterator_type::reference;
         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() ENTT_NOEXCEPT = default;
 
 
         iterator & operator++() {
         iterator & operator++() {
-            return (++begin != end && !valid()) ? ++(*this) : *this;
+            while(++it != (*pools)[0]->end() && !valid());
+            return *this;
         }
         }
 
 
         iterator operator++(int) {
         iterator operator++(int) {
@@ -99,8 +100,18 @@ class basic_runtime_view {
             return ++(*this), orig;
             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 {
         bool operator==(const iterator &other) const ENTT_NOEXCEPT {
-            return other.begin == begin;
+            return other.it == it;
         }
         }
 
 
         bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
         bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
@@ -108,7 +119,7 @@ class basic_runtime_view {
         }
         }
 
 
         pointer operator->() const {
         pointer operator->() const {
-            return begin.operator->();
+            return it.operator->();
         }
         }
 
 
         reference operator*() const {
         reference operator*() const {
@@ -116,10 +127,8 @@ class basic_runtime_view {
         }
         }
 
 
     private:
     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
     basic_runtime_view(std::vector<const sparse_set<Entity> *> others) ENTT_NOEXCEPT
@@ -179,9 +188,7 @@ public:
         iterator_type it{};
         iterator_type it{};
 
 
         if(valid()) {
         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;
         return it;
@@ -206,8 +213,7 @@ public:
         iterator_type it{};
         iterator_type it{};
 
 
         if(valid()) {
         if(valid()) {
-            const auto &pool = *pools.front();
-            it = { pool.end(), pool.end(), nullptr, nullptr };
+            it = { &pools, pools[0]->end() };
         }
         }
 
 
         return it;
         return it;

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

@@ -55,10 +55,10 @@ class sparse_set {
     class iterator {
     class iterator {
         friend class sparse_set<Entity>;
         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;
         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}
             : direct{ref}, index{idx}
         {}
         {}
 
 
@@ -149,7 +149,7 @@ class sparse_set {
         }
         }
 
 
     private:
     private:
-        direct_type *direct;
+        const direct_type *direct;
         index_type index;
         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 underlying_iterator_type = typename sparse_set<Entity>::iterator_type;
     using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
     using unchecked_type = std::array<const sparse_set<Entity> *, (sizeof...(Component) - 1)>;
     using filter_type = std::array<const sparse_set<Entity> *, sizeof...(Exclude)>;
     using filter_type = std::array<const sparse_set<Entity> *, sizeof...(Exclude)>;
+    using traits_type = entt_traits<std::underlying_type_t<Entity>>;
 
 
     class iterator {
     class iterator {
         friend class basic_view<Entity, exclude_t<Exclude...>, Component...>;
         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},
               unchecked{other},
-              filter{ignore}
+              filter{ignore},
+              it{curr}
         {
         {
-            if(begin != end && !valid()) {
+            if(it != view->end() && !valid()) {
                 ++(*this);
                 ++(*this);
             }
             }
         }
         }
 
 
         bool valid() const {
         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:
     public:
@@ -106,12 +107,13 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         using value_type = typename underlying_iterator_type::value_type;
         using value_type = typename underlying_iterator_type::value_type;
         using pointer = typename underlying_iterator_type::pointer;
         using pointer = typename underlying_iterator_type::pointer;
         using reference = typename underlying_iterator_type::reference;
         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() ENTT_NOEXCEPT = default;
 
 
         iterator & operator++() {
         iterator & operator++() {
-            return (++begin != end && !valid()) ? ++(*this) : *this;
+            while(++it != view->end() && !valid());
+            return *this;
         }
         }
 
 
         iterator operator++(int) {
         iterator operator++(int) {
@@ -119,8 +121,18 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
             return ++(*this), orig;
             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 {
         bool operator==(const iterator &other) const ENTT_NOEXCEPT {
-            return other.begin == begin;
+            return other.it == it;
         }
         }
 
 
         bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
         bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
@@ -128,7 +140,7 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
         }
 
 
         pointer operator->() const {
         pointer operator->() const {
-            return begin.operator->();
+            return it.operator->();
         }
         }
 
 
         reference operator*() const {
         reference operator*() const {
@@ -136,10 +148,10 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> {
         }
         }
 
 
     private:
     private:
-        underlying_iterator_type begin;
-        underlying_iterator_type end;
+        const sparse_set<Entity> *view;
         unchecked_type unchecked;
         unchecked_type unchecked;
         filter_type filter;
         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)
     // 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 {
     iterator_type begin() const {
         const auto *view = candidate();
         const auto *view = candidate();
         const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
         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 {
     iterator_type end() const {
         const auto *view = candidate();
         const auto *view = candidate();
         const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
         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 {
     iterator_type find(const entity_type entt) const {
         const auto *view = candidate();
         const auto *view = candidate();
         const filter_type ignore{std::get<pool_type<Exclude> *>(filter)...};
         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();
         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);
     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, 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_NE(view.begin(), view.end());
     ASSERT_EQ(view.size(), decltype(view.size()){1});
     ASSERT_EQ(view.size(), decltype(view.size()){1});
@@ -67,8 +67,14 @@ TEST(RuntimeView, Iterator) {
     ASSERT_EQ(end, view.end());
     ASSERT_EQ(end, view.end());
     ASSERT_NE(begin, 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) {
 TEST(RuntimeView, Contains) {

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

@@ -286,8 +286,14 @@ TEST(MultiComponentView, Iterator) {
     ASSERT_EQ(end, view.end());
     ASSERT_EQ(end, view.end());
     ASSERT_NE(begin, 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) {
 TEST(MultiComponentView, Contains) {