Skip to content

Commit 29d8c37

Browse files
committed
Fix a bunch of constness issues (fixes #95).
- Use SFINAE to prevent non-const access to component<C>(). - Correctly de-const component types before accessing Component<C>::family(). Avoids accidentally assigning new family IDs. - ComponentHandle should handle const propagation correctly now. - ComponentHandle.manager_ should now be `const EntityManager` where appropriate.
1 parent 26f02d3 commit 29d8c37

File tree

2 files changed

+81
-61
lines changed

2 files changed

+81
-61
lines changed

entityx/Entity.h

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <string>
2727
#include <utility>
2828
#include <vector>
29+
#include <type_traits>
2930

3031
#include "entityx/help/Pool.h"
3132
#include "entityx/config.h"
@@ -40,7 +41,7 @@ typedef std::uint64_t uint64_t;
4041
class EntityManager;
4142

4243

43-
template <typename C>
44+
template <typename C, typename EM = EntityManager>
4445
class ComponentHandle;
4546

4647
/** A convenience handle around an Entity::Id.
@@ -136,17 +137,17 @@ class Entity {
136137
template <typename C>
137138
void remove();
138139

139-
template <typename C>
140+
template <typename C, typename = typename std::enable_if<!std::is_const<C>::value>::type>
140141
ComponentHandle<C> component();
141142

142-
template <typename C>
143-
const ComponentHandle<const C> component() const;
143+
template <typename C, typename = typename std::enable_if<std::is_const<C>::value>::type>
144+
const ComponentHandle<C, const EntityManager> component() const;
144145

145146
template <typename ... Components>
146147
std::tuple<ComponentHandle<Components>...> components();
147148

148149
template <typename ... Components>
149-
std::tuple<ComponentHandle<const Components>...> components() const;
150+
std::tuple<ComponentHandle<const Components, const EntityManager>...> components() const;
150151

151152
template <typename C>
152153
bool has_component() const;
@@ -176,7 +177,7 @@ class Entity {
176177
* - If a component is removed from its host entity.
177178
* - If its host entity is destroyed.
178179
*/
179-
template <typename C>
180+
template <typename C, typename EM>
180181
class ComponentHandle {
181182
public:
182183
typedef C ComponentType;
@@ -208,12 +209,10 @@ class ComponentHandle {
208209
private:
209210
friend class EntityManager;
210211

211-
ComponentHandle(EntityManager *manager, Entity::Id id) :
212+
ComponentHandle(EM *manager, Entity::Id id) :
212213
manager_(manager), id_(id) {}
213-
ComponentHandle(const EntityManager *manager, Entity::Id id) :
214-
manager_(const_cast<EntityManager*>(manager)), id_(id) {}
215214

216-
EntityManager *manager_;
215+
EM *manager_;
217216
Entity::Id id_;
218217
};
219218

@@ -268,7 +267,10 @@ template <typename Derived>
268267
struct Component : public BaseComponent {
269268
public:
270269
typedef ComponentHandle<Derived> Handle;
270+
typedef ComponentHandle<const Derived, const EntityManager> ConstHandle;
271271

272+
private:
273+
friend class EntityManager;
272274
/// Used internally for registration.
273275
static Family family();
274276
};
@@ -416,7 +418,7 @@ class EntityManager : entityx::help::NonCopyable {
416418
private:
417419
friend class EntityManager;
418420

419-
BaseView(EntityManager *manager) : manager_(manager) { mask_.set(); }
421+
explicit BaseView(EntityManager *manager) : manager_(manager) { mask_.set(); }
420422
BaseView(EntityManager *manager, ComponentMask mask) :
421423
manager_(manager), mask_(mask) {}
422424

@@ -431,7 +433,7 @@ class EntityManager : entityx::help::NonCopyable {
431433
class UnpackingView {
432434
public:
433435
struct Unpacker {
434-
Unpacker(ComponentHandle<Components> & ... handles) :
436+
explicit Unpacker(ComponentHandle<Components> & ... handles) :
435437
handles(std::tuple<ComponentHandle<Components> & ...>(handles...)) {}
436438

437439
void unpack(entityx::Entity &entity) const {
@@ -502,7 +504,7 @@ class EntityManager : entityx::help::NonCopyable {
502504
/**
503505
* Return true if the given entity ID is still valid.
504506
*/
505-
bool valid(Entity::Id id) {
507+
bool valid(Entity::Id id) const {
506508
return id.index() < entity_version_.size() && entity_version_[id.index()] == id.version();
507509
}
508510

@@ -572,7 +574,7 @@ class EntityManager : entityx::help::NonCopyable {
572574
template <typename C, typename ... Args>
573575
ComponentHandle<C> assign(Entity::Id id, Args && ... args) {
574576
assert_valid(id);
575-
const BaseComponent::Family family = Component<C>::family();
577+
const BaseComponent::Family family = component_family<C>();
576578
assert(!entity_component_mask_[id.index()].test(family));
577579

578580
// Placement new into the component pool.
@@ -596,7 +598,7 @@ class EntityManager : entityx::help::NonCopyable {
596598
template <typename C>
597599
void remove(Entity::Id id) {
598600
assert_valid(id);
599-
const BaseComponent::Family family = Component<C>::family();
601+
const BaseComponent::Family family = component_family<C>();
600602
const uint32_t index = id.index();
601603

602604
// Find the pool for this component family.
@@ -617,7 +619,7 @@ class EntityManager : entityx::help::NonCopyable {
617619
template <typename C>
618620
bool has_component(Entity::Id id) const {
619621
assert_valid(id);
620-
size_t family = Component<C>::family();
622+
size_t family = component_family<C>();
621623
// We don't bother checking the component mask, as we return a nullptr anyway.
622624
if (family >= component_pools_.size())
623625
return false;
@@ -632,10 +634,10 @@ class EntityManager : entityx::help::NonCopyable {
632634
*
633635
* @returns Pointer to an instance of C, or nullptr if the Entity::Id does not have that Component.
634636
*/
635-
template <typename C>
637+
template <typename C, typename = typename std::enable_if<!std::is_const<C>::value>::type>
636638
ComponentHandle<C> component(Entity::Id id) {
637639
assert_valid(id);
638-
size_t family = Component<C>::family();
640+
size_t family = component_family<C>();
639641
// We don't bother checking the component mask, as we return a nullptr anyway.
640642
if (family >= component_pools_.size())
641643
return ComponentHandle<C>();
@@ -650,17 +652,17 @@ class EntityManager : entityx::help::NonCopyable {
650652
*
651653
* @returns Component instance, or nullptr if the Entity::Id does not have that Component.
652654
*/
653-
template <typename C>
654-
const ComponentHandle<const C> component(Entity::Id id) const {
655+
template <typename C, typename = typename std::enable_if<std::is_const<C>::value>::type>
656+
const ComponentHandle<C, const EntityManager> component(Entity::Id id) const {
655657
assert_valid(id);
656-
size_t family = Component<C>::family();
658+
size_t family = component_family<C>();
657659
// We don't bother checking the component mask, as we return a nullptr anyway.
658660
if (family >= component_pools_.size())
659-
return ComponentHandle<const C>();
661+
return ComponentHandle<C, const EntityManager>();
660662
BasePool *pool = component_pools_[family];
661663
if (!pool || !entity_component_mask_[id.index()][family])
662-
return ComponentHandle<const C>();
663-
return ComponentHandle<const C>(this, id);
664+
return ComponentHandle<C, const EntityManager>();
665+
return ComponentHandle<C, const EntityManager>(this, id);
664666
}
665667

666668
template <typename ... Components>
@@ -669,7 +671,7 @@ class EntityManager : entityx::help::NonCopyable {
669671
}
670672

671673
template <typename ... Components>
672-
std::tuple<ComponentHandle<const Components>...> components(Entity::Id id) const {
674+
std::tuple<ComponentHandle<const Components, const EntityManager>...> components(Entity::Id id) const {
673675
return std::make_tuple(component<const Components>(id)...);
674676
}
675677

@@ -751,11 +753,18 @@ class EntityManager : entityx::help::NonCopyable {
751753
*/
752754
void reset();
753755

756+
// Retrieve the component family for a type.
757+
template <typename C>
758+
static BaseComponent::Family component_family() {
759+
return Component<typename std::remove_const<C>::type>::family();
760+
}
761+
754762
private:
755763
friend class Entity;
756-
template <typename C>
764+
template <typename C, typename EM>
757765
friend class ComponentHandle;
758766

767+
759768
inline void assert_valid(Entity::Id id) const {
760769
assert(id.index() < entity_component_mask_.size() && "Entity::Id ID outside entity vector range");
761770
assert(entity_version_[id.index()] == id.version() && "Attempt to access Entity via a stale Entity::Id");
@@ -764,15 +773,15 @@ class EntityManager : entityx::help::NonCopyable {
764773
template <typename C>
765774
C *get_component_ptr(Entity::Id id) {
766775
assert(valid(id));
767-
BasePool *pool = component_pools_[Component<C>::family()];
776+
BasePool *pool = component_pools_[component_family<C>()];
768777
assert(pool);
769778
return static_cast<C*>(pool->get(id.index()));
770779
}
771780

772781
template <typename C>
773782
const C *get_component_ptr(Entity::Id id) const {
774783
assert_valid(id);
775-
BasePool *pool = component_pools_[Component<C>::family()];
784+
BasePool *pool = component_pools_[component_family<C>()];
776785
assert(pool);
777786
return static_cast<const C*>(pool->get(id.index()));
778787
}
@@ -785,7 +794,7 @@ class EntityManager : entityx::help::NonCopyable {
785794
template <typename C>
786795
ComponentMask component_mask() {
787796
ComponentMask mask;
788-
mask.set(Component<C>::family());
797+
mask.set(component_family<C>());
789798
return mask;
790799
}
791800

@@ -815,7 +824,7 @@ class EntityManager : entityx::help::NonCopyable {
815824

816825
template <typename C>
817826
Pool<C> *accomodate_component() {
818-
BaseComponent::Family family = Component<C>::family();
827+
BaseComponent::Family family = component_family<C>();
819828
if (component_pools_.size() <= family) {
820829
component_pools_.resize(family + 1, nullptr);
821830
}
@@ -869,8 +878,7 @@ ComponentHandle<C> Entity::replace(Args && ... args) {
869878
auto handle = component<C>();
870879
if (handle) {
871880
*(handle.get()) = C(std::forward<Args>(args) ...);
872-
}
873-
else {
881+
} else {
874882
handle = manager_->assign<C>(id_, std::forward<Args>(args) ...);
875883
}
876884
return handle;
@@ -882,16 +890,16 @@ void Entity::remove() {
882890
manager_->remove<C>(id_);
883891
}
884892

885-
template <typename C>
893+
template <typename C, typename>
886894
ComponentHandle<C> Entity::component() {
887895
assert(valid());
888896
return manager_->component<C>(id_);
889897
}
890898

891-
template <typename C>
892-
const ComponentHandle<const C> Entity::component() const {
899+
template <typename C, typename>
900+
const ComponentHandle<C, const EntityManager> Entity::component() const {
893901
assert(valid());
894-
return manager_->component<const C>(id_);
902+
return const_cast<const EntityManager*>(manager_)->component<const C>(id_);
895903
}
896904

897905
template <typename ... Components>
@@ -901,9 +909,9 @@ std::tuple<ComponentHandle<Components>...> Entity::components() {
901909
}
902910

903911
template <typename ... Components>
904-
std::tuple<ComponentHandle<const Components>...> Entity::components() const {
912+
std::tuple<ComponentHandle<const Components, const EntityManager>...> Entity::components() const {
905913
assert(valid());
906-
return manager_->components<const Components...>(id_);
914+
return const_cast<const EntityManager*>(manager_)->components<const Components...>(id_);
907915
}
908916

909917

@@ -935,44 +943,44 @@ inline std::ostream &operator << (std::ostream &out, const Entity &entity) {
935943
}
936944

937945

938-
template <typename C>
939-
inline ComponentHandle<C>::operator bool() const {
946+
template <typename C, typename EM>
947+
inline ComponentHandle<C, EM>::operator bool() const {
940948
return valid();
941949
}
942950

943-
template <typename C>
944-
inline bool ComponentHandle<C>::valid() const {
945-
return manager_ && manager_->valid(id_) && manager_->has_component<C>(id_);
951+
template <typename C, typename EM>
952+
inline bool ComponentHandle<C, EM>::valid() const {
953+
return manager_ && manager_->valid(id_) && manager_->template has_component<C>(id_);
946954
}
947955

948-
template <typename C>
949-
inline C *ComponentHandle<C>::operator -> () {
956+
template <typename C, typename EM>
957+
inline C *ComponentHandle<C, EM>::operator -> () {
950958
assert(valid());
951-
return manager_->get_component_ptr<C>(id_);
959+
return manager_->template get_component_ptr<C>(id_);
952960
}
953961

954-
template <typename C>
955-
inline const C *ComponentHandle<C>::operator -> () const {
962+
template <typename C, typename EM>
963+
inline const C *ComponentHandle<C, EM>::operator -> () const {
956964
assert(valid());
957-
return manager_->get_component_ptr<C>(id_);
965+
return manager_->template get_component_ptr<C>(id_);
958966
}
959967

960-
template <typename C>
961-
inline C *ComponentHandle<C>::get() {
968+
template <typename C, typename EM>
969+
inline C *ComponentHandle<C, EM>::get() {
962970
assert(valid());
963-
return manager_->get_component_ptr<C>(id_);
971+
return manager_->template get_component_ptr<C>(id_);
964972
}
965973

966-
template <typename C>
967-
inline const C *ComponentHandle<C>::get() const {
974+
template <typename C, typename EM>
975+
inline const C *ComponentHandle<C, EM>::get() const {
968976
assert(valid());
969-
return manager_->get_component_ptr<C>(id_);
977+
return manager_->template get_component_ptr<C>(id_);
970978
}
971979

972-
template <typename C>
973-
inline void ComponentHandle<C>::remove() {
980+
template <typename C, typename EM>
981+
inline void ComponentHandle<C, EM>::remove() {
974982
assert(valid());
975-
manager_->remove<C>(id_);
983+
manager_->template remove<C>(id_);
976984
}
977985

978986

entityx/Entity_test.cc

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ TEST_CASE_METHOD(EntityManagerFixture, "TestUnpack") {
290290
// }
291291

292292
TEST_CASE_METHOD(EntityManagerFixture, "TestComponentIdsDiffer") {
293-
REQUIRE(Component<Position>::family() != Component<Direction>::family());
293+
REQUIRE(EntityManager::component_family<Position>() != EntityManager::component_family<Direction>());
294294
}
295295

296296
TEST_CASE_METHOD(EntityManagerFixture, "TestEntityCreatedEvent") {
@@ -542,7 +542,7 @@ TEST_CASE("TestComponentDestructorCalledWhenManagerDestroyed") {
542542
};
543543

544544
struct Test : Component<Test> {
545-
Test(bool &yes) : freed(yes) {}
545+
explicit Test(bool &yes) : freed(yes) {}
546546

547547
Freed freed;
548548
};
@@ -565,7 +565,7 @@ TEST_CASE("TestComponentDestructorCalledWhenEntityDestroyed") {
565565
};
566566

567567
struct Test : Component<Test> {
568-
Test(bool &yes) : freed(yes) {}
568+
explicit Test(bool &yes) : freed(yes) {}
569569

570570
Freed freed;
571571
};
@@ -592,3 +592,15 @@ TEST_CASE_METHOD(EntityManagerFixture, "TestComponentsRemovedFromReusedEntities"
592592
REQUIRE(!b.has_component<Position>());
593593
b.assign<Position>(3, 4);
594594
}
595+
596+
TEST_CASE_METHOD(EntityManagerFixture, "TestConstComponentsNotInstantiatedTwice") {
597+
Entity a = em.create();
598+
a.assign<Position>(1, 2);
599+
600+
const Entity b = a;
601+
602+
REQUIRE(a.component<Position>().valid());
603+
REQUIRE(b.component<const Position>().valid());
604+
REQUIRE(b.component<const Position>()->x == 1);
605+
REQUIRE(b.component<const Position>()->y == 2);
606+
}

0 commit comments

Comments
 (0)