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

registry (close #521):
* remove visit
* added storage_proxy_iterator (input iterator category, modeled as a random access iterator)
* added ::storage() (const and non-const) to return an iterable object over all pools

Michele Caini 4 лет назад
Родитель
Сommit
3498dea486

+ 3 - 3
docs/md/entity.md

@@ -1208,11 +1208,11 @@ them by copy if needed:
 
 ```cpp
 // create a copy of an entity component by component
-registry.visit([entity, other](auto &&storage) {
-    if(storage.contains(entity)) {
+for(auto &&curr: registry.storage()) {
+    if(auto &storage = curr.second; storage.contains(entity)) {
         storage.emplace(other, storage.get(entity));
     }
-});
+}
 ```
 
 This is particularly useful to clone entities in an opaque way. In addition, the

+ 13 - 4
src/entt/entity/handle.hpp

@@ -266,17 +266,26 @@ struct basic_handle {
 
     /**
      * @brief Visits a handle and returns the pools for its components.
-     * @sa basic_registry::visit
+     *
+     * The signature of the function should be equivalent to the following:
+     *
+     * @code{.cpp}
+     * void(id_type, const basic_sparse_set<entity_type> &);
+     * @endcode
+     *
+     * Returned pools are those that contain the entity associated with the
+     * handle.
+     *
      * @tparam Func Type of the function object to invoke.
      * @param func A valid function object.
      */
     template<typename Func>
     void visit(Func &&func) const {
-        reg->visit([func = std::forward<Func>(func), this](auto &&storage) {
+        for(auto [id, storage]: reg->storage()) {
             if(storage.contains(entt)) {
-                func(storage);
+                func(id, storage);
             }
-        });
+        }
     }
 
 private:

+ 154 - 22
src/entt/entity/registry.hpp

@@ -14,6 +14,7 @@
 #include "../core/algorithm.hpp"
 #include "../core/any.hpp"
 #include "../core/fwd.hpp"
+#include "../core/iterator.hpp"
 #include "../core/type_info.hpp"
 #include "../core/type_traits.hpp"
 #include "../core/utility.hpp"
@@ -29,6 +30,141 @@
 
 namespace entt {
 
+/**
+ * @cond TURN_OFF_DOXYGEN
+ * Internal details not to be documented.
+ */
+
+namespace internal {
+
+template<typename It>
+class storage_proxy_iterator {
+    template<typename Other>
+    friend class storage_proxy_iterator;
+
+    using iterator_traits = std::iterator_traits<It>;
+    using first_type = typename iterator_traits::value_type::first_type;
+    using second_type = typename iterator_traits::value_type::second_type::element_type;
+
+public:
+    using value_type = std::pair<first_type, constness_as_t<second_type, std::remove_reference_t<typename iterator_traits::reference>> &>;
+    using pointer = input_iterator_pointer<value_type>;
+    using reference = value_type;
+    using difference_type = typename iterator_traits::difference_type;
+    using iterator_category = std::input_iterator_tag;
+
+    storage_proxy_iterator() ENTT_NOEXCEPT = default;
+
+    storage_proxy_iterator(const It iter) ENTT_NOEXCEPT
+        : it{iter} {}
+
+    template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+    storage_proxy_iterator(const storage_proxy_iterator<Other> &other)
+        : it{other.it} {}
+
+    storage_proxy_iterator &operator++() ENTT_NOEXCEPT {
+        return ++it, *this;
+    }
+
+    storage_proxy_iterator operator++(int) ENTT_NOEXCEPT {
+        storage_proxy_iterator orig = *this;
+        return ++(*this), orig;
+    }
+
+    storage_proxy_iterator &operator--() ENTT_NOEXCEPT {
+        return --it, *this;
+    }
+
+    storage_proxy_iterator operator--(int) ENTT_NOEXCEPT {
+        storage_proxy_iterator orig = *this;
+        return operator--(), orig;
+    }
+
+    storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
+        it += value;
+        return *this;
+    }
+
+    storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
+        storage_proxy_iterator copy = *this;
+        return (copy += value);
+    }
+
+    storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
+        return (*this += -value);
+    }
+
+    storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
+        return (*this + -value);
+    }
+
+    [[nodiscard]] reference operator[](const difference_type value) const {
+        return {it[value].first, *it[value].second};
+    }
+
+    [[nodiscard]] reference operator*() const {
+        return {it->first, *it->second};
+    }
+
+    [[nodiscard]] pointer operator->() const {
+        return operator*();
+    }
+
+    template<typename ILhs, typename IRhs>
+    friend auto operator-(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+    template<typename ILhs, typename IRhs>
+    friend bool operator==(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+    template<typename ILhs, typename IRhs>
+    friend bool operator<(const storage_proxy_iterator<ILhs> &, const storage_proxy_iterator<IRhs> &) ENTT_NOEXCEPT;
+
+private:
+    It it;
+};
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] auto operator-(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+    return lhs.it - rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator==(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+    return lhs.it == rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator!=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+    return !(lhs == rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+    return lhs.it < rhs.it;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+    return rhs < lhs;
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator<=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+    return !(lhs > rhs);
+}
+
+template<typename ILhs, typename IRhs>
+[[nodiscard]] bool operator>=(const storage_proxy_iterator<ILhs> &lhs, const storage_proxy_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
+    return !(lhs < rhs);
+}
+
+} // namespace internal
+
+/**
+ * Internal details not to be documented.
+ * @endcond
+ */
+
 /**
  * @brief Fast and reliable entity-component system.
  * @tparam Entity A valid entity type (see entt_traits for more details).
@@ -181,6 +317,23 @@ public:
         return *this;
     }
 
+    /**
+     * @brief Returns an iterable object to use to _visit_ a registry.
+     *
+     * The iterable object returns a pair that contains the name and a reference
+     * to the current storage.
+     *
+     * @return An iterable object to use to _visit_ the registry.
+     */
+    [[nodiscard]] auto storage() ENTT_NOEXCEPT {
+        return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}};
+    }
+
+    /*! @copydoc storage */
+    [[nodiscard]] auto storage() const ENTT_NOEXCEPT {
+        return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_iterator{pools.cend()}};
+    }
+
     /**
      * @brief Returns the storage for a given component type.
      * @tparam Component Type of component of which to return the storage.
@@ -583,7 +736,7 @@ public:
     /**
      * @brief Patches the given component for an entity.
      *
-     * The signature of the functions should be equivalent to the following:
+     * The signature of the function should be equivalent to the following:
      *
      * @code{.cpp}
      * void(Component &);
@@ -1266,27 +1419,6 @@ public:
         assure<To>().respect(assure<From>());
     }
 
-    /**
-     * @brief Visits a registry and returns the pools for its components.
-     *
-     * The signature of the function should be equivalent to the following:
-     *
-     * @code{.cpp}
-     * void(const basic_sparse_set<entity_type> &);
-     * @endcode
-     *
-     * Returned pools are those of the components managed by the registry.
-     *
-     * @tparam Func Type of the function object to invoke.
-     * @param func A valid function object.
-     */
-    template<typename Func>
-    void visit(Func func) const {
-        for(auto &&curr: pools) {
-            func(*curr.second);
-        }
-    }
-
     /**
      * @brief Binds an object to the context of the registry.
      *

+ 4 - 1
test/entt/entity/handle.cpp

@@ -169,7 +169,10 @@ TEST(BasicHandle, Component) {
     ASSERT_TRUE(registry.storage<double>().empty());
     ASSERT_EQ(0u, (handle.remove<char, double>()));
 
-    handle.visit([](const auto &pool) { ASSERT_EQ(entt::type_id<int>(), pool.type()); });
+    handle.visit([&handle](const auto id, const auto &pool) {
+        ASSERT_EQ(id, entt::type_id<int>().hash());
+        ASSERT_TRUE(pool.contains(handle.entity()));
+    });
 
     ASSERT_TRUE((handle.any_of<int, char, double>()));
     ASSERT_FALSE((handle.all_of<int, char, double>()));

+ 82 - 20
test/entt/entity/registry.cpp

@@ -1854,26 +1854,6 @@ TEST(Registry, AssignEntities) {
     ASSERT_EQ(traits_type::to_entity(other.create()), traits_type::to_integral(entities[1]));
 }
 
-TEST(Registry, Visit) {
-    entt::registry registry;
-    const auto entity = registry.create();
-    const auto other = registry.create();
-
-    registry.emplace<int>(entity);
-    registry.emplace<double>(other);
-    registry.emplace<char>(entity);
-
-    bool hasType[3]{};
-
-    registry.visit([&hasType](const auto &pool) {
-        hasType[0] = hasType[0] || (pool.type() == entt::type_id<int>());
-        hasType[1] = hasType[1] || (pool.type() == entt::type_id<double>());
-        hasType[2] = hasType[2] || (pool.type() == entt::type_id<char>());
-    });
-
-    ASSERT_TRUE(hasType[0] && hasType[1] && hasType[2]);
-}
-
 TEST(Registry, ScramblingPoolsIsAllowed) {
     entt::registry registry;
     registry.on_destroy<int>().connect<&listener::sort<int>>();
@@ -1926,3 +1906,85 @@ TEST(Registry, RuntimePools) {
     ASSERT_FALSE(storage.contains(entity));
     ASSERT_FALSE(registry.any_of<empty_type>(entity));
 }
+
+TEST(Registry, StorageProxy) {
+    using namespace entt::literals;
+
+    entt::registry registry;
+    const auto entity = registry.create();
+    auto &storage = registry.storage<int>("int"_hs);
+    storage.emplace(entity);
+
+    for(auto [id, pool]: registry.storage()) {
+        static_assert(std::is_same_v<decltype(pool), entt::sparse_set &>);
+        static_assert(std::is_same_v<decltype(id), const entt::id_type>);
+
+        ASSERT_TRUE(pool.contains(entity));
+        ASSERT_EQ(std::addressof(storage), std::addressof(pool));
+        ASSERT_EQ(id, "int"_hs);
+    }
+
+    for(auto &&curr: std::as_const(registry).storage()) {
+        static_assert(std::is_same_v<decltype(curr.second), const entt::sparse_set &>);
+        static_assert(std::is_same_v<decltype(curr.first), const entt::id_type>);
+
+        ASSERT_TRUE(curr.second.contains(entity));
+        ASSERT_EQ(std::addressof(storage), std::addressof(curr.second));
+        ASSERT_EQ(curr.first, "int"_hs);
+    }
+}
+
+TEST(Registry, StorageProxyIterator) {
+    entt::registry registry;
+    const auto entity = registry.create();
+    registry.emplace<int>(entity);
+
+    auto test = [entity](auto iterable) {
+        auto end{iterable.begin()};
+        decltype(end) begin{};
+        begin = iterable.end();
+        std::swap(begin, end);
+
+        ASSERT_EQ(begin, iterable.cbegin());
+        ASSERT_EQ(end, iterable.cend());
+        ASSERT_NE(begin, end);
+
+        ASSERT_EQ(begin++, iterable.begin());
+        ASSERT_EQ(begin--, iterable.end());
+
+        ASSERT_EQ(begin + 1, iterable.end());
+        ASSERT_EQ(end - 1, iterable.begin());
+
+        ASSERT_EQ(++begin, iterable.end());
+        ASSERT_EQ(--begin, iterable.begin());
+
+        ASSERT_EQ(begin += 1, iterable.end());
+        ASSERT_EQ(begin -= 1, iterable.begin());
+
+        ASSERT_EQ(begin + (end - begin), iterable.end());
+        ASSERT_EQ(begin - (begin - end), iterable.end());
+
+        ASSERT_EQ(end - (end - begin), iterable.begin());
+        ASSERT_EQ(end + (begin - end), iterable.begin());
+
+        ASSERT_EQ(begin[0u].first, iterable.begin()->first);
+        ASSERT_EQ(std::addressof(begin[0u].second), std::addressof((*iterable.begin()).second));
+
+        ASSERT_LT(begin, end);
+        ASSERT_LE(begin, iterable.begin());
+
+        ASSERT_GT(end, begin);
+        ASSERT_GE(end, iterable.end());
+
+        ASSERT_EQ(begin[0u].first, entt::type_id<int>().hash());
+        ASSERT_TRUE(begin[0u].second.contains(entity));
+    };
+
+    test(registry.storage());
+    test(std::as_const(registry).storage());
+
+    decltype(std::as_const(registry).storage().begin()) cit = registry.storage().begin();
+
+    ASSERT_EQ(cit, registry.storage().begin());
+    ASSERT_NE(cit, std::as_const(registry).storage().end());
+}

+ 8 - 8
test/example/entity_copy.cpp

@@ -22,17 +22,17 @@ TEST(Example, EntityCopy) {
     ASSERT_FALSE((registry.any_of<int, char, double>(dst)));
     ASSERT_FALSE(custom.contains(dst));
 
-    registry.visit([src, dst](auto &&storage) {
-        // discard chars because why not, this is just an example after all
-        if(storage.type() != entt::type_id<char>() && storage.contains(src)) {
+    for(auto [id, storage]: registry.storage()) {
+        // discard the custom storage because why not, this is just an example after all
+        if(id != "custom"_hs && storage.contains(src)) {
             storage.emplace(dst, storage.get(src));
         }
-    });
+    }
 
-    ASSERT_TRUE((registry.all_of<int>(dst)));
-    ASSERT_FALSE((registry.any_of<char, double>(dst)));
-    ASSERT_TRUE(custom.contains(dst));
+    ASSERT_TRUE((registry.all_of<int, char>(dst)));
+    ASSERT_FALSE((registry.all_of<double>(dst)));
+    ASSERT_FALSE(custom.contains(dst));
 
     ASSERT_EQ(registry.get<int>(dst), 42);
-    ASSERT_EQ(custom.get(dst), 1.);
+    ASSERT_EQ(registry.get<char>(dst), 'c');
 }