From 29d8c3763c27e5cc18695f8261145c836670efd7 Mon Sep 17 00:00:00 2001 From: Alec Thomas Date: Fri, 24 Apr 2015 11:47:33 +1000 Subject: [PATCH] Fix a bunch of constness issues (fixes #95). - Use SFINAE to prevent non-const access to component(). - Correctly de-const component types before accessing Component::family(). Avoids accidentally assigning new family IDs. - ComponentHandle should handle const propagation correctly now. - ComponentHandle.manager_ should now be `const EntityManager` where appropriate. --- entityx/Entity.h | 124 ++++++++++++++++++++++------------------- entityx/Entity_test.cc | 18 +++++- 2 files changed, 81 insertions(+), 61 deletions(-) diff --git a/entityx/Entity.h b/entityx/Entity.h index 8dd3410..a19d971 100644 --- a/entityx/Entity.h +++ b/entityx/Entity.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "entityx/help/Pool.h" #include "entityx/config.h" @@ -40,7 +41,7 @@ typedef std::uint64_t uint64_t; class EntityManager; -template +template class ComponentHandle; /** A convenience handle around an Entity::Id. @@ -136,17 +137,17 @@ class Entity { template void remove(); - template + template ::value>::type> ComponentHandle component(); - template - const ComponentHandle component() const; + template ::value>::type> + const ComponentHandle component() const; template std::tuple...> components(); template - std::tuple...> components() const; + std::tuple...> components() const; template bool has_component() const; @@ -176,7 +177,7 @@ class Entity { * - If a component is removed from its host entity. * - If its host entity is destroyed. */ -template +template class ComponentHandle { public: typedef C ComponentType; @@ -208,12 +209,10 @@ class ComponentHandle { private: friend class EntityManager; - ComponentHandle(EntityManager *manager, Entity::Id id) : + ComponentHandle(EM *manager, Entity::Id id) : manager_(manager), id_(id) {} - ComponentHandle(const EntityManager *manager, Entity::Id id) : - manager_(const_cast(manager)), id_(id) {} - EntityManager *manager_; + EM *manager_; Entity::Id id_; }; @@ -268,7 +267,10 @@ template struct Component : public BaseComponent { public: typedef ComponentHandle Handle; + typedef ComponentHandle ConstHandle; +private: + friend class EntityManager; /// Used internally for registration. static Family family(); }; @@ -416,7 +418,7 @@ class EntityManager : entityx::help::NonCopyable { private: friend class EntityManager; - BaseView(EntityManager *manager) : manager_(manager) { mask_.set(); } + explicit BaseView(EntityManager *manager) : manager_(manager) { mask_.set(); } BaseView(EntityManager *manager, ComponentMask mask) : manager_(manager), mask_(mask) {} @@ -431,7 +433,7 @@ class EntityManager : entityx::help::NonCopyable { class UnpackingView { public: struct Unpacker { - Unpacker(ComponentHandle & ... handles) : + explicit Unpacker(ComponentHandle & ... handles) : handles(std::tuple & ...>(handles...)) {} void unpack(entityx::Entity &entity) const { @@ -502,7 +504,7 @@ class EntityManager : entityx::help::NonCopyable { /** * Return true if the given entity ID is still valid. */ - bool valid(Entity::Id id) { + bool valid(Entity::Id id) const { return id.index() < entity_version_.size() && entity_version_[id.index()] == id.version(); } @@ -572,7 +574,7 @@ class EntityManager : entityx::help::NonCopyable { template ComponentHandle assign(Entity::Id id, Args && ... args) { assert_valid(id); - const BaseComponent::Family family = Component::family(); + const BaseComponent::Family family = component_family(); assert(!entity_component_mask_[id.index()].test(family)); // Placement new into the component pool. @@ -596,7 +598,7 @@ class EntityManager : entityx::help::NonCopyable { template void remove(Entity::Id id) { assert_valid(id); - const BaseComponent::Family family = Component::family(); + const BaseComponent::Family family = component_family(); const uint32_t index = id.index(); // Find the pool for this component family. @@ -617,7 +619,7 @@ class EntityManager : entityx::help::NonCopyable { template bool has_component(Entity::Id id) const { assert_valid(id); - size_t family = Component::family(); + size_t family = component_family(); // We don't bother checking the component mask, as we return a nullptr anyway. if (family >= component_pools_.size()) return false; @@ -632,10 +634,10 @@ class EntityManager : entityx::help::NonCopyable { * * @returns Pointer to an instance of C, or nullptr if the Entity::Id does not have that Component. */ - template + template ::value>::type> ComponentHandle component(Entity::Id id) { assert_valid(id); - size_t family = Component::family(); + size_t family = component_family(); // We don't bother checking the component mask, as we return a nullptr anyway. if (family >= component_pools_.size()) return ComponentHandle(); @@ -650,17 +652,17 @@ class EntityManager : entityx::help::NonCopyable { * * @returns Component instance, or nullptr if the Entity::Id does not have that Component. */ - template - const ComponentHandle component(Entity::Id id) const { + template ::value>::type> + const ComponentHandle component(Entity::Id id) const { assert_valid(id); - size_t family = Component::family(); + size_t family = component_family(); // We don't bother checking the component mask, as we return a nullptr anyway. if (family >= component_pools_.size()) - return ComponentHandle(); + return ComponentHandle(); BasePool *pool = component_pools_[family]; if (!pool || !entity_component_mask_[id.index()][family]) - return ComponentHandle(); - return ComponentHandle(this, id); + return ComponentHandle(); + return ComponentHandle(this, id); } template @@ -669,7 +671,7 @@ class EntityManager : entityx::help::NonCopyable { } template - std::tuple...> components(Entity::Id id) const { + std::tuple...> components(Entity::Id id) const { return std::make_tuple(component(id)...); } @@ -751,11 +753,18 @@ class EntityManager : entityx::help::NonCopyable { */ void reset(); + // Retrieve the component family for a type. + template + static BaseComponent::Family component_family() { + return Component::type>::family(); + } + private: friend class Entity; - template + template friend class ComponentHandle; + inline void assert_valid(Entity::Id id) const { assert(id.index() < entity_component_mask_.size() && "Entity::Id ID outside entity vector range"); assert(entity_version_[id.index()] == id.version() && "Attempt to access Entity via a stale Entity::Id"); @@ -764,7 +773,7 @@ class EntityManager : entityx::help::NonCopyable { template C *get_component_ptr(Entity::Id id) { assert(valid(id)); - BasePool *pool = component_pools_[Component::family()]; + BasePool *pool = component_pools_[component_family()]; assert(pool); return static_cast(pool->get(id.index())); } @@ -772,7 +781,7 @@ class EntityManager : entityx::help::NonCopyable { template const C *get_component_ptr(Entity::Id id) const { assert_valid(id); - BasePool *pool = component_pools_[Component::family()]; + BasePool *pool = component_pools_[component_family()]; assert(pool); return static_cast(pool->get(id.index())); } @@ -785,7 +794,7 @@ class EntityManager : entityx::help::NonCopyable { template ComponentMask component_mask() { ComponentMask mask; - mask.set(Component::family()); + mask.set(component_family()); return mask; } @@ -815,7 +824,7 @@ class EntityManager : entityx::help::NonCopyable { template Pool *accomodate_component() { - BaseComponent::Family family = Component::family(); + BaseComponent::Family family = component_family(); if (component_pools_.size() <= family) { component_pools_.resize(family + 1, nullptr); } @@ -869,8 +878,7 @@ ComponentHandle Entity::replace(Args && ... args) { auto handle = component(); if (handle) { *(handle.get()) = C(std::forward(args) ...); - } - else { + } else { handle = manager_->assign(id_, std::forward(args) ...); } return handle; @@ -882,16 +890,16 @@ void Entity::remove() { manager_->remove(id_); } -template +template ComponentHandle Entity::component() { assert(valid()); return manager_->component(id_); } -template -const ComponentHandle Entity::component() const { + template +const ComponentHandle Entity::component() const { assert(valid()); - return manager_->component(id_); + return const_cast(manager_)->component(id_); } template @@ -901,9 +909,9 @@ std::tuple...> Entity::components() { } template -std::tuple...> Entity::components() const { +std::tuple...> Entity::components() const { assert(valid()); - return manager_->components(id_); + return const_cast(manager_)->components(id_); } @@ -935,44 +943,44 @@ inline std::ostream &operator << (std::ostream &out, const Entity &entity) { } -template -inline ComponentHandle::operator bool() const { +template +inline ComponentHandle::operator bool() const { return valid(); } -template -inline bool ComponentHandle::valid() const { - return manager_ && manager_->valid(id_) && manager_->has_component(id_); +template +inline bool ComponentHandle::valid() const { + return manager_ && manager_->valid(id_) && manager_->template has_component(id_); } -template -inline C *ComponentHandle::operator -> () { +template +inline C *ComponentHandle::operator -> () { assert(valid()); - return manager_->get_component_ptr(id_); + return manager_->template get_component_ptr(id_); } -template -inline const C *ComponentHandle::operator -> () const { +template +inline const C *ComponentHandle::operator -> () const { assert(valid()); - return manager_->get_component_ptr(id_); + return manager_->template get_component_ptr(id_); } -template -inline C *ComponentHandle::get() { +template +inline C *ComponentHandle::get() { assert(valid()); - return manager_->get_component_ptr(id_); + return manager_->template get_component_ptr(id_); } -template -inline const C *ComponentHandle::get() const { +template +inline const C *ComponentHandle::get() const { assert(valid()); - return manager_->get_component_ptr(id_); + return manager_->template get_component_ptr(id_); } -template -inline void ComponentHandle::remove() { +template +inline void ComponentHandle::remove() { assert(valid()); - manager_->remove(id_); + manager_->template remove(id_); } diff --git a/entityx/Entity_test.cc b/entityx/Entity_test.cc index 7ea4231..f5667e2 100644 --- a/entityx/Entity_test.cc +++ b/entityx/Entity_test.cc @@ -290,7 +290,7 @@ TEST_CASE_METHOD(EntityManagerFixture, "TestUnpack") { // } TEST_CASE_METHOD(EntityManagerFixture, "TestComponentIdsDiffer") { - REQUIRE(Component::family() != Component::family()); + REQUIRE(EntityManager::component_family() != EntityManager::component_family()); } TEST_CASE_METHOD(EntityManagerFixture, "TestEntityCreatedEvent") { @@ -542,7 +542,7 @@ TEST_CASE("TestComponentDestructorCalledWhenManagerDestroyed") { }; struct Test : Component { - Test(bool &yes) : freed(yes) {} + explicit Test(bool &yes) : freed(yes) {} Freed freed; }; @@ -565,7 +565,7 @@ TEST_CASE("TestComponentDestructorCalledWhenEntityDestroyed") { }; struct Test : Component { - Test(bool &yes) : freed(yes) {} + explicit Test(bool &yes) : freed(yes) {} Freed freed; }; @@ -592,3 +592,15 @@ TEST_CASE_METHOD(EntityManagerFixture, "TestComponentsRemovedFromReusedEntities" REQUIRE(!b.has_component()); b.assign(3, 4); } + +TEST_CASE_METHOD(EntityManagerFixture, "TestConstComponentsNotInstantiatedTwice") { + Entity a = em.create(); + a.assign(1, 2); + + const Entity b = a; + + REQUIRE(a.component().valid()); + REQUIRE(b.component().valid()); + REQUIRE(b.component()->x == 1); + REQUIRE(b.component()->y == 2); +}