From 007f1b228c1738f6078d1c171e983967ced9c783 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 10 Apr 2019 01:37:37 +0700 Subject: [PATCH 1/7] sparse_map::insert_or_assign and noexcept fixes --- ecs.hpp | 98 +++++++++++++++++++++++++++++---------------------- ecs_tests.cpp | 19 ++++++++++ 2 files changed, 75 insertions(+), 42 deletions(-) diff --git a/ecs.hpp b/ecs.hpp index c045567..ed8d5e5 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -321,16 +321,19 @@ namespace ecs_hpp return insert(T(std::forward(args)...)); } - bool unordered_erase(const T& v) - noexcept(std::is_nothrow_move_assignable::value) + bool unordered_erase(const T& v) noexcept( + noexcept(indexer_(std::declval())) && + std::is_nothrow_move_assignable::value) { if ( !has(v) ) { return false; } const std::size_t vi = indexer_(v); const std::size_t dense_index = sparse_[vi]; - dense_[dense_index] = std::move(dense_.back()); - sparse_[indexer_(dense_[dense_index])] = dense_index; + if ( dense_index != dense_.size() - 1 ) { + dense_[dense_index] = std::move(dense_.back()); + sparse_[indexer_(dense_[dense_index])] = dense_index; + } dense_.pop_back(); return true; } @@ -339,17 +342,20 @@ namespace ecs_hpp dense_.clear(); } - bool has(const T& v) const noexcept { + bool has(const T& v) const noexcept( + noexcept(indexer_(std::declval()))) + { const std::size_t vi = indexer_(v); return vi < sparse_.size() && sparse_[vi] < dense_.size() && dense_[sparse_[vi]] == v; } - const_iterator find(const T& v) const noexcept { - const std::size_t vi = indexer_(v); + const_iterator find(const T& v) const noexcept( + noexcept(indexer_(std::declval()))) + { return has(v) - ? begin() + sparse_[vi] + ? begin() + sparse_[indexer_(v)] : end(); } @@ -361,7 +367,9 @@ namespace ecs_hpp throw std::logic_error("ecs_hpp::sparse_set (value not found)"); } - std::pair find_dense_index(const T& v) const noexcept { + std::pair find_dense_index(const T& v) const noexcept( + noexcept(indexer_(std::declval()))) + { return has(v) ? std::make_pair(sparse_[indexer_(v)], true) : std::make_pair(std::size_t(-1), false); @@ -438,57 +446,59 @@ namespace ecs_hpp sparse_map(const Indexer& indexer = Indexer()) : keys_(indexer) {} - bool insert(const K& k, const T& v) { + template < typename UK, typename UT > + bool insert(UK&& k, UT&& v) { if ( keys_.has(k) ) { return false; } - values_.push_back(v); + values_.push_back(std::forward(v)); try { - return keys_.insert(k); + return keys_.insert(std::forward(k)); } catch (...) { values_.pop_back(); throw; } } - bool insert(const K& k, T&& v) { - if ( keys_.has(k) ) { - return false; - } - values_.push_back(std::move(v)); - try { - return keys_.insert(k); - } catch (...) { - values_.pop_back(); - throw; - } - } - - template < typename... Args > - bool emplace(const K& k, Args&&... args) { + template < typename UK, typename... Args > + bool emplace(UK&& k, Args&&... args) { if ( keys_.has(k) ) { return false; } values_.emplace_back(std::forward(args)...); try { - return keys_.insert(k); + return keys_.insert(std::forward(k)); } catch (...) { values_.pop_back(); throw; } } - std::enable_if_t< - std::is_nothrow_move_assignable::value, - bool> - unordered_erase(const K& k) - noexcept(std::is_nothrow_move_assignable::value) + template < typename UK, typename UT > + bool insert_or_assign(UK&& k, UT&& v) { + if ( keys_.has(k) ) { + get(k) = std::forward(v); + return false; + } else { + insert(std::forward(k), std::forward(v)); + return true; + } + } + + bool unordered_erase(const K& k) noexcept( + noexcept(keys_.find_dense_index(k)) && + std::is_nothrow_move_assignable::value) { - if ( !keys_.has(k) ) { + static_assert( + noexcept(keys_.unordered_erase(k)), + "unsupported with current key type"); + const auto value_index_p = keys_.find_dense_index(k); + if ( !value_index_p.second ) { return false; } - const std::size_t value_index = keys_.get_dense_index(k); - values_[value_index] = std::move(values_.back()); + if ( value_index_p.first != values_.size() - 1 ) { + values_[value_index_p.first] = std::move(values_.back()); + } values_.pop_back(); keys_.unordered_erase(k); return true; @@ -499,7 +509,9 @@ namespace ecs_hpp values_.clear(); } - bool has(const K& k) const noexcept { + bool has(const K& k) const noexcept( + noexcept(keys_.has(k))) + { return keys_.has(k); } @@ -511,14 +523,18 @@ namespace ecs_hpp return values_[keys_.get_dense_index(k)]; } - T* find(const K& k) noexcept { + T* find(const K& k) noexcept( + noexcept(keys_.find_dense_index(k))) + { const auto value_index_p = keys_.find_dense_index(k); return value_index_p.second ? &values_[value_index_p.first] : nullptr; } - const T* find(const K& k) const noexcept { + const T* find(const K& k) const noexcept( + noexcept(keys_.find_dense_index(k))) + { const auto value_index_p = keys_.find_dense_index(k); return value_index_p.second ? &values_[value_index_p.first] @@ -606,9 +622,7 @@ namespace ecs_hpp template < typename T > template < typename... Args > void component_storage::assign(entity_id id, Args&&... args) { - if ( !components_.emplace(id, std::forward(args)...) ) { - components_.get(id) = T(std::forward(args)...); - } + components_.insert_or_assign(id, T(std::forward(args)...)); } template < typename T > diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 775c6e5..446256f 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -280,6 +280,25 @@ TEST_CASE("detail") { REQUIRE(s.unordered_erase(position_c(1,2))); REQUIRE(s.get(position_c(3,4)).x == 3); } + { + struct obj_t { + int x; + obj_t(int nx) : x(nx) {} + }; + + sparse_map m; + REQUIRE(m.insert_or_assign(42, obj_t(42))); + REQUIRE(m.has(42)); + REQUIRE(m.get(42).x == 42); + REQUIRE_FALSE(m.insert_or_assign(42, obj_t(21))); + REQUIRE(m.has(42)); + REQUIRE(m.get(42).x == 21); + REQUIRE(m.size() == 1); + REQUIRE(m.insert_or_assign(84, obj_t(84))); + REQUIRE(m.has(84)); + REQUIRE(m.get(84).x == 84); + REQUIRE(m.size() == 2); + } } } From 24bfcaf7ebb11ea10d79dcb949e2efa0100f4332 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 10 Apr 2019 02:08:55 +0700 Subject: [PATCH 2/7] add entity cloning --- ecs.hpp | 53 +++++++++++++++++++++++++++++++++++++++++---------- ecs_tests.cpp | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/ecs.hpp b/ecs.hpp index ed8d5e5..8f9e4b8 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -322,7 +322,7 @@ namespace ecs_hpp } bool unordered_erase(const T& v) noexcept( - noexcept(indexer_(std::declval())) && + noexcept(std::declval()(std::declval())) && std::is_nothrow_move_assignable::value) { if ( !has(v) ) { @@ -343,7 +343,7 @@ namespace ecs_hpp } bool has(const T& v) const noexcept( - noexcept(indexer_(std::declval()))) + noexcept(std::declval()(std::declval()))) { const std::size_t vi = indexer_(v); return vi < sparse_.size() @@ -352,7 +352,7 @@ namespace ecs_hpp } const_iterator find(const T& v) const noexcept( - noexcept(indexer_(std::declval()))) + noexcept(std::declval()(std::declval()))) { return has(v) ? begin() + sparse_[indexer_(v)] @@ -368,7 +368,7 @@ namespace ecs_hpp } std::pair find_dense_index(const T& v) const noexcept( - noexcept(indexer_(std::declval()))) + noexcept(std::declval()(std::declval()))) { return has(v) ? std::make_pair(sparse_[indexer_(v)], true) @@ -486,7 +486,7 @@ namespace ecs_hpp } bool unordered_erase(const K& k) noexcept( - noexcept(keys_.find_dense_index(k)) && + noexcept(std::declval>().find_dense_index(k)) && std::is_nothrow_move_assignable::value) { static_assert( @@ -510,7 +510,7 @@ namespace ecs_hpp } bool has(const K& k) const noexcept( - noexcept(keys_.has(k))) + noexcept(std::declval>().has(k))) { return keys_.has(k); } @@ -524,7 +524,7 @@ namespace ecs_hpp } T* find(const K& k) noexcept( - noexcept(keys_.find_dense_index(k))) + noexcept(std::declval>().find_dense_index(k))) { const auto value_index_p = keys_.find_dense_index(k); return value_index_p.second @@ -533,7 +533,7 @@ namespace ecs_hpp } const T* find(const K& k) const noexcept( - noexcept(keys_.find_dense_index(k))) + noexcept(std::declval>().find_dense_index(k))) { const auto value_index_p = keys_.find_dense_index(k); return value_index_p.second @@ -588,6 +588,7 @@ namespace ecs_hpp virtual ~component_storage_base() = default; virtual bool remove(entity_id id) noexcept = 0; virtual bool has(entity_id id) const noexcept = 0; + virtual void clone(entity_id from, entity_id to) = 0; }; template < typename T > @@ -605,6 +606,7 @@ namespace ecs_hpp std::size_t count() const noexcept; bool has(entity_id id) const noexcept override; + void clone(entity_id from, entity_id to) override; template < typename F > void for_each_component(F&& f); @@ -655,10 +657,18 @@ namespace ecs_hpp return components_.has(id); } + template < typename T > + void component_storage::clone(entity_id from, entity_id to) { + const T* c = components_.find(from); + if ( c ) { + components_.insert_or_assign(to, *c); + } + } + template < typename T > template < typename F > void component_storage::for_each_component(F&& f) { - for ( const auto id : components_ ) { + for ( const entity_id id : components_ ) { f(id, components_.get(id)); } } @@ -666,7 +676,7 @@ namespace ecs_hpp template < typename T > template < typename F > void component_storage::for_each_component(F&& f) const { - for ( const auto id : components_ ) { + for ( const entity_id id : components_ ) { f(id, components_.get(id)); } } @@ -699,6 +709,7 @@ namespace ecs_hpp bool destroy(); bool alive() const noexcept; + entity clone() const; template < typename T, typename... Args > bool assign_component(Args&&... args); @@ -1032,6 +1043,7 @@ namespace ecs_hpp const_component wrap_component(const const_uentity& ent) const noexcept; entity create_entity(); + entity create_entity(const const_uentity& prototype); bool destroy_entity(const uentity& ent); bool alive_entity(const const_uentity& ent) const noexcept; @@ -1244,6 +1256,10 @@ namespace ecs_hpp return detail::as_const(*owner_).alive_entity(id_); } + inline entity entity::clone() const { + return (*owner_).create_entity(id_); + } + template < typename T, typename... Args > bool entity::assign_component(Args&&... args) { return (*owner_).assign_component( @@ -1691,6 +1707,23 @@ namespace ecs_hpp throw std::logic_error("ecs_hpp::registry (entity index overlow)"); } + inline entity registry::create_entity(const const_uentity& prototype) { + assert(prototype.check_owner(this)); + entity ent = create_entity(); + if ( !alive_entity(prototype) ) { + return ent; + } + try { + for ( const auto family_id : storages_ ) { + storages_.get(family_id)->clone(prototype, ent.id()); + } + } catch (...) { + destroy_entity(ent); + throw; + } + return ent; + } + inline bool registry::destroy_entity(const uentity& ent) { assert(ent.check_owner(this)); remove_all_components(ent); diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 446256f..072968a 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -732,6 +732,49 @@ TEST_CASE("registry") { REQUIRE(e1.get_component().y == 40); } } + SECTION("cloning") { + { + ecs::registry w; + + auto e1 = w.create_entity(); + ecs::entity_filler(e1) + .component(1, 2) + .component(3, 4); + + auto e2 = w.create_entity(e1); + REQUIRE(w.component_count() == 2); + REQUIRE(w.component_count() == 2); + REQUIRE(e2.exists_component()); + REQUIRE(e2.exists_component()); + REQUIRE(e2.get_component() == position_c(1, 2)); + REQUIRE(e2.get_component() == velocity_c(3, 4)); + + e2.remove_component(); + auto e3 = e2.clone(); + + REQUIRE(w.component_count() == 3); + REQUIRE(w.component_count() == 1); + + REQUIRE(e3.exists_component()); + REQUIRE_FALSE(e3.exists_component()); + REQUIRE(e3.get_component() == position_c(1, 2)); + } + { + ecs::registry w; + + auto e1 = w.create_entity(); + ecs::entity_filler(e1) + .component(1, 2) + .component(3, 4); + e1.destroy(); + + auto e2 = e1.clone(); + REQUIRE_FALSE(e2.exists_component()); + REQUIRE_FALSE(e2.exists_component()); + REQUIRE(w.component_count() == 0); + REQUIRE(w.component_count() == 0); + } + } SECTION("for_each_entity") { { ecs::registry w; From 783376c6fc5f43fbd962c889aba97bac5e088eb3 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 10 Apr 2019 03:56:30 +0700 Subject: [PATCH 3/7] return component reference from assign. rename alive to valid. fix entity component_count function. --- ecs.hpp | 104 +++++++++++++++++++++++--------------------------- ecs_tests.cpp | 102 ++++++++++++++++++------------------------------- 2 files changed, 85 insertions(+), 121 deletions(-) diff --git a/ecs.hpp b/ecs.hpp index 8f9e4b8..cd30ce9 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -597,7 +597,7 @@ namespace ecs_hpp component_storage(registry& owner); template < typename... Args > - void assign(entity_id id, Args&&... args); + T& assign(entity_id id, Args&&... args); bool exists(entity_id id) const noexcept; bool remove(entity_id id) noexcept override; @@ -623,8 +623,9 @@ namespace ecs_hpp template < typename T > template < typename... Args > - void component_storage::assign(entity_id id, Args&&... args) { + T& component_storage::assign(entity_id id, Args&&... args) { components_.insert_or_assign(id, T(std::forward(args)...)); + return components_.get(id); } template < typename T > @@ -708,11 +709,11 @@ namespace ecs_hpp entity_id id() const noexcept; bool destroy(); - bool alive() const noexcept; entity clone() const; + bool valid() const noexcept; template < typename T, typename... Args > - bool assign_component(Args&&... args); + T& assign_component(Args&&... args); template < typename T > bool remove_component(); @@ -743,6 +744,8 @@ namespace ecs_hpp std::tuple find_components() noexcept; template < typename... Ts > std::tuple find_components() const noexcept; + + std::size_t component_count() const noexcept; private: registry* owner_{nullptr}; entity_id id_{0u}; @@ -795,7 +798,7 @@ namespace ecs_hpp const registry& owner() const noexcept; entity_id id() const noexcept; - bool alive() const noexcept; + bool valid() const noexcept; template < typename T > bool exists_component() const noexcept; @@ -811,6 +814,8 @@ namespace ecs_hpp template < typename... Ts > std::tuple find_components() const noexcept; + + std::size_t component_count() const noexcept; private: const registry* owner_{nullptr}; entity_id id_{0u}; @@ -865,7 +870,7 @@ namespace ecs_hpp bool exists() const noexcept; template < typename... Args > - bool assign(Args&&... args); + T& assign(Args&&... args); T& get(); const T& get() const; @@ -1046,10 +1051,10 @@ namespace ecs_hpp entity create_entity(const const_uentity& prototype); bool destroy_entity(const uentity& ent); - bool alive_entity(const const_uentity& ent) const noexcept; + bool valid_entity(const const_uentity& ent) const noexcept; template < typename T, typename... Args > - bool assign_component(const uentity& ent, Args&&... args); + T& assign_component(const uentity& ent, Args&&... args); template < typename T > bool remove_component(const uentity& ent); @@ -1081,7 +1086,6 @@ namespace ecs_hpp template < typename T > std::size_t component_count() const noexcept; - template < typename T > std::size_t entity_component_count(const const_uentity& ent) const noexcept; template < typename F > @@ -1252,16 +1256,16 @@ namespace ecs_hpp return (*owner_).destroy_entity(id_); } - inline bool entity::alive() const noexcept { - return detail::as_const(*owner_).alive_entity(id_); - } - inline entity entity::clone() const { return (*owner_).create_entity(id_); } + inline bool entity::valid() const noexcept { + return detail::as_const(*owner_).valid_entity(id_); + } + template < typename T, typename... Args > - bool entity::assign_component(Args&&... args) { + T& entity::assign_component(Args&&... args) { return (*owner_).assign_component( id_, std::forward(args)...); @@ -1321,6 +1325,10 @@ namespace ecs_hpp return detail::as_const(*owner_).find_components(id_); } + inline std::size_t entity::component_count() const noexcept { + return detail::as_const(*owner_).entity_component_count(id_); + } + inline bool operator<(const entity& l, const entity& r) noexcept { return (&l.owner() < &r.owner()) || (&l.owner() == &r.owner() && l.id() < r.id()); @@ -1372,8 +1380,8 @@ namespace ecs_hpp return id_; } - inline bool const_entity::alive() const noexcept { - return (*owner_).alive_entity(id_); + inline bool const_entity::valid() const noexcept { + return (*owner_).valid_entity(id_); } template < typename T > @@ -1401,6 +1409,10 @@ namespace ecs_hpp return (*owner_).find_components(id_); } + inline std::size_t const_entity::component_count() const noexcept { + return (*owner_).entity_component_count(id_); + } + inline bool operator<(const const_entity& l, const const_entity& r) noexcept { return (&l.owner() < &r.owner()) || (&l.owner() == &r.owner() && l.id() < r.id()); @@ -1459,7 +1471,7 @@ namespace ecs_hpp template < typename T > template < typename... Args > - bool component::assign(Args&&... args) { + T& component::assign(Args&&... args) { return owner_.assign_component( std::forward(args)...); } @@ -1708,11 +1720,8 @@ namespace ecs_hpp } inline entity registry::create_entity(const const_uentity& prototype) { - assert(prototype.check_owner(this)); + assert(valid_entity(prototype)); entity ent = create_entity(); - if ( !alive_entity(prototype) ) { - return ent; - } try { for ( const auto family_id : storages_ ) { storages_.get(family_id)->clone(prototype, ent.id()); @@ -1725,7 +1734,7 @@ namespace ecs_hpp } inline bool registry::destroy_entity(const uentity& ent) { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); remove_all_components(ent); if ( entity_ids_.unordered_erase(ent) ) { free_entity_ids_.push_back(ent); @@ -1734,29 +1743,22 @@ namespace ecs_hpp return false; } - inline bool registry::alive_entity(const const_uentity& ent) const noexcept { + inline bool registry::valid_entity(const const_uentity& ent) const noexcept { assert(ent.check_owner(this)); return entity_ids_.has(ent); } template < typename T, typename... Args > - bool registry::assign_component(const uentity& ent, Args&&... args) { - assert(ent.check_owner(this)); - if ( !alive_entity(ent) ) { - return false; - } - get_or_create_storage_().assign( + T& registry::assign_component(const uentity& ent, Args&&... args) { + assert(valid_entity(ent)); + return get_or_create_storage_().assign( ent, std::forward(args)...); - return true; } template < typename T > bool registry::remove_component(const uentity& ent) { - assert(ent.check_owner(this)); - if ( !alive_entity(ent) ) { - return false; - } + assert(valid_entity(ent)); detail::component_storage* storage = find_storage_(); return storage ? storage->remove(ent) @@ -1765,10 +1767,7 @@ namespace ecs_hpp template < typename T > bool registry::exists_component(const const_uentity& ent) const noexcept { - assert(ent.check_owner(this)); - if ( !alive_entity(ent) ) { - return false; - } + assert(valid_entity(ent)); const detail::component_storage* storage = find_storage_(); return storage ? storage->exists(ent) @@ -1776,10 +1775,7 @@ namespace ecs_hpp } inline std::size_t registry::remove_all_components(const uentity& ent) noexcept { - assert(ent.check_owner(this)); - if ( !alive_entity(ent) ) { - return 0u; - } + assert(valid_entity(ent)); std::size_t removed_count = 0u; for ( const auto family_id : storages_ ) { if ( storages_.get(family_id)->remove(ent) ) { @@ -1791,7 +1787,7 @@ namespace ecs_hpp template < typename T > T& registry::get_component(const uentity& ent) { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); T* component = find_component(ent); if ( component ) { return *component; @@ -1801,7 +1797,7 @@ namespace ecs_hpp template < typename T > const T& registry::get_component(const const_uentity& ent) const { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); const T* component = find_component(ent); if ( component ) { return *component; @@ -1811,7 +1807,7 @@ namespace ecs_hpp template < typename T > T* registry::find_component(const uentity& ent) noexcept { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); detail::component_storage* storage = find_storage_(); return storage ? storage->find(ent) @@ -1820,7 +1816,7 @@ namespace ecs_hpp template < typename T > const T* registry::find_component(const const_uentity& ent) const noexcept { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); const detail::component_storage* storage = find_storage_(); return storage ? storage->find(ent) @@ -1829,25 +1825,25 @@ namespace ecs_hpp template < typename... Ts > std::tuple registry::get_components(const uentity& ent) { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); return std::make_tuple(std::ref(get_component(ent))...); } template < typename... Ts > std::tuple registry::get_components(const const_uentity& ent) const { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); return std::make_tuple(std::cref(get_component(ent))...); } template < typename... Ts > std::tuple registry::find_components(const uentity& ent) noexcept { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); return std::make_tuple(find_component(ent)...); } template < typename... Ts > std::tuple registry::find_components(const const_uentity& ent) const noexcept { - assert(ent.check_owner(this)); + assert(valid_entity(ent)); return std::make_tuple(find_component(ent)...); } @@ -1859,12 +1855,8 @@ namespace ecs_hpp : 0u; } - template < typename T > - std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept { - assert(ent.check_owner(this)); - if ( !alive_entity(ent) ) { - return 0u; - } + inline std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept { + assert(valid_entity(ent)); std::size_t component_count = 0u; for ( const auto family_id : storages_ ) { if ( storages_.get(family_id)->has(ent) ) { diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 072968a..53f3df6 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -330,12 +330,9 @@ TEST_CASE("registry") { REQUIRE_FALSE(e1 != e2); REQUIRE_FALSE(e2 != e3); - REQUIRE_FALSE(w.alive_entity(e1)); - REQUIRE_FALSE(w.alive_entity(e2)); - REQUIRE_FALSE(w.alive_entity(e3)); - - REQUIRE_FALSE(w.destroy_entity(e1)); - REQUIRE_FALSE(w.destroy_entity(e2)); + REQUIRE_FALSE(w.valid_entity(e1)); + REQUIRE_FALSE(w.valid_entity(e2)); + REQUIRE_FALSE(w.valid_entity(e3)); } { ecs::registry w; @@ -353,26 +350,22 @@ TEST_CASE("registry") { REQUIRE_FALSE(e2 == e3); REQUIRE(e3 == ee3); - REQUIRE(w.alive_entity(e1)); - REQUIRE(w.alive_entity(e2)); - REQUIRE(w.alive_entity(e3)); - REQUIRE(w.alive_entity(ee3)); + REQUIRE(w.valid_entity(e1)); + REQUIRE(w.valid_entity(e2)); + REQUIRE(w.valid_entity(e3)); + REQUIRE(w.valid_entity(ee3)); REQUIRE(w.destroy_entity(e1)); - REQUIRE_FALSE(w.alive_entity(e1)); - REQUIRE(w.alive_entity(e2)); + REQUIRE_FALSE(w.valid_entity(e1)); + REQUIRE(w.valid_entity(e2)); REQUIRE(w.destroy_entity(e2)); - REQUIRE_FALSE(w.alive_entity(e1)); - REQUIRE_FALSE(w.alive_entity(e2)); + REQUIRE_FALSE(w.valid_entity(e1)); + REQUIRE_FALSE(w.valid_entity(e2)); REQUIRE(w.destroy_entity(ee3)); - REQUIRE_FALSE(w.alive_entity(e3)); - REQUIRE_FALSE(w.alive_entity(ee3)); - - REQUIRE_FALSE(w.destroy_entity(e1)); - REQUIRE_FALSE(w.destroy_entity(e2)); - REQUIRE_FALSE(w.destroy_entity(ee3)); + REQUIRE_FALSE(w.valid_entity(e3)); + REQUIRE_FALSE(w.valid_entity(ee3)); } { ecs::registry w; @@ -469,7 +462,7 @@ TEST_CASE("registry") { REQUIRE_THROWS_AS(c1.get(), std::logic_error); REQUIRE_THROWS_AS(c2.get(), std::logic_error); - REQUIRE(c1.assign(4,2)); + REQUIRE(c1.assign(4,2) == position_c(4,2)); REQUIRE(c1.exists()); REQUIRE(c2.exists()); @@ -482,7 +475,7 @@ TEST_CASE("registry") { REQUIRE(c2.get().x == 4); REQUIRE(c2.get().y == 2); - REQUIRE(c1.assign(2,4)); + REQUIRE(&c1.assign(2,4) == &c1.get()); REQUIRE(c1.find()->x == 2); REQUIRE(c1.find()->y == 4); @@ -514,20 +507,23 @@ TEST_CASE("registry") { REQUIRE_FALSE(w.exists_component(e1)); REQUIRE_FALSE(w.exists_component(e1)); REQUIRE_FALSE(w.component_count()); - REQUIRE_FALSE(w.entity_component_count(e1)); + REQUIRE_FALSE(w.entity_component_count(e1)); + REQUIRE_FALSE(e1.component_count()); - REQUIRE(w.assign_component(e1)); + REQUIRE(w.assign_component(e1) == position_c()); REQUIRE(w.exists_component(e1)); REQUIRE_FALSE(w.exists_component(e1)); REQUIRE(w.component_count() == 1u); REQUIRE(w.component_count() == 0u); - REQUIRE(w.entity_component_count(e1) == 1u); + REQUIRE(w.entity_component_count(e1) == 1u); + REQUIRE(e1.component_count() == 1u); - REQUIRE(w.assign_component(e1)); + REQUIRE(w.assign_component(e1) == velocity_c()); REQUIRE(w.component_count() == 1u); REQUIRE(w.component_count() == 1u); - REQUIRE(w.entity_component_count(e1) == 2u); + REQUIRE(w.entity_component_count(e1) == 2u); + REQUIRE(e1.component_count() == 2u); REQUIRE(w.exists_component(e1)); REQUIRE(w.exists_component(e1)); @@ -538,30 +534,28 @@ TEST_CASE("registry") { REQUIRE_FALSE(w.exists_component(e1)); REQUIRE_FALSE(w.component_count()); REQUIRE_FALSE(w.component_count()); - REQUIRE_FALSE(w.entity_component_count(e1)); + REQUIRE_FALSE(w.entity_component_count(e1)); + REQUIRE_FALSE(e1.component_count()); } { REQUIRE_FALSE(e1.exists_component()); REQUIRE_FALSE(e1.exists_component()); - REQUIRE(e1.assign_component()); + REQUIRE(e1.assign_component() == position_c()); REQUIRE(e1.exists_component()); REQUIRE_FALSE(e1.exists_component()); - REQUIRE(e1.assign_component()); + REQUIRE(e1.assign_component() == velocity_c()); REQUIRE(e1.exists_component()); REQUIRE(e1.exists_component()); REQUIRE(e1.destroy()); - REQUIRE_FALSE(e1.exists_component()); - REQUIRE_FALSE(e1.exists_component()); REQUIRE_FALSE(w.component_count()); REQUIRE_FALSE(w.component_count()); - REQUIRE_FALSE(w.entity_component_count(e1)); } } { @@ -570,37 +564,30 @@ TEST_CASE("registry") { auto e1 = w.create_entity(); auto e2 = w.create_entity(); - REQUIRE(w.assign_component(e1)); - REQUIRE(w.assign_component(e1)); + w.assign_component(e1); + w.assign_component(e1); - REQUIRE(w.assign_component(e2)); - REQUIRE(w.assign_component(e2)); + w.assign_component(e2); + w.assign_component(e2); REQUIRE(w.destroy_entity(e1)); - REQUIRE_FALSE(w.exists_component(e1)); - REQUIRE_FALSE(w.exists_component(e1)); - REQUIRE(w.exists_component(e2)); REQUIRE(w.exists_component(e2)); } - { - ecs::registry w; - auto e1 = w.create_entity(); - REQUIRE(e1.destroy()); - REQUIRE_FALSE(e1.assign_component()); - REQUIRE_FALSE(w.exists_component(e1)); - } { ecs::registry w; auto e1 = w.create_entity(); auto e2 = w.create_entity(); - REQUIRE(w.assign_component(e1)); + const position_c& e1_pos = w.assign_component(e1); + REQUIRE(&e1_pos == &w.get_component(e1)); - REQUIRE(w.assign_component(e2)); - REQUIRE(w.assign_component(e2)); + const position_c& e2_pos = w.assign_component(e2); + const velocity_c& e2_vel = w.assign_component(e2); + REQUIRE(&e2_pos == &w.get_component(e2)); + REQUIRE(&e2_vel == &w.get_component(e2)); REQUIRE(e1.destroy()); } @@ -759,21 +746,6 @@ TEST_CASE("registry") { REQUIRE_FALSE(e3.exists_component()); REQUIRE(e3.get_component() == position_c(1, 2)); } - { - ecs::registry w; - - auto e1 = w.create_entity(); - ecs::entity_filler(e1) - .component(1, 2) - .component(3, 4); - e1.destroy(); - - auto e2 = e1.clone(); - REQUIRE_FALSE(e2.exists_component()); - REQUIRE_FALSE(e2.exists_component()); - REQUIRE(w.component_count() == 0); - REQUIRE(w.component_count() == 0); - } } SECTION("for_each_entity") { { From e3358ee2e61f8a2792acf2b74de08b88426b178e Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 10 Apr 2019 05:28:42 +0700 Subject: [PATCH 4/7] add registry::entity_count function --- ecs.hpp | 5 +++++ ecs_tests.cpp | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/ecs.hpp b/ecs.hpp index cd30ce9..99b683e 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -1086,6 +1086,7 @@ namespace ecs_hpp template < typename T > std::size_t component_count() const noexcept; + std::size_t entity_count() const noexcept; std::size_t entity_component_count(const const_uentity& ent) const noexcept; template < typename F > @@ -1855,6 +1856,10 @@ namespace ecs_hpp : 0u; } + inline std::size_t registry::entity_count() const noexcept { + return entity_ids_.size(); + } + inline std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept { assert(valid_entity(ent)); std::size_t component_count = 0u; diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 53f3df6..3d00d26 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -411,6 +411,18 @@ TEST_CASE("registry") { // entity index overflow REQUIRE_THROWS_AS(w.create_entity(), std::logic_error); } + { + ecs::registry w; + REQUIRE_FALSE(w.entity_count()); + auto e1 = w.create_entity(); + REQUIRE(w.entity_count() == 1u); + auto e2 = w.create_entity(); + REQUIRE(w.entity_count() == 2u); + e1.destroy(); + REQUIRE(w.entity_count() == 1u); + e2.destroy(); + REQUIRE_FALSE(w.entity_count()); + } } SECTION("components") { { From cb3272d0dc594d9cc69101ba413dccdb1a659c89 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 10 Apr 2019 17:54:50 +0700 Subject: [PATCH 5/7] noexcept destroy --- ecs.hpp | 131 ++++++++++++++++++++++++-------------------------- ecs_tests.cpp | 6 ++- 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/ecs.hpp b/ecs.hpp index 99b683e..143b571 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -157,6 +157,24 @@ namespace ecs_hpp return impl::tuple_contains_impl<0>(t, v); } + // + // next_capacity_size + // + + inline std::size_t next_capacity_size( + std::size_t cur_size, + std::size_t min_size, + std::size_t max_size) + { + if ( min_size > max_size ) { + throw std::length_error("ecs_hpp::next_capacity_size"); + } + if ( cur_size >= max_size / 2u ) { + return max_size; + } + return std::max(cur_size * 2u, min_size); + } + // // entity_id index/version // @@ -262,6 +280,15 @@ namespace ecs_hpp public: using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; + public: + static_assert( + noexcept(std::declval()(std::declval())), + "unsupported sparse_set indexer"); + static_assert( + std::is_nothrow_destructible::value && + std::is_nothrow_move_assignable::value && + noexcept(std::declval() == std::declval()), + "unsupported sparse_set value type"); public: iterator begin() noexcept { return dense_.begin(); @@ -290,28 +317,17 @@ namespace ecs_hpp sparse_set(const Indexer& indexer = Indexer()) : indexer_(indexer) {} - bool insert(T&& v) { + template < typename UT > + bool insert(UT&& v) { if ( has(v) ) { return false; } const std::size_t vi = indexer_(v); if ( vi >= sparse_.size() ) { - sparse_.resize(new_sparse_size_for_(vi + 1u)); + sparse_.resize(next_capacity_size( + sparse_.size(), vi + 1u, sparse_.max_size())); } - dense_.push_back(std::move(v)); - sparse_[vi] = dense_.size() - 1u; - return true; - } - - bool insert(const T& v) { - if ( has(v) ) { - return false; - } - const std::size_t vi = indexer_(v); - if ( vi >= sparse_.size() ) { - sparse_.resize(new_sparse_size_for_(vi + 1u)); - } - dense_.push_back(v); + dense_.push_back(std::forward(v)); sparse_[vi] = dense_.size() - 1u; return true; } @@ -321,10 +337,7 @@ namespace ecs_hpp return insert(T(std::forward(args)...)); } - bool unordered_erase(const T& v) noexcept( - noexcept(std::declval()(std::declval())) && - std::is_nothrow_move_assignable::value) - { + bool unordered_erase(const T& v) noexcept { if ( !has(v) ) { return false; } @@ -342,18 +355,14 @@ namespace ecs_hpp dense_.clear(); } - bool has(const T& v) const noexcept( - noexcept(std::declval()(std::declval()))) - { + bool has(const T& v) const noexcept { const std::size_t vi = indexer_(v); return vi < sparse_.size() && sparse_[vi] < dense_.size() && dense_[sparse_[vi]] == v; } - const_iterator find(const T& v) const noexcept( - noexcept(std::declval()(std::declval()))) - { + const_iterator find(const T& v) const noexcept { return has(v) ? begin() + sparse_[indexer_(v)] : end(); @@ -367,9 +376,7 @@ namespace ecs_hpp throw std::logic_error("ecs_hpp::sparse_set (value not found)"); } - std::pair find_dense_index(const T& v) const noexcept( - noexcept(std::declval()(std::declval()))) - { + std::pair find_dense_index(const T& v) const noexcept { return has(v) ? std::make_pair(sparse_[indexer_(v)], true) : std::make_pair(std::size_t(-1), false); @@ -382,17 +389,6 @@ namespace ecs_hpp std::size_t size() const noexcept { return dense_.size(); } - private: - std::size_t new_sparse_size_for_(std::size_t nsize) const { - const std::size_t ms = sparse_.max_size(); - if ( nsize > ms ) { - throw std::length_error("ecs_hpp::sparse_set"); - } - if ( sparse_.size() >= ms / 2u ) { - return ms; - } - return std::max(sparse_.size() * 2u, nsize); - } private: Indexer indexer_; std::vector dense_; @@ -418,6 +414,11 @@ namespace ecs_hpp public: using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; + public: + static_assert( + std::is_nothrow_destructible::value && + std::is_nothrow_move_assignable::value, + "unsupported sparse_map value type"); public: iterator begin() noexcept { return keys_.begin(); @@ -485,13 +486,7 @@ namespace ecs_hpp } } - bool unordered_erase(const K& k) noexcept( - noexcept(std::declval>().find_dense_index(k)) && - std::is_nothrow_move_assignable::value) - { - static_assert( - noexcept(keys_.unordered_erase(k)), - "unsupported with current key type"); + bool unordered_erase(const K& k) noexcept { const auto value_index_p = keys_.find_dense_index(k); if ( !value_index_p.second ) { return false; @@ -509,9 +504,7 @@ namespace ecs_hpp values_.clear(); } - bool has(const K& k) const noexcept( - noexcept(std::declval>().has(k))) - { + bool has(const K& k) const noexcept { return keys_.has(k); } @@ -523,18 +516,14 @@ namespace ecs_hpp return values_[keys_.get_dense_index(k)]; } - T* find(const K& k) noexcept( - noexcept(std::declval>().find_dense_index(k))) - { + T* find(const K& k) noexcept { const auto value_index_p = keys_.find_dense_index(k); return value_index_p.second ? &values_[value_index_p.first] : nullptr; } - const T* find(const K& k) const noexcept( - noexcept(std::declval>().find_dense_index(k))) - { + const T* find(const K& k) const noexcept { const auto value_index_p = keys_.find_dense_index(k); return value_index_p.second ? &values_[value_index_p.first] @@ -708,8 +697,8 @@ namespace ecs_hpp entity_id id() const noexcept; - bool destroy(); entity clone() const; + bool destroy() noexcept; bool valid() const noexcept; template < typename T, typename... Args > @@ -1050,7 +1039,7 @@ namespace ecs_hpp entity create_entity(); entity create_entity(const const_uentity& prototype); - bool destroy_entity(const uentity& ent); + bool destroy_entity(const uentity& ent) noexcept; bool valid_entity(const const_uentity& ent) const noexcept; template < typename T, typename... Args > @@ -1253,14 +1242,14 @@ namespace ecs_hpp return id_; } - inline bool entity::destroy() { - return (*owner_).destroy_entity(id_); - } - inline entity entity::clone() const { return (*owner_).create_entity(id_); } + inline bool entity::destroy() noexcept { + return (*owner_).destroy_entity(id_); + } + inline bool entity::valid() const noexcept { return detail::as_const(*owner_).valid_entity(id_); } @@ -1713,11 +1702,18 @@ namespace ecs_hpp return wrap_entity(new_ent_id); } - if ( last_entity_id_ < detail::entity_id_index_mask ) { - entity_ids_.insert(last_entity_id_ + 1); - return wrap_entity(++last_entity_id_); + if ( last_entity_id_ >= detail::entity_id_index_mask ) { + throw std::logic_error("ecs_hpp::registry (entity index overlow)"); } - throw std::logic_error("ecs_hpp::registry (entity index overlow)"); + if ( free_entity_ids_.capacity() <= entity_ids_.size() ) { + // ensure free entity ids capacity for safe (noexcept) entity destroying + free_entity_ids_.reserve(detail::next_capacity_size( + free_entity_ids_.capacity(), + entity_ids_.size() + 1, + free_entity_ids_.max_size())); + } + entity_ids_.insert(last_entity_id_ + 1); + return wrap_entity(++last_entity_id_); } inline entity registry::create_entity(const const_uentity& prototype) { @@ -1734,10 +1730,11 @@ namespace ecs_hpp return ent; } - inline bool registry::destroy_entity(const uentity& ent) { + inline bool registry::destroy_entity(const uentity& ent) noexcept { assert(valid_entity(ent)); remove_all_components(ent); if ( entity_ids_.unordered_erase(ent) ) { + assert(free_entity_ids_.size() < free_entity_ids_.capacity()); free_entity_ids_.push_back(ent); return true; } diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 3d00d26..1809071 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -405,11 +405,15 @@ TEST_CASE("registry") { ecs::registry w; using namespace ecs::detail; + std::vector entities; for ( std::size_t i = 0; i < entity_id_index_mask; ++i ) { - w.create_entity(); + entities.push_back(w.create_entity()); } // entity index overflow REQUIRE_THROWS_AS(w.create_entity(), std::logic_error); + for ( auto& ent : entities ) { + ent.destroy(); + } } { ecs::registry w; From b7bbd986a0a665f6bd3c906f5d1280d2da94ba54 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 10 Apr 2019 23:23:21 +0700 Subject: [PATCH 6/7] add basic prototype functionality --- ecs.hpp | 243 ++++++++++++++++++++++++++++++++++++++++++++++++++ ecs_tests.cpp | 86 ++++++++++++++++++ 2 files changed, 329 insertions(+) diff --git a/ecs.hpp b/ecs.hpp index 143b571..aace988 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -157,6 +157,26 @@ namespace ecs_hpp return impl::tuple_contains_impl<0>(t, v); } + // + // tiny_tuple_apply + // + + namespace impl + { + template < typename F, typename Tuple, std::size_t... I > + void tiny_tuple_apply_impl(F&& f, Tuple&& args, std::index_sequence) { + std::forward(f)(std::get(std::forward(args))...); + } + } + + template < typename F, typename Tuple > + void tiny_tuple_apply(F&& f, Tuple&& args) { + impl::tiny_tuple_apply_impl( + std::forward(f), + std::forward(args), + std::make_index_sequence>::value>()); + } + // // next_capacity_size // @@ -317,6 +337,18 @@ namespace ecs_hpp sparse_set(const Indexer& indexer = Indexer()) : indexer_(indexer) {} + sparse_set(const sparse_set& other) = default; + sparse_set& operator=(const sparse_set& other) = default; + + sparse_set(sparse_set&& other) noexcept = default; + sparse_set& operator=(sparse_set&& other) noexcept = default; + + void swap(sparse_set& other) noexcept { + using std::swap; + swap(dense_, other.dense_); + swap(sparse_, other.sparse_); + } + template < typename UT > bool insert(UT&& v) { if ( has(v) ) { @@ -394,6 +426,11 @@ namespace ecs_hpp std::vector dense_; std::vector sparse_; }; + + template < typename T, typename Indexer > + void swap(sparse_set& l, sparse_set& r) noexcept { + l.swap(r); + } } } @@ -447,6 +484,18 @@ namespace ecs_hpp sparse_map(const Indexer& indexer = Indexer()) : keys_(indexer) {} + sparse_map(const sparse_map& other) = default; + sparse_map& operator=(const sparse_map& other) = default; + + sparse_map(sparse_map&& other) noexcept = default; + sparse_map& operator=(sparse_map&& other) noexcept = default; + + void swap(sparse_map& other) noexcept { + using std::swap; + swap(keys_, other.keys_); + swap(values_, other.values_); + } + template < typename UK, typename UT > bool insert(UK&& k, UT&& v) { if ( keys_.has(k) ) { @@ -541,6 +590,11 @@ namespace ecs_hpp sparse_set keys_; std::vector values_; }; + + template < typename K, typename T, typename Indexer > + void swap(sparse_map& l, sparse_map& r) noexcept { + l.swap(r); + } } } @@ -952,6 +1006,72 @@ namespace std }; } +// ----------------------------------------------------------------------------- +// +// prototype +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + namespace detail + { + class component_applier_base; + using component_applier_uptr = std::unique_ptr; + + class component_applier_base { + public: + virtual ~component_applier_base() = default; + virtual component_applier_uptr clone() const = 0; + virtual void apply(entity& ent, bool override) const = 0; + }; + + template < typename T, typename... Args > + class component_applier final : public component_applier_base { + public: + component_applier(std::tuple&& args); + component_applier(const std::tuple& args); + component_applier_uptr clone() const override; + void apply(entity& ent, bool override) const override; + private: + std::tuple args_; + }; + } + + class prototype final { + public: + prototype() = default; + ~prototype() noexcept = default; + + prototype(const prototype& other); + prototype& operator=(const prototype& other); + + prototype(prototype&& other) noexcept; + prototype& operator=(prototype&& other) noexcept; + + void clear() noexcept; + bool empty() const noexcept; + void swap(prototype& other) noexcept; + + template < typename T, typename... Args > + prototype& assign_component(Args&&... args) &; + + template < typename T, typename... Args > + prototype&& assign_component(Args&&... args) &&; + + prototype& merge(const prototype& other, bool override) &; + prototype&& merge(const prototype& other, bool override) &&; + + entity create_entity(registry& owner) const; + private: + detail::sparse_map< + family_id, + detail::component_applier_uptr> appliers_; + }; + + void swap(prototype& l, prototype& r) noexcept; +} + // ----------------------------------------------------------------------------- // // system @@ -1574,6 +1694,129 @@ namespace ecs_hpp } } +// ----------------------------------------------------------------------------- +// +// prototype impl +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + namespace detail + { + template < typename T, typename... Args > + component_applier::component_applier(std::tuple&& args) + : args_(std::move(args)) {} + + template < typename T, typename... Args > + component_applier::component_applier(const std::tuple& args) + : args_(args) {} + + template < typename T, typename... Args > + component_applier_uptr component_applier::clone() const { + return std::make_unique(args_); + } + + template < typename T, typename... Args > + void component_applier::apply(entity& ent, bool override) const { + detail::tiny_tuple_apply([&ent, override](const Args&... args){ + if ( override || !ent.exists_component() ) { + ent.assign_component(args...); + } + }, args_); + } + } + + inline prototype::prototype(const prototype& other) { + for ( const family_id id : other.appliers_ ) { + appliers_.insert(id, other.appliers_.get(id)->clone()); + } + } + + inline prototype& prototype::operator=(const prototype& other) { + if ( this != &other ) { + prototype p(other); + swap(p); + } + return *this; + } + + inline prototype::prototype(prototype&& other) noexcept + : appliers_(std::move(other.appliers_)) {} + + inline prototype& prototype::operator=(prototype&& other) noexcept { + if ( this != &other ) { + swap(other); + other.clear(); + } + return *this; + } + + inline void prototype::clear() noexcept { + appliers_.clear(); + } + + inline bool prototype::empty() const noexcept { + return appliers_.empty(); + } + + inline void prototype::swap(prototype& other) noexcept { + using std::swap; + swap(appliers_, other.appliers_); + } + + template < typename T, typename... Args > + prototype& prototype::assign_component(Args&&... args) & { + using applier_t = detail::component_applier< + T, + std::decay_t...>; + auto applier = std::make_unique( + std::make_tuple(std::forward(args)...)); + const auto family = detail::type_family::id(); + appliers_.emplace(family, std::move(applier)); + return *this; + } + + template < typename T, typename... Args > + prototype&& prototype::assign_component(Args&&... args) && { + assign_component(std::forward(args)...); + return std::move(*this); + } + + prototype& prototype::merge(const prototype& other, bool override) & { + for ( const auto family_id : other.appliers_ ) { + if ( override || !appliers_.has(family_id) ) { + appliers_.insert_or_assign( + family_id, + other.appliers_.get(family_id)->clone()); + } + } + return *this; + } + + prototype&& prototype::merge(const prototype& other, bool override) && { + merge(other, override); + return std::move(*this); + } + + inline entity prototype::create_entity(registry& owner) const { + auto ent = owner.create_entity(); + try { + for ( const auto family_id : appliers_ ) { + appliers_.get(family_id)->apply(ent, true); + } + } catch (...) { + owner.destroy_entity(ent); + throw; + } + return ent; + } + + inline void swap(prototype& l, prototype& r) noexcept { + l.swap(r); + } +} + // ----------------------------------------------------------------------------- // // registry impl diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 1809071..6d8eda5 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -514,6 +514,92 @@ TEST_CASE("registry") { REQUIRE_FALSE(c1.remove()); } } + SECTION("prototypes") { + { + ecs::prototype p; + p.assign_component(1, 2); + + ecs::registry w; + const auto e1 = p.create_entity(w); + const auto e2 = p.create_entity(w); + + REQUIRE(w.entity_count() == 2u); + REQUIRE(w.component_count() == 2u); + REQUIRE(w.component_count() == 0u); + + REQUIRE(e1.component_count() == 1u); + REQUIRE(e1.get_component() == position_c(1,2)); + + REQUIRE(e2.component_count() == 1u); + REQUIRE(e2.get_component() == position_c(1,2)); + } + { + const auto p = ecs::prototype() + .assign_component(1,2) + .assign_component(3,4); + + ecs::registry w; + const auto e1 = p.create_entity(w); + const auto e2 = p.create_entity(w); + + REQUIRE(w.entity_count() == 2u); + REQUIRE(w.component_count() == 2u); + REQUIRE(w.component_count() == 2u); + + REQUIRE(e1.component_count() == 2u); + REQUIRE(e1.get_component() == position_c(1,2)); + REQUIRE(e1.get_component() == velocity_c(3,4)); + + REQUIRE(e2.component_count() == 2u); + REQUIRE(e2.get_component() == position_c(1,2)); + REQUIRE(e2.get_component() == velocity_c(3,4)); + } + { + const auto p1 = ecs::prototype() + .assign_component(1,2) + .assign_component(3,4); + + ecs::prototype p2 = p1; + ecs::prototype p3; + p3 = p2; + + ecs::registry w; + const auto e3 = p3.create_entity(w); + REQUIRE(e3.get_component() == position_c(1,2)); + REQUIRE(e3.get_component() == velocity_c(3,4)); + } + { + const auto p1 = ecs::prototype() + .assign_component(1,2) + .merge(ecs::prototype().assign_component(3,4), false); + + const auto p2 = ecs::prototype() + .assign_component(1,2) + .merge(ecs::prototype().assign_component(3,4), true); + + const auto p3 = ecs::prototype() + .assign_component(1,2) + .merge(ecs::prototype().assign_component(3,4), false); + + const auto p4 = ecs::prototype() + .assign_component(1,2) + .merge(ecs::prototype().assign_component(3,4), true); + + ecs::registry w; + + const auto e1 = p1.create_entity(w); + REQUIRE(e1.get_component() == position_c(1,2)); + const auto e2 = p2.create_entity(w); + REQUIRE(e2.get_component() == position_c(3,4)); + + const auto e3 = p3.create_entity(w); + REQUIRE(e3.get_component() == position_c(1,2)); + REQUIRE(e3.get_component() == velocity_c(3,4)); + const auto e4 = p4.create_entity(w); + REQUIRE(e4.get_component() == position_c(1,2)); + REQUIRE(e4.get_component() == velocity_c(3,4)); + } + } SECTION("component_assigning") { { ecs::registry w; From 73896bdd242433f3a842270ded583f3ee6fac69e Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 12 Apr 2019 04:22:02 +0700 Subject: [PATCH 7/7] registry::create_entity from prototype --- ecs.hpp | 70 +++++++++++++++++++++++++++++++-------------------- ecs_tests.cpp | 31 ++++++++++++----------- 2 files changed, 59 insertions(+), 42 deletions(-) diff --git a/ecs.hpp b/ecs.hpp index aace988..b983501 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -37,9 +37,14 @@ namespace ecs_hpp template < typename T > class const_component; + class prototype; + class system; class registry; + class entity_filler; + class registry_filler; + using family_id = std::uint16_t; using entity_id = std::uint32_t; using priority_t = std::int32_t; @@ -427,8 +432,12 @@ namespace ecs_hpp std::vector sparse_; }; - template < typename T, typename Indexer > - void swap(sparse_set& l, sparse_set& r) noexcept { + template < typename T + , typename Indexer > + void swap( + sparse_set& l, + sparse_set& r) noexcept + { l.swap(r); } } @@ -591,8 +600,13 @@ namespace ecs_hpp std::vector values_; }; - template < typename K, typename T, typename Indexer > - void swap(sparse_map& l, sparse_map& r) noexcept { + template < typename K + , typename T + , typename Indexer > + void swap( + sparse_map& l, + sparse_map& r) noexcept + { l.swap(r); } } @@ -752,7 +766,7 @@ namespace ecs_hpp entity_id id() const noexcept; entity clone() const; - bool destroy() noexcept; + void destroy() noexcept; bool valid() const noexcept; template < typename T, typename... Args > @@ -1055,14 +1069,13 @@ namespace ecs_hpp template < typename T, typename... Args > prototype& assign_component(Args&&... args) &; - template < typename T, typename... Args > prototype&& assign_component(Args&&... args) &&; prototype& merge(const prototype& other, bool override) &; prototype&& merge(const prototype& other, bool override) &&; - entity create_entity(registry& owner) const; + void apply(entity& ent, bool override) const; private: detail::sparse_map< family_id, @@ -1157,9 +1170,10 @@ namespace ecs_hpp const_component wrap_component(const const_uentity& ent) const noexcept; entity create_entity(); - entity create_entity(const const_uentity& prototype); + entity create_entity(const prototype& proto); + entity create_entity(const const_uentity& proto); - bool destroy_entity(const uentity& ent) noexcept; + void destroy_entity(const uentity& ent) noexcept; bool valid_entity(const const_uentity& ent) const noexcept; template < typename T, typename... Args > @@ -1366,8 +1380,8 @@ namespace ecs_hpp return (*owner_).create_entity(id_); } - inline bool entity::destroy() noexcept { - return (*owner_).destroy_entity(id_); + inline void entity::destroy() noexcept { + (*owner_).destroy_entity(id_); } inline bool entity::valid() const noexcept { @@ -1799,17 +1813,10 @@ namespace ecs_hpp return std::move(*this); } - inline entity prototype::create_entity(registry& owner) const { - auto ent = owner.create_entity(); - try { - for ( const auto family_id : appliers_ ) { - appliers_.get(family_id)->apply(ent, true); - } - } catch (...) { - owner.destroy_entity(ent); - throw; + inline void prototype::apply(entity& ent, bool override) const { + for ( const auto family_id : appliers_ ) { + appliers_.get(family_id)->apply(ent, override); } - return ent; } inline void swap(prototype& l, prototype& r) noexcept { @@ -1959,12 +1966,23 @@ namespace ecs_hpp return wrap_entity(++last_entity_id_); } - inline entity registry::create_entity(const const_uentity& prototype) { - assert(valid_entity(prototype)); + inline entity registry::create_entity(const prototype& proto) { + auto ent = create_entity(); + try { + proto.apply(ent, true); + } catch (...) { + destroy_entity(ent); + throw; + } + return ent; + } + + inline entity registry::create_entity(const const_uentity& proto) { + assert(valid_entity(proto)); entity ent = create_entity(); try { for ( const auto family_id : storages_ ) { - storages_.get(family_id)->clone(prototype, ent.id()); + storages_.get(family_id)->clone(proto, ent.id()); } } catch (...) { destroy_entity(ent); @@ -1973,15 +1991,13 @@ namespace ecs_hpp return ent; } - inline bool registry::destroy_entity(const uentity& ent) noexcept { + inline void registry::destroy_entity(const uentity& ent) noexcept { assert(valid_entity(ent)); remove_all_components(ent); if ( entity_ids_.unordered_erase(ent) ) { assert(free_entity_ids_.size() < free_entity_ids_.capacity()); free_entity_ids_.push_back(ent); - return true; } - return false; } inline bool registry::valid_entity(const const_uentity& ent) const noexcept { diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 6d8eda5..9299464 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -118,6 +118,7 @@ TEST_CASE("detail") { REQUIRE_THROWS(s.get_dense_index(42u)); REQUIRE(s.insert(42u)); + REQUIRE_FALSE(s.insert(42u)); REQUIRE_FALSE(s.empty()); REQUIRE(s.size() == 1u); @@ -355,15 +356,15 @@ TEST_CASE("registry") { REQUIRE(w.valid_entity(e3)); REQUIRE(w.valid_entity(ee3)); - REQUIRE(w.destroy_entity(e1)); + w.destroy_entity(e1); REQUIRE_FALSE(w.valid_entity(e1)); REQUIRE(w.valid_entity(e2)); - REQUIRE(w.destroy_entity(e2)); + w.destroy_entity(e2); REQUIRE_FALSE(w.valid_entity(e1)); REQUIRE_FALSE(w.valid_entity(e2)); - REQUIRE(w.destroy_entity(ee3)); + w.destroy_entity(ee3); REQUIRE_FALSE(w.valid_entity(e3)); REQUIRE_FALSE(w.valid_entity(ee3)); } @@ -520,8 +521,8 @@ TEST_CASE("registry") { p.assign_component(1, 2); ecs::registry w; - const auto e1 = p.create_entity(w); - const auto e2 = p.create_entity(w); + const auto e1 = w.create_entity(p); + const auto e2 = w.create_entity(p); REQUIRE(w.entity_count() == 2u); REQUIRE(w.component_count() == 2u); @@ -539,8 +540,8 @@ TEST_CASE("registry") { .assign_component(3,4); ecs::registry w; - const auto e1 = p.create_entity(w); - const auto e2 = p.create_entity(w); + const auto e1 = w.create_entity(p); + const auto e2 = w.create_entity(p); REQUIRE(w.entity_count() == 2u); REQUIRE(w.component_count() == 2u); @@ -564,7 +565,7 @@ TEST_CASE("registry") { p3 = p2; ecs::registry w; - const auto e3 = p3.create_entity(w); + const auto e3 = w.create_entity(p3); REQUIRE(e3.get_component() == position_c(1,2)); REQUIRE(e3.get_component() == velocity_c(3,4)); } @@ -587,15 +588,15 @@ TEST_CASE("registry") { ecs::registry w; - const auto e1 = p1.create_entity(w); + const auto e1 = w.create_entity(p1); REQUIRE(e1.get_component() == position_c(1,2)); - const auto e2 = p2.create_entity(w); + const auto e2 = w.create_entity(p2); REQUIRE(e2.get_component() == position_c(3,4)); - const auto e3 = p3.create_entity(w); + const auto e3 = w.create_entity(p3); REQUIRE(e3.get_component() == position_c(1,2)); REQUIRE(e3.get_component() == velocity_c(3,4)); - const auto e4 = p4.create_entity(w); + const auto e4 = w.create_entity(p4); REQUIRE(e4.get_component() == position_c(1,2)); REQUIRE(e4.get_component() == velocity_c(3,4)); } @@ -654,7 +655,7 @@ TEST_CASE("registry") { REQUIRE(e1.exists_component()); REQUIRE(e1.exists_component()); - REQUIRE(e1.destroy()); + e1.destroy(); REQUIRE_FALSE(w.component_count()); REQUIRE_FALSE(w.component_count()); @@ -672,7 +673,7 @@ TEST_CASE("registry") { w.assign_component(e2); w.assign_component(e2); - REQUIRE(w.destroy_entity(e1)); + w.destroy_entity(e1); REQUIRE(w.exists_component(e2)); REQUIRE(w.exists_component(e2)); @@ -691,7 +692,7 @@ TEST_CASE("registry") { REQUIRE(&e2_pos == &w.get_component(e2)); REQUIRE(&e2_vel == &w.get_component(e2)); - REQUIRE(e1.destroy()); + e1.destroy(); } } SECTION("component_accessing") {