From 960bbbde293759a61ba50b654fe3ce3549f2d443 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Mon, 17 Dec 2018 21:39:30 +0100 Subject: [PATCH] more on registry::clone (close #161) --- src/entt/entity/registry.hpp | 69 ++++++++++++++++++++++++--------- src/entt/entity/sparse_set.hpp | 68 +++++++++++++++++++------------- test/entt/entity/registry.cpp | 61 ++++++++++++++++++++++++++--- test/entt/entity/sparse_set.cpp | 12 +----- 4 files changed, 149 insertions(+), 61 deletions(-) diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index b92768faf..9f2682bb5 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -53,6 +53,15 @@ class registry { : reg{reg} {} + component_pool(const component_pool &other) + : sparse_set{other}, ctor{}, dtor{}, reg{other.reg} + {} + + component_pool & operator=(const component_pool &other) { + sparse_set::operator=(other); + reg = other.reg; + } + template Component & construct(const Entity entity, Args &&... args) { auto &component = sparse_set::construct(entity, std::forward(args)...); @@ -65,6 +74,14 @@ class registry { sparse_set::destroy(entity); } + std::unique_ptr> clone() const override { + if constexpr(std::is_copy_constructible_v) { + return std::make_unique(*this); + } else { + return nullptr; + } + } + typename component_signal_type::sink_type construction() ENTT_NOEXCEPT { return ctor.sink(); } @@ -1279,37 +1296,51 @@ public: * @brief Clones the given components and all the entity identifiers. * * The components must be copiable for obvious reasons. The entities - * maintain their versions once copied. + * maintain their versions once copied.
+ * If no components are provided, the registry will try to clone all the + * existing pools. + * + * @warning + * Attempting to clone components that aren't copyable can result in + * unexpected behaviors.
+ * A static assertion will abort the compilation when one or more components + * are provided at the call site. Otherwise, an assertion will abort the + * execution at runtime in debug mode in case one or more pools cannot be + * cloned. * * @note * There isn't an efficient way to know if all the entities are assigned at * least one component once copied. Therefore, there may be orphans. It is * up to the caller to clean up the registry if necessary. * - * @warning - * This function requires that the registry be empty. In case it isn't, all - * the data will be automatically deleted beforehand. - * * @tparam Component Types of components to clone. - * @param reg A valid reference to a source registry. + * @return A fresh copy of the registry. */ template - void clone(const registry ®, type_list = {}) { - *this = {}; + registry clone() const { + registry other; + other.pools.resize(pools.size()); - (assure(), ...); - (reserve(reg.size()), ...); + if(sizeof...(Component)) { + static_assert(std::conjunction_v...>); + ((other.pools[component_family::type] = managed() ? pool().clone() : nullptr), ...); + } else { + for(auto pos = pools.size(); pos; --pos) { + auto &cpool = pools[pos-1]; - (std::copy(reg.raw(), reg.raw() + reg.size(), pool().raw()), ...); - // double lambda function used to work around a bug of gcc7 - (std::for_each(reg.data(), reg.data() + reg.size(), ([](auto *cpool) { - return [cpool](const auto entity) { cpool->construct(entity); }; - })(pools[component_family::type].get())), ...); + if(cpool) { + other.pools[pos-1] = cpool->clone(); + assert(other.pools[pos-1]); + } + }; + } - next = reg.next; - available = reg.available; - entities.resize(reg.entities.size()); - std::copy(reg.entities.cbegin(), reg.entities.cend(), entities.begin()); + other.next = next; + other.available = available; + other.entities.resize(entities.size()); + std::copy(entities.cbegin(), entities.cend(), other.entities.begin()); + + return other; } /** diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index ca5c29fce..a556a061e 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +81,10 @@ class sparse_set { iterator() ENTT_NOEXCEPT = default; iterator(const iterator &) ENTT_NOEXCEPT = default; + iterator(iterator &&) ENTT_NOEXCEPT = default; + iterator & operator=(const iterator &) ENTT_NOEXCEPT = default; + iterator & operator=(iterator &&) ENTT_NOEXCEPT = default; iterator & operator++() ENTT_NOEXCEPT { return --index, *this; @@ -172,22 +176,9 @@ public: /*! @brief Input iterator type. */ using iterator_type = iterator; - /*! @brief Default constructor. */ - sparse_set() ENTT_NOEXCEPT = default; - /*! @brief Default destructor. */ virtual ~sparse_set() ENTT_NOEXCEPT = default; - /*! @brief Copying a sparse set isn't allowed. */ - sparse_set(const sparse_set &) = delete; - /*! @brief Default move constructor. */ - sparse_set(sparse_set &&) = default; - - /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */ - sparse_set & operator=(const sparse_set &) = delete; - /*! @brief Default move assignment operator. @return This sparse set. */ - sparse_set & operator=(sparse_set &&) = default; - /** * @brief Increases the capacity of a sparse set. * @@ -477,6 +468,23 @@ public: direct.clear(); } + /** + * @brief Clones and returns a sparse set. + * + * The basic implementation of a sparse set is always copyable. Therefore, + * the returned instance is always valid. + * + * @return A fresh copy of the given sparse set. + */ + virtual std::unique_ptr clone() const { + auto other = std::make_unique(); + other->reverse.resize(reverse.size()); + other->direct.resize(direct.size()); + std::copy(reverse.cbegin(), reverse.cend(), other->reverse.begin()); + std::copy(direct.cbegin(), direct.cend(), other->direct.begin()); + return other; + } + private: std::vector reverse; std::vector direct; @@ -531,7 +539,10 @@ class sparse_set: public sparse_set { iterator() ENTT_NOEXCEPT = default; iterator(const iterator &) ENTT_NOEXCEPT = default; + iterator(iterator &&) ENTT_NOEXCEPT = default; + iterator & operator=(const iterator &) ENTT_NOEXCEPT = default; + iterator & operator=(iterator &&) ENTT_NOEXCEPT = default; iterator & operator++() ENTT_NOEXCEPT { return --index, *this; @@ -627,19 +638,6 @@ public: /*! @brief Constant input iterator type. */ using const_iterator_type = iterator; - /*! @brief Default constructor. */ - sparse_set() ENTT_NOEXCEPT = default; - - /*! @brief Copying a sparse set isn't allowed. */ - sparse_set(const sparse_set &) = delete; - /*! @brief Default move constructor. */ - sparse_set(sparse_set &&) = default; - - /*! @brief Copying a sparse set isn't allowed. @return This sparse set. */ - sparse_set & operator=(const sparse_set &) = delete; - /*! @brief Default move assignment operator. @return This sparse set. */ - sparse_set & operator=(sparse_set &&) = default; - /** * @brief Increases the capacity of a sparse set. * @@ -939,6 +937,24 @@ public: instances.clear(); } + /** + * @brief Clones and returns a sparse set if possible. + * + * The extended implementation of a sparse set is copyable only if its + * object type is copyable. Because of that, this member functions isn't + * guaranteed to return always a valid pointer. + * + * @return A fresh copy of the given sparse set if its object type is + * copyable, an empty unique pointer otherwise. + */ + std::unique_ptr> clone() const override { + if constexpr(std::is_copy_constructible_v) { + return std::make_unique(*this); + } else { + return nullptr; + } + } + private: std::vector instances; }; diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index 2cf51378d..cbfb229a4 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -781,10 +782,6 @@ TEST(Registry, Clone) { entt::registry<> registry; entt::registry<> other; - const auto entity = other.create(); - other.assign(entity, 42); - other.assign(entity, 'c'); - registry.destroy(registry.create()); const auto e0 = registry.create(); @@ -801,9 +798,12 @@ TEST(Registry, Clone) { registry.assign(e2, '2'); registry.destroy(e1); - other.clone(registry); - ASSERT_FALSE(other.valid(entity)); + other = registry.clone(); + + ASSERT_EQ(other.size(), registry.size()); + ASSERT_EQ(other.alive(), registry.alive()); + ASSERT_TRUE(other.valid(e0)); ASSERT_FALSE(other.valid(e1)); ASSERT_TRUE(other.valid(e2)); @@ -815,4 +815,53 @@ TEST(Registry, Clone) { ASSERT_EQ(other.get(e0), 0); ASSERT_EQ(other.get(e2), 2); ASSERT_EQ(other.get(e2), '2'); + + other = registry.clone(); + + ASSERT_EQ(other.size(), registry.size()); + ASSERT_EQ(other.alive(), registry.alive()); + + ASSERT_TRUE(other.valid(e0)); + ASSERT_FALSE(other.valid(e1)); + ASSERT_TRUE(other.valid(e2)); + + ASSERT_TRUE((other.has(e0))); + ASSERT_TRUE((other.has(e2))); + + ASSERT_EQ(other.get(e0), 0); + ASSERT_EQ(other.get(e0), 0.); + ASSERT_EQ(other.get(e2), 2); + ASSERT_EQ(other.get(e2), '2'); + + other = registry.clone(); + + ASSERT_EQ(other.size(), registry.size()); + ASSERT_EQ(other.alive(), registry.alive()); + + ASSERT_TRUE(other.valid(e0)); + ASSERT_FALSE(other.valid(e1)); + ASSERT_TRUE(other.valid(e2)); + + ASSERT_FALSE((other.has(e0))); + ASSERT_FALSE((other.has(e0))); + ASSERT_FALSE((other.has(e2))); + ASSERT_TRUE((other.has(e2))); + + ASSERT_TRUE(other.orphan(e0)); + ASSERT_EQ(other.get(e2), '2'); + + const auto entity = registry.create(); + listener listener; + + ASSERT_NE(e1, entity); + ASSERT_EQ(registry.entity(e1), registry.entity(entity)); + + registry.construction().connect<&listener::incr>(&listener); + registry.destruction().connect<&listener::decr>(&listener); + registry.assign(entity, 'e'); + registry.assign(e0, '0'); + registry.remove(e0); + + ASSERT_EQ(listener.counter, 1); + ASSERT_EQ(listener.last, e0); } diff --git a/test/entt/entity/sparse_set.cpp b/test/entt/entity/sparse_set.cpp index 6205453f7..836b1a333 100644 --- a/test/entt/entity/sparse_set.cpp +++ b/test/entt/entity/sparse_set.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -839,16 +840,7 @@ TEST(SparseSetWithType, ReferencesGuaranteed) { } TEST(SparseSetWithType, MoveOnlyComponent) { - struct move_only_component { - move_only_component() = default; - ~move_only_component() = default; - move_only_component(const move_only_component &) = delete; - move_only_component(move_only_component &&) = default; - move_only_component & operator=(const move_only_component &) = delete; - move_only_component & operator=(move_only_component &&) = default; - }; - // the purpose is to ensure that move only components are always accepted - entt::sparse_set set; + entt::sparse_set> set; (void)set; }