Explorar o código

storage/registry: empty types are no longer a thing

Michele Caini %!s(int64=6) %!d(string=hai) anos
pai
achega
c7de058e1d
Modificáronse 5 ficheiros con 72 adicións e 298 borrados
  1. 1 0
      TODO
  2. 59 41
      src/entt/entity/registry.hpp
  3. 0 177
      src/entt/entity/storage.hpp
  4. 12 2
      test/entt/entity/registry.cpp
  5. 0 78
      test/entt/entity/storage.cpp

+ 1 - 0
TODO

@@ -17,6 +17,7 @@
   - get -> all, exclude -> none
 
 Next:
+* can I remove pool_handler from the registry?
 * replace observer class with observer functions
 * get(cmp, entity) -> void *, set(cmp, entity, void *)
 * ENTT_ENABLE_ETO -> ENTT_IS_EMPTY (ignore default constructible)

+ 59 - 41
src/entt/entity/registry.hpp

@@ -65,7 +65,10 @@ class basic_registry {
         decltype(auto) emplace(basic_registry &owner, const Entity entt, Args &&... args) {
             this->construct(entt, std::forward<Args>(args)...);
             construction.publish(owner, entt);
-            return this->get(entt);
+
+            if constexpr(!ENTT_ENABLE_ETO(Component)) {
+                return this->get(entt);
+            }
         }
 
         template<typename It, typename Value>
@@ -99,7 +102,18 @@ class basic_registry {
         decltype(auto) patch(basic_registry &owner, const Entity entt, Func &&... func) {
             (std::forward<Func>(func)(this->get(entt)), ...);
             update.publish(owner, entt);
-            return this->get(entt);
+
+            if constexpr(!ENTT_ENABLE_ETO(Component)) {
+                return this->get(entt);
+            }
+        }
+
+        decltype(auto) replace(basic_registry &owner, const Entity entt, [[maybe_unused]] Component &&component) {
+            if constexpr(ENTT_ENABLE_ETO(Component)) {
+                return patch(owner, entt);
+            } else {
+                return patch(owner, entt, [&component](auto &&curr) { curr = std::move(component); });
+            }
         }
 
     private:
@@ -512,6 +526,37 @@ public:
         std::generate(first, last, [this]() { return create(); });
     }
 
+    /**
+     * @brief Assigns entities to an empty registry.
+     *
+     * This function is intended for use in conjunction with `raw`.<br/>
+     * Don't try to inject ranges of randomly generated entities because there
+     * is no guarantee that the registry will continue to function properly in
+     * this case.
+     *
+     * @warning
+     * An assertion will abort the execution at runtime in debug mode if all
+     * pools aren't empty. Groups and context variables are ignored.
+     *
+     * @tparam It Type of input iterator.
+     * @param first An iterator to the first element of the range of entities.
+     * @param last An iterator past the last element of the range of entities.
+     */
+    template<typename It>
+    void assign(It first, It last) {
+        ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { return cpool.pool->empty(); }));
+        entities.assign(first, last);
+        destroyed = null;
+
+        for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) {
+            if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) {
+                const auto version = to_integral(entities[pos]) & (traits_type::version_mask << traits_type::entity_shift);
+                entities[pos] = entity_type{to_integral(destroyed) | version};
+                destroyed = entity_type(pos);
+            }
+        }
+    }
+
     /**
      * @brief Destroys an entity and lets the registry recycle the identifier.
      *
@@ -650,37 +695,6 @@ public:
         return insert<Component>(std::move(first), std::move(last), std::move(value));
     }
 
-    /**
-     * @brief Assigns entities to an empty registry.
-     *
-     * This function is intended for use in conjunction with `raw`.<br/>
-     * Don't try to inject ranges of randomly generated entities because there
-     * is no guarantee that the registry will continue to function properly in
-     * this case.
-     *
-     * @warning
-     * An assertion will abort the execution at runtime in debug mode if all
-     * pools aren't empty. Groups and context variables are ignored.
-     *
-     * @tparam It Type of input iterator.
-     * @param first An iterator to the first element of the range of entities.
-     * @param last An iterator past the last element of the range of entities.
-     */
-    template<typename It>
-    void assign(It first, It last) {
-        ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { return cpool.pool->empty(); }));
-        entities.assign(first, last);
-        destroyed = null;
-
-        for(std::size_t pos{}, end = entities.size(); pos < end; ++pos) {
-            if((to_integral(entities[pos]) & traits_type::entity_mask) != pos) {
-                const auto version = to_integral(entities[pos]) & (traits_type::version_mask << traits_type::entity_shift);
-                entities[pos] = entity_type{to_integral(destroyed) | version};
-                destroyed = entity_type(pos);
-            }
-        }
-    }
-
     /**
      * @brief Assigns or replaces the given component for an entity.
      *
@@ -709,7 +723,7 @@ public:
         auto &cpool = assure<Component>();
 
         return cpool.has(entity)
-                ? (cpool.patch(*this, entity, [&args...](auto &&component) { component = Component{std::forward<Args>(args)...}; }), cpool.get(entity))
+                ? cpool.replace(*this, entity, Component{std::forward<Args>(args)...})
                 : cpool.emplace(*this, entity, std::forward<Args>(args)...);
     }
 
@@ -746,7 +760,6 @@ public:
      * @return A reference to the patched component.
      */
     template<typename Component, typename... Func>
-    [[deprecated("use registry::patch instead")]]
     decltype(auto) patch(const entity_type entity, Func &&... func) {
         ENTT_ASSERT(valid(entity));
         return assure<Component>().patch(*this, entity, std::forward<Func>(func)...);
@@ -756,7 +769,7 @@ public:
     template<typename Component, typename... Func>
     [[deprecated("use registry::patch instead")]]
     auto replace(const entity_type entity, Func &&... func)
-    -> decltype((func(assure<Component>().get(entity)), ...), assure<Component>().get(entity)) {
+    -> decltype((func(std::declval<Component &>()), ...), patch<Component>(entity, std::forward<Func>(func)...)) {
         return patch<Component>(entity, std::forward<Func>(func)...);
     }
 
@@ -783,9 +796,7 @@ public:
     template<typename Component, typename... Args>
     auto replace(const entity_type entity, Args &&... args)
     -> decltype(std::enable_if_t<sizeof...(Args) != 0>(), Component{std::forward<Args>(args)...}, assure<Component>().get(entity)) {
-        return patch<Component>(entity, [args = std::forward_as_tuple(std::forward<Args>(args)...)](auto &&component) {
-            component = std::make_from_tuple<Component>(std::move(args));
-        });
+        return assure<Component>().replace(*this, entity, Component{std::forward<Args>(args)...});
     }
 
     /**
@@ -845,7 +856,12 @@ public:
     template<typename... Component>
     void remove_if_exists(const entity_type entity) {
         ENTT_ASSERT(valid(entity));
-        ([this, entity](auto &&cpool) { if(cpool.has(entity)) { cpool.remove(*this, entity); } }(assure<Component>()), ...);
+
+        ([this, entity](auto &&cpool) {
+            if(cpool.has(entity)) {
+                cpool.remove(*this, entity);
+            }
+        }(assure<Component>()), ...);
     }
 
     /**
@@ -1005,7 +1021,9 @@ public:
             // useless this-> used to suppress a warning with clang
             each([this](const auto entity) { this->destroy(entity); });
         } else {
-            ([this](auto &&cpool) { cpool.remove(*this, cpool.sparse_set<entity_type>::begin(), cpool.sparse_set<entity_type>::end()); }(assure<Component>()), ...);
+            ([this](auto &&cpool) {
+                cpool.remove(*this, cpool.sparse_set<entity_type>::begin(), cpool.sparse_set<entity_type>::end());
+            }(assure<Component>()), ...);
         }
     }
 

+ 0 - 177
src/entt/entity/storage.hpp

@@ -510,103 +510,6 @@ class storage<Entity, Type, std::enable_if_t<ENTT_ENABLE_ETO(Type)>>: public spa
     using traits_type = entt_traits<std::underlying_type_t<Entity>>;
     using underlying_type = sparse_set<Entity>;
 
-    class iterator final {
-        friend class storage<Entity, Type>;
-
-        using index_type = typename traits_type::difference_type;
-
-        iterator(const index_type idx) ENTT_NOEXCEPT
-            : index{idx}
-        {}
-
-    public:
-        using difference_type = index_type;
-        using value_type = Type;
-        using pointer = const value_type *;
-        using reference = value_type;
-        using iterator_category = std::input_iterator_tag;
-
-        iterator() ENTT_NOEXCEPT = default;
-
-        iterator & operator++() ENTT_NOEXCEPT {
-            return --index, *this;
-        }
-
-        iterator operator++(int) ENTT_NOEXCEPT {
-            iterator orig = *this;
-            return operator++(), orig;
-        }
-
-        iterator & operator--() ENTT_NOEXCEPT {
-            return ++index, *this;
-        }
-
-        iterator operator--(int) ENTT_NOEXCEPT {
-            iterator orig = *this;
-            return operator--(), orig;
-        }
-
-        iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
-            index -= value;
-            return *this;
-        }
-
-        iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
-            return iterator{index-value};
-        }
-
-        iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
-            return (*this += -value);
-        }
-
-        iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
-            return (*this + -value);
-        }
-
-        difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
-            return other.index - index;
-        }
-
-        reference operator[](const difference_type) const ENTT_NOEXCEPT {
-            return {};
-        }
-
-        bool operator==(const iterator &other) const ENTT_NOEXCEPT {
-            return other.index == index;
-        }
-
-        bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
-            return !(*this == other);
-        }
-
-        bool operator<(const iterator &other) const ENTT_NOEXCEPT {
-            return index > other.index;
-        }
-
-        bool operator>(const iterator &other) const ENTT_NOEXCEPT {
-            return index < other.index;
-        }
-
-        bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
-            return !(*this > other);
-        }
-
-        bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
-            return !(*this < other);
-        }
-
-        pointer operator->() const ENTT_NOEXCEPT {
-            return nullptr;
-        }
-
-        reference operator*() const ENTT_NOEXCEPT {
-            return {};
-        }
-
-    private:
-        index_type index;
-    };
-
 public:
     /*! @brief Type of the objects associated with the entities. */
     using object_type = Type;
@@ -614,74 +517,6 @@ public:
     using entity_type = Entity;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
-    /*! @brief Random access iterator type. */
-    using iterator_type = iterator;
-
-    /**
-     * @brief Returns an iterator to the beginning.
-     *
-     * The returned iterator points to the first instance of the given type. If
-     * the storage is empty, the returned iterator will be equal to `end()`.
-     *
-     * @note
-     * Input iterators stay true to the order imposed by a call to either `sort`
-     * or `respect`.
-     *
-     * @return An iterator to the first instance of the given type.
-     */
-    iterator_type cbegin() const ENTT_NOEXCEPT {
-        const typename traits_type::difference_type pos = underlying_type::size();
-        return iterator_type{pos};
-    }
-
-    /*! @copydoc cbegin */
-    iterator_type begin() const ENTT_NOEXCEPT {
-        return cbegin();
-    }
-
-    /**
-     * @brief Returns an iterator to the end.
-     *
-     * The returned iterator points to the element following the last instance
-     * of the given type. Attempting to dereference the returned iterator
-     * results in undefined behavior.
-     *
-     * @note
-     * Input iterators stay true to the order imposed by a call to either `sort`
-     * or `respect`.
-     *
-     * @return An iterator to the element following the last instance of the
-     * given type.
-     */
-    iterator_type cend() const ENTT_NOEXCEPT {
-        return iterator_type{};
-    }
-
-    /*! @copydoc cend */
-    iterator_type end() const ENTT_NOEXCEPT {
-        return cend();
-    }
-
-    /**
-     * @brief Returns the object associated with an entity.
-     *
-     * @note
-     * Empty types aren't explicitly instantiated. Therefore, this function
-     * always returns a temporary object.
-     *
-     * @warning
-     * Attempting to use an entity that doesn't belong to the storage results in
-     * undefined behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode if the
-     * storage doesn't contain the given entity.
-     *
-     * @param entt A valid entity identifier.
-     * @return The object associated with the entity.
-     */
-    object_type get([[maybe_unused]] const entity_type entt) const {
-        ENTT_ASSERT(underlying_type::has(entt));
-        return {};
-    }
 
     /**
      * @brief Assigns an entity to a storage and constructs its object.
@@ -737,18 +572,6 @@ public:
     construct(It first, It last, const object_type &value = {}) {
         insert(std::move(first), std::move(last), value);
     }
-
-    /*! @copydoc storage::sort */
-    template<typename Compare, typename Sort = std_sort, typename... Args>
-    void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
-        ENTT_ASSERT(!(last < first));
-        ENTT_ASSERT(!(last > end()));
-
-        const auto from = underlying_type::begin() + std::distance(begin(), first);
-        const auto to = from + std::distance(first, last);
-
-        underlying_type::sort(from, to, std::move(compare), std::move(algo), std::forward<Args>(args)...);
-    }
 };
 
 

+ 12 - 2
test/entt/entity/registry.cpp

@@ -260,7 +260,17 @@ TEST(Registry, Functionalities) {
     ASSERT_EQ(registry.capacity<char>(), entt::registry::size_type{});
 }
 
-TEST(Registry, AssignOrReplaceAggregates) {
+TEST(Registry, ReplaceAggregate) {
+    entt::registry registry;
+    const auto entity = registry.create();
+
+    registry.assign<aggregate>(entity, 0);
+    auto &instance = registry.replace<aggregate>(entity, 42);
+
+    ASSERT_EQ(instance.value, 42);
+}
+
+TEST(Registry, AssignOrReplaceAggregate) {
     entt::registry registry;
     const auto entity = registry.create();
     auto &instance = registry.assign_or_replace<aggregate>(entity, 42);
@@ -1310,7 +1320,7 @@ TEST(Registry, Constness) {
     entt::registry registry;
 
     ASSERT_TRUE((std::is_same_v<decltype(registry.assign<int>({})), int &>));
-    ASSERT_TRUE((std::is_same_v<decltype(registry.assign<empty_type>({})), empty_type>));
+    ASSERT_TRUE((std::is_same_v<decltype(registry.assign<empty_type>({})), void>));
 
     ASSERT_TRUE((std::is_same_v<decltype(registry.get<int>({})), int &>));
     ASSERT_TRUE((std::is_same_v<decltype(registry.get<int, char>({})), std::tuple<int &, char &>>));

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

@@ -82,16 +82,9 @@ TEST(Storage, Functionalities) {
 
 TEST(Storage, EmptyType) {
     entt::storage<entt::entity, empty_type> pool;
-
-    pool.construct(entt::entity{42});
     pool.construct(entt::entity{99});
 
-    ASSERT_TRUE(pool.has(entt::entity{42}));
     ASSERT_TRUE(pool.has(entt::entity{99}));
-
-    auto &&component = pool.get(entt::entity{42});
-
-    ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
 }
 
 TEST(Storage, BatchAdd) {
@@ -125,10 +118,6 @@ TEST(Storage, BatchAddEmptyType) {
 
     ASSERT_FALSE(pool.empty());
     ASSERT_EQ(pool.size(), 2u);
-
-    auto &&component = pool.get(entities[0]);
-
-    ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
 }
 
 TEST(Storage, AggregatesMustWork) {
@@ -229,52 +218,6 @@ TEST(Storage, ConstIterator) {
     ASSERT_GE(cend, pool.cend());
 }
 
-TEST(Storage, IteratorEmptyType) {
-    using iterator_type = typename entt::storage<entt::entity, empty_type>::iterator_type;
-    entt::storage<entt::entity, empty_type> pool;
-    pool.construct(entt::entity{3});
-
-    iterator_type end{pool.begin()};
-    iterator_type begin{};
-    begin = pool.end();
-    std::swap(begin, end);
-
-    ASSERT_EQ(begin, pool.begin());
-    ASSERT_EQ(end, pool.end());
-    ASSERT_NE(begin, end);
-
-    ASSERT_EQ(begin++, pool.begin());
-    ASSERT_EQ(begin--, pool.end());
-
-    ASSERT_EQ(begin+1, pool.end());
-    ASSERT_EQ(end-1, pool.begin());
-
-    ASSERT_EQ(++begin, pool.end());
-    ASSERT_EQ(--begin, pool.begin());
-
-    ASSERT_EQ(begin += 1, pool.end());
-    ASSERT_EQ(begin -= 1, pool.begin());
-
-    ASSERT_EQ(begin + (end - begin), pool.end());
-    ASSERT_EQ(begin - (begin - end), pool.end());
-
-    ASSERT_EQ(end - (end - begin), pool.begin());
-    ASSERT_EQ(end + (begin - end), pool.begin());
-
-    ASSERT_EQ(pool.begin().operator->(), nullptr);
-
-    ASSERT_LT(begin, end);
-    ASSERT_LE(begin, pool.begin());
-
-    ASSERT_GT(end, begin);
-    ASSERT_GE(end, pool.end());
-
-    pool.construct(entt::entity{33});
-    auto &&component = *begin;
-
-    ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
-}
-
 TEST(Storage, Raw) {
     entt::storage<entt::entity, int> pool;
 
@@ -677,27 +620,6 @@ TEST(Storage, RespectUnordered) {
     ASSERT_EQ(*(rhs.data() + 5u), entt::entity{5});
 }
 
-TEST(Storage, RespectOverlapEmptyType) {
-    entt::storage<entt::entity, empty_type> lhs;
-    entt::storage<entt::entity, empty_type> rhs;
-
-    lhs.construct(entt::entity{3});
-    lhs.construct(entt::entity{12});
-    lhs.construct(entt::entity{42});
-
-    rhs.construct(entt::entity{12});
-
-    ASSERT_EQ(lhs.index(entt::entity{3}), 0u);
-    ASSERT_EQ(lhs.index(entt::entity{12}), 1u);
-    ASSERT_EQ(lhs.index(entt::entity{42}), 2u);
-
-    lhs.respect(rhs);
-
-    ASSERT_EQ(std::as_const(lhs).index(entt::entity{3}), 0u);
-    ASSERT_EQ(std::as_const(lhs).index(entt::entity{12}), 2u);
-    ASSERT_EQ(std::as_const(lhs).index(entt::entity{42}), 1u);
-}
-
 TEST(Storage, CanModifyDuringIteration) {
     entt::storage<entt::entity, int> pool;
     pool.construct(entt::entity{0}, 42);