more on registry::clone (close #161)
This commit is contained in:
@@ -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 ®, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user