From 448c3ae425faa3c16bdb03c3afbc811eb49656c9 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Tue, 4 Feb 2020 20:23:22 +0100 Subject: [PATCH] registry/storage: assign review --- docs/md/entity.md | 22 +++++++++++++-- src/entt/entity/registry.hpp | 52 ++++++++++++++++++----------------- src/entt/entity/storage.hpp | 33 +++++++++++++++++----- test/entt/entity/registry.cpp | 21 ++++++++------ test/entt/entity/storage.cpp | 2 +- 5 files changed, 87 insertions(+), 43 deletions(-) diff --git a/docs/md/entity.md b/docs/md/entity.md index 10772d411..960e907b2 100644 --- a/docs/md/entity.md +++ b/docs/md/entity.md @@ -206,8 +206,26 @@ vel.dx = 0.; vel.dy = 0.; ``` -This function is overloaded and accepts also a couple of iterators to be used to -assign the same component to multiple entities at once. +This function is overloaded and accepts also a couple of iterators in order to: + +* Assign the same component to multiple entities at once when a type is + specified as a template parameter or an instance is passed as an argument. + + ```cpp + // default initialized type assigned by copy to all entities + registry.assign(first, last); + + // user-defined instance assigned by copy to all entities + registry.assign(from, to, position{0., 0.}); + ``` + +* Assign a range of components to multiple entities when a range is provided + (the length of the range of components must be the same of that of entities): + + ```cpp + // first and last specify the range of entities, instances points to the first element of the range of components + registry.assign(first, last, instances); + ``` If an entity already has the given component, the `replace` member function template can be used to replace it: diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index a1bdc751a..a6e649bc7 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -69,11 +69,10 @@ class basic_registry { return this->get(entt); } - template + template std::enable_if_t::value_type, Entity>, void> - assign(basic_registry &owner, It first, It last, Func... func) { - this->construct(first, last); - (func(this->raw() + this->size() - std::distance(first, last)), ...); + assign(basic_registry &owner, It first, It last, Value &&... value) { + this->construct(first, last, std::forward(value)...); if(!construction.empty()) { std::for_each(first, last, [this, &owner](const auto entt) { construction.publish(owner, entt); }); @@ -213,10 +212,7 @@ class basic_registry { if constexpr(ENTT_ENABLE_ETO(Component)) { other.assure().assign(other, cpool.begin(), cpool.end()); } else { - other.assure().assign(other, cpool.begin(), cpool.end(), [&cpool](auto *instance) { - const auto &source = static_cast &>(cpool); - std::copy(source.cbegin(), source.cend(), instance); - }); + other.assure().assign(other, cpool.begin(), cpool.end(), static_cast &>(cpool).cbegin()); } }; @@ -635,32 +631,38 @@ public: /** * @brief Assigns each entity in a range the given component. * - * Function objects are optional. They are invoked to give the caller a - * chance to initialize the components before they are passed to any - * registered listener.
- * The signature of the functions should be equivalent to the following: - * - * @code{.cpp} - * void(It it); - * @endcode - * - * Where `it` is an iterator to the first element of the range of newly - * created objects. - * * @sa assign * * @tparam Component Type of component to create. * @tparam It Type of input iterator. - * @tparam Func Types of the function objects to invoke. * @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. - * @param func Valid function objects. + * @param value An instance of the component to assign. */ - template + template std::enable_if_t::value_type, entity_type>, void> - assign(It first, It last, Func... func) { + assign(It first, It last, const Component &value = {}) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); - assure().assign(*this, first, last, std::move(func)...); + assure().assign(*this, first, last, value); + } + + /** + * @brief Assigns each entity in a range the given components. + * + * @sa assign + * + * @tparam Component Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt 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. + * @param value An iterator to the first element of the range of components. + */ + template + std::enable_if_t::value_type, entity_type>, void> + assign(EIt first, EIt last, CIt value) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); })); + assure().assign(*this, first, last, value); } /** diff --git a/src/entt/entity/storage.hpp b/src/entt/entity/storage.hpp index a3b8b60ac..c10a26922 100644 --- a/src/entt/entity/storage.hpp +++ b/src/entt/entity/storage.hpp @@ -329,10 +329,8 @@ public: } /** - * @brief Assigns one or more entities to a storage and default constructs - * their objects. - * - * The object type must be at least move and default insertable. + * @brief Assigns one or more entities to a storage and constructs their + * objects with from a given instance. * * @warning * Attempting to assign an entity that already belongs to the storage @@ -343,11 +341,32 @@ public: * @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. + * @param value An instance of the object to construct. */ template std::enable_if_t::value_type, entity_type>, void> - construct(It first, It last) { - instances.insert(instances.end(), std::distance(first, last), object_type{}); + construct(It first, It last, const object_type &value = {}) { + instances.insert(instances.end(), std::distance(first, last), value); + // entities go after components in case constructors throw + underlying_type::construct(first, last); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given range. + * + * @sa construct + * + * @tparam EIt Type of input iterator. + * @tparam CIt 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. + * @param value An iterator to the first element of the range of objects. + */ + template + std::enable_if_t::value_type, entity_type>, void> + construct(EIt first, EIt last, CIt value) { + instances.insert(instances.end(), value, value + std::distance(first, last)); // entities go after components in case constructors throw underlying_type::construct(first, last); } @@ -675,7 +694,7 @@ public: */ template std::enable_if_t::value_type, entity_type>, void> - construct(It first, It last) { + construct(It first, It last, const object_type & = {}) { underlying_type::construct(first, last); } diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index 6f643f12b..6fa7d63f5 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -315,17 +315,21 @@ TEST(Registry, CreateManyEntitiesAtOnceWithListener) { registry.on_construct().connect<&listener::incr>(listener); registry.create(std::begin(entities), std::end(entities)); - registry.assign(std::begin(entities), std::end(entities)); - registry.assign(std::begin(entities), std::end(entities)); + registry.assign(std::begin(entities), std::end(entities), 42); + registry.assign(std::begin(entities), std::end(entities), 'c'); + ASSERT_EQ(registry.get(entities[0]), 42); + ASSERT_EQ(registry.get(entities[1]), 'c'); ASSERT_EQ(listener.counter, 3); registry.on_construct().disconnect<&listener::incr>(listener); registry.on_construct().connect<&listener::incr>(listener); registry.create(std::begin(entities), std::end(entities)); - registry.assign(std::begin(entities), std::end(entities)); + registry.assign(std::begin(entities), std::end(entities), 'a'); registry.assign(std::begin(entities), std::end(entities)); + ASSERT_TRUE(registry.has(entities[0])); + ASSERT_EQ(registry.get(entities[2]), 'a'); ASSERT_EQ(listener.counter, 6); } @@ -1131,18 +1135,19 @@ TEST(Registry, RangeAssign) { ASSERT_FALSE(registry.has(e2)); const auto view = registry.view(); - registry.assign(view.begin(), view.end()); + registry.assign(view.begin(), view.end(), 3.f); - ASSERT_EQ(registry.get(e0), 0.f); - ASSERT_EQ(registry.get(e1), 0.f); + ASSERT_EQ(registry.get(e0), 3.f); + ASSERT_EQ(registry.get(e1), 3.f); ASSERT_FALSE(registry.has(e2)); registry.clear(); - registry.assign(registry.data(), registry.data() + registry.size(), [](float *raw) { *(++raw) = 1.f; }); + float value[3]{0.f, 1.f, 2.f}; + registry.assign(registry.data(), registry.data() + registry.size(), value); ASSERT_EQ(registry.get(e0), 0.f); ASSERT_EQ(registry.get(e1), 1.f); - ASSERT_EQ(registry.get(e2), 0.f); + ASSERT_EQ(registry.get(e2), 2.f); } TEST(Registry, RangeRemove) { diff --git a/test/entt/entity/storage.cpp b/test/entt/entity/storage.cpp index 6c7ec381a..1d9e8aaf2 100644 --- a/test/entt/entity/storage.cpp +++ b/test/entt/entity/storage.cpp @@ -100,7 +100,7 @@ TEST(Storage, BatchAdd) { entities[0] = entt::entity{3}; entities[1] = entt::entity{42}; - pool.construct(std::begin(entities), std::end(entities)); + pool.construct(std::begin(entities), std::end(entities), {}); ASSERT_TRUE(pool.has(entities[0])); ASSERT_TRUE(pool.has(entities[1]));