more on registry::clone (close #161)

This commit is contained in:
Michele Caini
2018-12-17 21:39:30 +01:00
parent eb79b79c35
commit 960bbbde29
4 changed files with 149 additions and 61 deletions

View File

@@ -53,6 +53,15 @@ class registry {
: reg{reg}
{}
component_pool(const component_pool &other)
: sparse_set<Entity, Component>{other}, ctor{}, dtor{}, reg{other.reg}
{}
component_pool & operator=(const component_pool &other) {
sparse_set<Entity, Component>::operator=(other);
reg = other.reg;
}
template<typename... Args>
Component & construct(const Entity entity, Args &&... args) {
auto &component = sparse_set<Entity, Component>::construct(entity, std::forward<Args>(args)...);
@@ -65,6 +74,14 @@ class registry {
sparse_set<Entity, Component>::destroy(entity);
}
std::unique_ptr<sparse_set<Entity>> clone() const override {
if constexpr(std::is_copy_constructible_v<Component>) {
return std::make_unique<component_pool>(*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.<br/>
* 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.<br/>
* 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<typename... Component>
void clone(const registry &reg, type_list<Component...> = {}) {
*this = {};
registry clone() const {
registry other;
other.pools.resize(pools.size());
(assure<Component>(), ...);
(reserve<Component>(reg.size<Component>()), ...);
if(sizeof...(Component)) {
static_assert(std::conjunction_v<std::is_copy_constructible<Component>...>);
((other.pools[component_family::type<Component>] = managed<Component>() ? pool<Component>().clone() : nullptr), ...);
} else {
for(auto pos = pools.size(); pos; --pos) {
auto &cpool = pools[pos-1];
(std::copy(reg.raw<Component>(), reg.raw<Component>() + reg.size<Component>(), pool<Component>().raw()), ...);
// double lambda function used to work around a bug of gcc7
(std::for_each(reg.data<Component>(), reg.data<Component>() + reg.size<Component>(), ([](auto *cpool) {
return [cpool](const auto entity) { cpool->construct(entity); };
})(pools[component_family::type<Component>].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;
}
/**

View File

@@ -7,6 +7,7 @@
#include <numeric>
#include <utility>
#include <vector>
#include <memory>
#include <cstddef>
#include <cassert>
#include <type_traits>
@@ -80,7 +81,10 @@ class sparse_set<Entity> {
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<sparse_set> clone() const {
auto other = std::make_unique<sparse_set>();
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<entity_type> reverse;
std::vector<entity_type> direct;
@@ -531,7 +539,10 @@ class sparse_set<Entity, Type>: public sparse_set<Entity> {
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<true>;
/*! @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<sparse_set<Entity>> clone() const override {
if constexpr(std::is_copy_constructible_v<Type>) {
return std::make_unique<sparse_set>(*this);
} else {
return nullptr;
}
}
private:
std::vector<object_type> instances;
};

View File

@@ -2,6 +2,7 @@
#include <unordered_set>
#include <functional>
#include <iterator>
#include <memory>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/entity/entt_traits.hpp>
@@ -781,10 +782,6 @@ TEST(Registry, Clone) {
entt::registry<> registry;
entt::registry<> other;
const auto entity = other.create();
other.assign<int>(entity, 42);
other.assign<char>(entity, 'c');
registry.destroy(registry.create());
const auto e0 = registry.create();
@@ -801,9 +798,12 @@ TEST(Registry, Clone) {
registry.assign<char>(e2, '2');
registry.destroy(e1);
other.clone<int, char>(registry);
ASSERT_FALSE(other.valid(entity));
other = registry.clone<int, char>();
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<int>(e0), 0);
ASSERT_EQ(other.get<int>(e2), 2);
ASSERT_EQ(other.get<char>(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<int, double>(e0)));
ASSERT_TRUE((other.has<int, char>(e2)));
ASSERT_EQ(other.get<int>(e0), 0);
ASSERT_EQ(other.get<double>(e0), 0.);
ASSERT_EQ(other.get<int>(e2), 2);
ASSERT_EQ(other.get<char>(e2), '2');
other = registry.clone<char>();
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<int>(e0)));
ASSERT_FALSE((other.has<double>(e0)));
ASSERT_FALSE((other.has<int>(e2)));
ASSERT_TRUE((other.has<char>(e2)));
ASSERT_TRUE(other.orphan(e0));
ASSERT_EQ(other.get<char>(e2), '2');
const auto entity = registry.create();
listener listener;
ASSERT_NE(e1, entity);
ASSERT_EQ(registry.entity(e1), registry.entity(entity));
registry.construction<char>().connect<&listener::incr<char>>(&listener);
registry.destruction<char>().connect<&listener::decr<char>>(&listener);
registry.assign<char>(entity, 'e');
registry.assign<char>(e0, '0');
registry.remove<char>(e0);
ASSERT_EQ(listener.counter, 1);
ASSERT_EQ(listener.last, e0);
}

View File

@@ -1,3 +1,4 @@
#include <memory>
#include <unordered_set>
#include <gtest/gtest.h>
#include <entt/entity/sparse_set.hpp>
@@ -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<std::uint64_t, move_only_component> set;
entt::sparse_set<std::uint64_t, std::unique_ptr<int>> set;
(void)set;
}