Przeglądaj źródła

tag dispatching to disambiguate tags and components (#65)

Michele Caini 8 lat temu
rodzic
commit
820178f006

+ 7 - 6
README.md

@@ -578,7 +578,8 @@ data structures or more complex and movable data structures with a proper
 constructor.<br/>
 Actually, the same type can be used both as a tag and as a component and the
 registry will not complain about it. It is up to the users to properly manage
-their own types.
+their own types. In some cases, the `tag_type_t` must also be used in order to
+disambiguate overloads of member functions.
 
 Attaching tags to entities and removing them is trivial:
 
@@ -587,10 +588,10 @@ auto player = registry.create();
 auto camera = registry.create();
 
 // attaches a default-initialized tag to an entity
-registry.attach<PlayingCharacter>(player);
+registry.assign<PlayingCharacter>(entt::tag_type_t{}, player);
 
 // attaches a tag to an entity and initializes it
-registry.attach<Camera>(camera, player);
+registry.assign<Camera>(entt::tag_type_t{}, camera, player);
 
 // removes tags from their owners
 registry.remove<PlayingCharacter>();
@@ -598,12 +599,12 @@ registry.remove<Camera>();
 ```
 
 In case a tag already has an owner, its content can be updated by means of the
-`set` member function template and the ownership of the tag can be transferred
-to another entity using the `move` member function template:
+`replace` member function template and the ownership of the tag can be
+transferred to another entity using the `move` member function template:
 
 ```
 // replaces the content of the given tag
-Point &point = registry.set<Point>(1.f, 1.f);
+Point &point = registry.replace<Point>(entt::tag_type_t{}, 1.f, 1.f);
 
 // transfers the ownership of the tag to another entity
 entity_type prev = registry.move<Point>(next);

+ 0 - 1
TODO

@@ -5,6 +5,5 @@
 * define a macro for the noexcept policy, so as to provide users with an easy way to disable exception handling
 * define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
 * blueprint registry - kind of factory to create entitites template for initialization (use signals, it's trivial this way)
-* use tag dispatching to disambiguate between standard components and single instance components, then provide users with an common API
 * remove Actor::update (it's application dependent), allow tag instead
 * AOB

+ 132 - 131
src/entt/entity/registry.hpp

@@ -16,6 +16,7 @@
 #include "entt_traits.hpp"
 #include "snapshot.hpp"
 #include "sparse_set.hpp"
+#include "utility.hpp"
 #include "view.hpp"
 
 
@@ -182,7 +183,7 @@ public:
      * @return Runtime numeric identifier of the given type of tag.
      */
     template<typename Tag>
-    tag_type tag() const noexcept {
+    tag_type type(tag_type_t) const noexcept {
         return tag_family::type<Tag>();
     }
 
@@ -199,7 +200,7 @@ public:
      * @return Runtime numeric identifier of the given type of component.
      */
     template<typename Component>
-    component_type component() const noexcept {
+    component_type type() const noexcept {
         return component_family::type<Component>();
     }
 
@@ -430,7 +431,7 @@ public:
      * @return A reference to the newly created tag.
      */
     template<typename Tag, typename... Args>
-    Tag & attach(entity_type entity, Args &&... args) {
+    Tag & assign(tag_type_t, entity_type entity, Args &&... args) {
         assert(valid(entity));
         assert(!has<Tag>());
         const auto ttype = tag_family::type<Tag>();
@@ -444,134 +445,6 @@ public:
         return static_cast<Attaching<Tag> *>(tags[ttype].get())->tag;
     }
 
-    /**
-     * @brief Removes the given tag from its owner, if any.
-     * @tparam Tag Type of tag to remove.
-     */
-    template<typename Tag>
-    void remove() {
-        if(has<Tag>()) {
-            tags[tag_family::type<Tag>()].reset();
-        }
-    }
-
-    /**
-     * @brief Checks if the given tag has an owner.
-     * @tparam Tag Type of tag for which to perform the check.
-     * @return True if the tag already has an owner, false otherwise.
-     */
-    template<typename Tag>
-    bool has() const noexcept {
-        const auto ttype = tag_family::type<Tag>();
-        return (ttype < tags.size() &&
-                // it's a valid tag
-                tags[ttype] &&
-                // the associated entity hasn't been destroyed in the meantime
-                tags[ttype]->entity == (entities[tags[ttype]->entity & traits_type::entity_mask]));
-    }
-
-    /**
-     * @brief Returns a reference to the given tag.
-     *
-     * @warning
-     * Attempting to get a tag that hasn't an owner results in undefined
-     * behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode if the
-     * tag hasn't been previously attached to an entity.
-     *
-     * @tparam Tag Type of tag to get.
-     * @return A reference to the tag.
-     */
-    template<typename Tag>
-    const Tag & get() const noexcept {
-        assert(has<Tag>());
-        return static_cast<Attaching<Tag> *>(tags[tag_family::type<Tag>()].get())->tag;
-    }
-
-    /**
-     * @brief Returns a reference to the given tag.
-     *
-     * @warning
-     * Attempting to get a tag that hasn't an owner results in undefined
-     * behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode if the
-     * tag hasn't been previously attached to an entity.
-     *
-     * @tparam Tag Type of tag to get.
-     * @return A reference to the tag.
-     */
-    template<typename Tag>
-    Tag & get() noexcept {
-        return const_cast<Tag &>(const_cast<const Registry *>(this)->get<Tag>());
-    }
-
-    /**
-     * @brief Replaces the given tag.
-     *
-     * A new instance of the given tag is created and initialized with the
-     * arguments provided (the tag must have a proper constructor or be of
-     * aggregate type).
-     *
-     * @warning
-     * Attempting to replace a tag that hasn't an owner results in undefined
-     * behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode if the
-     * tag hasn't been previously attached to an entity.
-     *
-     * @tparam Tag Type of tag to replace.
-     * @tparam Args Types of arguments to use to construct the tag.
-     * @param args Parameters to use to initialize the tag.
-     * @return A reference to the tag.
-     */
-    template<typename Tag, typename... Args>
-    Tag & set(Args &&... args) {
-        return get<Tag>() = Tag{std::forward<Args>(args)...};
-    }
-
-    /**
-     * @brief Changes the owner of the given tag.
-     *
-     * The ownership of the tag is transferred from one entity to another.
-     *
-     * @warning
-     * Attempting to use an invalid entity or to transfer the ownership of a tag
-     * that hasn't an owner results in undefined behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode in case of
-     * invalid entity or if the tag hasn't been previously attached to an
-     * entity.
-     *
-     * @tparam Tag Type of tag of which to transfer the ownership.
-     * @param entity A valid entity identifier.
-     * @return A valid entity identifier.
-     */
-    template<typename Tag>
-    entity_type move(entity_type entity) {
-        assert(valid(entity));
-        assert(has<Tag>());
-        const auto ttype = tag_family::type<Tag>();
-        const auto owner = tags[ttype]->entity;
-        tags[ttype]->entity = entity;
-        return owner;
-    }
-
-    /**
-     * @brief Gets the owner of the given tag, if any.
-     *
-     * @warning
-     * Attempting to get the owner of a tag that hasn't been previously attached
-     * to an entity results in undefined behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode if the
-     * tag hasn't an owner.
-     *
-     * @tparam Tag Type of tag of which to get the owner.
-     * @return A valid entity identifier.
-     */
-    template<typename Tag>
-    entity_type attachee() const noexcept {
-        assert(has<Tag>());
-        return tags[tag_family::type<Tag>()]->entity;
-    }
-
     /**
      * @brief Assigns the given component to an entity.
      *
@@ -598,6 +471,17 @@ public:
         return assure<Component>().construct(entity, std::forward<Args>(args)...);
     }
 
+    /**
+     * @brief Removes the given tag from its owner, if any.
+     * @tparam Tag Type of tag to remove.
+     */
+    template<typename Tag>
+    void remove() {
+        if(has<Tag>()) {
+            tags[tag_family::type<Tag>()].reset();
+        }
+    }
+
     /**
      * @brief Removes the given component from an entity.
      *
@@ -617,6 +501,21 @@ public:
         pool<Component>().destroy(entity);
     }
 
+    /**
+     * @brief Checks if the given tag has an owner.
+     * @tparam Tag Type of tag for which to perform the check.
+     * @return True if the tag already has an owner, false otherwise.
+     */
+    template<typename Tag>
+    bool has() const noexcept {
+        const auto ttype = tag_family::type<Tag>();
+        return (ttype < tags.size() &&
+                // it's a valid tag
+                tags[ttype] &&
+                // the associated entity hasn't been destroyed in the meantime
+                tags[ttype]->entity == (entities[tags[ttype]->entity & traits_type::entity_mask]));
+    }
+
     /**
      * @brief Checks if an entity has all the given components.
      *
@@ -639,6 +538,41 @@ public:
         return all;
     }
 
+    /**
+     * @brief Returns a reference to the given tag.
+     *
+     * @warning
+     * Attempting to get a tag that hasn't an owner results in undefined
+     * behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if the
+     * tag hasn't been previously attached to an entity.
+     *
+     * @tparam Tag Type of tag to get.
+     * @return A reference to the tag.
+     */
+    template<typename Tag>
+    const Tag & get() const noexcept {
+        assert(has<Tag>());
+        return static_cast<Attaching<Tag> *>(tags[tag_family::type<Tag>()].get())->tag;
+    }
+
+    /**
+     * @brief Returns a reference to the given tag.
+     *
+     * @warning
+     * Attempting to get a tag that hasn't an owner results in undefined
+     * behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if the
+     * tag hasn't been previously attached to an entity.
+     *
+     * @tparam Tag Type of tag to get.
+     * @return A reference to the tag.
+     */
+    template<typename Tag>
+    Tag & get() noexcept {
+        return const_cast<Tag &>(const_cast<const Registry *>(this)->get<Tag>());
+    }
+
     /**
      * @brief Returns a reference to the given component for an entity.
      *
@@ -718,6 +652,29 @@ public:
         return std::tuple<Component &...>{get<Component>(entity)...};
     }
 
+    /**
+     * @brief Replaces the given tag.
+     *
+     * A new instance of the given tag is created and initialized with the
+     * arguments provided (the tag must have a proper constructor or be of
+     * aggregate type).
+     *
+     * @warning
+     * Attempting to replace a tag that hasn't an owner results in undefined
+     * behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if the
+     * tag hasn't been previously attached to an entity.
+     *
+     * @tparam Tag Type of tag to replace.
+     * @tparam Args Types of arguments to use to construct the tag.
+     * @param args Parameters to use to initialize the tag.
+     * @return A reference to the tag.
+     */
+    template<typename Tag, typename... Args>
+    Tag & replace(tag_type_t, Args &&... args) {
+        return get<Tag>() = Tag{std::forward<Args>(args)...};
+    }
+
     /**
      * @brief Replaces the given component for an entity.
      *
@@ -743,6 +700,50 @@ public:
         return (get<Component>(entity) = Component{std::forward<Args>(args)...});
     }
 
+    /**
+     * @brief Changes the owner of the given tag.
+     *
+     * The ownership of the tag is transferred from one entity to another.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to transfer the ownership of a tag
+     * that hasn't an owner results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode in case of
+     * invalid entity or if the tag hasn't been previously attached to an
+     * entity.
+     *
+     * @tparam Tag Type of tag of which to transfer the ownership.
+     * @param entity A valid entity identifier.
+     * @return A valid entity identifier.
+     */
+    template<typename Tag>
+    entity_type move(entity_type entity) {
+        assert(valid(entity));
+        assert(has<Tag>());
+        const auto ttype = tag_family::type<Tag>();
+        const auto owner = tags[ttype]->entity;
+        tags[ttype]->entity = entity;
+        return owner;
+    }
+
+    /**
+     * @brief Gets the owner of the given tag, if any.
+     *
+     * @warning
+     * Attempting to get the owner of a tag that hasn't been previously attached
+     * to an entity results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if the
+     * tag hasn't an owner.
+     *
+     * @tparam Tag Type of tag of which to get the owner.
+     * @return A valid entity identifier.
+     */
+    template<typename Tag>
+    entity_type attachee() const noexcept {
+        assert(has<Tag>());
+        return tags[tag_family::type<Tag>()]->entity;
+    }
+
     /**
      * @brief Assigns or replaces the given component for an entity.
      *

+ 8 - 16
src/entt/entity/snapshot.hpp

@@ -10,6 +10,7 @@
 #include <iterator>
 #include <type_traits>
 #include "entt_traits.hpp"
+#include "utility.hpp"
 
 
 namespace entt {
@@ -50,7 +51,7 @@ class Snapshot final {
 
     template<typename Component, typename Archive>
     void get(Archive &archive, const Registry<Entity> &registry) {
-        const auto component = registry.template component<Component>();
+        const auto component = registry.template type<Component>();
         const auto sz = registry.template size<Component>();
         const auto *entities = raw(registry, component);
 
@@ -216,21 +217,12 @@ class SnapshotLoader final {
         }
     }
 
-    template<typename Component, typename Archive>
-    void assign(Archive &archive) {
-        each(archive, [&archive, this](auto entity) {
-            static constexpr auto destroyed = false;
-            assure_fn(registry, entity, destroyed);
-            archive(registry.template assign<Component>(entity));
-        });
-    }
-
-    template<typename Tag, typename Archive>
-    void attach(Archive &archive) {
-        each(archive, [&archive, this](auto entity) {
+    template<typename Type, typename Archive, typename... Args>
+    void assign(Archive &archive, Args... args) {
+        each(archive, [&archive, this, args...](auto entity) {
             static constexpr auto destroyed = false;
             assure_fn(registry, entity, destroyed);
-            archive(registry.template attach<Tag>(entity));
+            archive(registry.template assign<Type>(args..., entity));
         });
     }
 
@@ -322,7 +314,7 @@ public:
     template<typename... Tag, typename Archive>
     SnapshotLoader & tag(Archive &archive) {
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (attach<Tag>(archive), 0)... };
+        accumulator_type accumulator = { 0, (assign<Tag>(archive, tag_type_t{}), 0)... };
         (void)accumulator;
         return *this;
     }
@@ -471,7 +463,7 @@ class ContinuousLoader final {
 
         each(archive, [&archive, this](auto entity) {
             entity = restore(entity);
-            archive(registry.template attach<Tag>(entity));
+            archive(registry.template assign<Tag>(tag_type_t{}, entity));
         });
     }
 

+ 20 - 0
src/entt/entity/utility.hpp

@@ -0,0 +1,20 @@
+#ifndef ENTT_ENTITY_UTILITY_HPP
+#define ENTT_ENTITY_UTILITY_HPP
+
+
+namespace entt {
+
+
+/**
+ * @brief Tag class type.
+ *
+ * An empty class type used to disambiguate the overloads of some member
+ * functions of the registry.
+ */
+struct tag_type_t final {};
+
+
+}
+
+
+#endif // ENTT_ENTITY_UTILITY_HPP

+ 1 - 0
src/entt/entt.hpp

@@ -6,6 +6,7 @@
 #include "entity/registry.hpp"
 #include "entity/snapshot.hpp"
 #include "entity/sparse_set.hpp"
+#include "entity/utility.hpp"
 #include "entity/view.hpp"
 #include "locator/locator.hpp"
 #include "process/process.hpp"

+ 9 - 9
test/entt/entity/registry.cpp

@@ -253,7 +253,7 @@ TEST(DefaultRegistry, Orphans) {
     registry.create();
     registry.assign<int>(registry.create());
     registry.create();
-    registry.attach<double>(registry.create());
+    registry.assign<double>(entt::tag_type_t{}, registry.create());
 
     registry.orphans([&](auto) { ++tot; });
     ASSERT_EQ(tot, 2u);
@@ -272,11 +272,11 @@ TEST(DefaultRegistry, Orphans) {
 TEST(DefaultRegistry, Types) {
     entt::DefaultRegistry registry;
 
-    ASSERT_EQ(registry.tag<int>(), registry.tag<int>());
-    ASSERT_EQ(registry.component<int>(), registry.component<int>());
+    ASSERT_EQ(registry.type<int>(entt::tag_type_t{}), registry.type<int>(entt::tag_type_t{}));
+    ASSERT_EQ(registry.type<int>(), registry.type<int>());
 
-    ASSERT_NE(registry.tag<int>(), registry.tag<double>());
-    ASSERT_NE(registry.component<int>(), registry.component<double>());
+    ASSERT_NE(registry.type<int>(entt::tag_type_t{}), registry.type<double>(entt::tag_type_t{}));
+    ASSERT_NE(registry.type<int>(), registry.type<double>(entt::tag_type_t{}));
 }
 
 TEST(DefaultRegistry, CreateDestroyEntities) {
@@ -317,14 +317,14 @@ TEST(DefaultRegistry, AttachSetRemoveTags) {
     ASSERT_FALSE(registry.has<int>());
 
     const auto entity = registry.create();
-    registry.attach<int>(entity, 42);
+    registry.assign<int>(entt::tag_type_t{}, entity, 42);
 
     ASSERT_TRUE(registry.has<int>());
     ASSERT_EQ(registry.get<int>(), 42);
     ASSERT_EQ(cregistry.get<int>(), 42);
     ASSERT_EQ(registry.attachee<int>(), entity);
 
-    registry.set<int>(3);
+    registry.replace<int>(entt::tag_type_t{}, 3);
 
     ASSERT_TRUE(registry.has<int>());
     ASSERT_EQ(registry.get<int>(), 3);
@@ -343,7 +343,7 @@ TEST(DefaultRegistry, AttachSetRemoveTags) {
 
     ASSERT_FALSE(registry.has<int>());
 
-    registry.attach<int>(entity, 42);
+    registry.assign<int>(entt::tag_type_t{}, entity, 42);
     registry.destroy(entity);
 
     ASSERT_FALSE(registry.has<int>());
@@ -437,7 +437,7 @@ TEST(DefaultRegistry, CleanPersistentViewsAfterReset) {
 TEST(DefaultRegistry, CleanTagsAfterReset) {
     entt::DefaultRegistry registry;
     const auto entity = registry.create();
-    registry.attach<int>(entity);
+    registry.assign<int>(entt::tag_type_t{}, entity);
 
     ASSERT_TRUE(registry.has<int>());
 

+ 5 - 5
test/entt/entity/snapshot.cpp

@@ -63,10 +63,10 @@ TEST(Snapshot, Dump) {
 
     const auto e3 = registry.create();
     registry.assign<char>(e3, '0');
-    registry.attach<float>(e3, .3f);
+    registry.assign<float>(entt::tag_type_t{}, e3, .3f);
 
     const auto e4 = registry.create();
-    registry.attach<AComponent>(e4);
+    registry.assign<AComponent>(entt::tag_type_t{}, e4);
 
     registry.destroy(e1);
     auto v1 = registry.current(e1);
@@ -152,10 +152,10 @@ TEST(Snapshot, Partial) {
 
     const auto e3 = registry.create();
     registry.assign<char>(e3, '0');
-    registry.attach<float>(e3, .3f);
+    registry.assign<float>(entt::tag_type_t{}, e3, .3f);
 
     const auto e4 = registry.create();
-    registry.attach<AComponent>(e4);
+    registry.assign<AComponent>(entt::tag_type_t{}, e4);
 
     registry.destroy(e1);
     auto v1 = registry.current(e1);
@@ -281,7 +281,7 @@ TEST(Snapshot, Continuous) {
         if(i % 2) {
             src.assign<Foo>(entity, entity);
         } else if(i == 2) {
-            src.attach<double>(entity, .3);
+            src.assign<double>(entt::tag_type_t{}, entity, .3);
         }
     }
 

+ 4 - 4
test/mod/mod.cpp

@@ -157,7 +157,7 @@ class DuktapeRegistry {
     template<typename... Comp>
     void reg() {
         using accumulator_type = int[];
-        accumulator_type acc = { (func[registry.component<Comp>()] = {
+        accumulator_type acc = { (func[registry.type<Comp>()] = {
                                      &::set<Comp>,
                                      &::unset<Comp>,
                                      &::has<Comp>,
@@ -186,7 +186,7 @@ class DuktapeRegistry {
         auto type = duk_require_uint(ctx, 1);
 
         if(type >= udef) {
-            type = registry.component<DuktapeRuntime>();
+            type = registry.type<DuktapeRuntime>();
         }
 
         assert(func.find(type) != func.cend());
@@ -248,7 +248,7 @@ public:
                     assert(func.find(type) != func.cend());
                     match = (registry.*func[type].test)(entity);
                 } else {
-                    const auto ctype = registry.component<DuktapeRuntime>();
+                    const auto ctype = registry.type<DuktapeRuntime>();
                     assert(func.find(ctype) != func.cend());
                     match = (registry.*func[ctype].test)(entity);
 
@@ -287,7 +287,7 @@ const duk_function_list_entry js_DuktapeRegistry_methods[] = {
 void exportTypes(duk_context *ctx, entt::DefaultRegistry &registry) {
     auto exportType = [](auto *ctx, auto &registry, auto idx, auto type, const auto *name) {
         duk_push_string(ctx, name);
-        duk_push_uint(ctx, registry.template component<typename decltype(type)::type>());
+        duk_push_uint(ctx, registry.template type<typename decltype(type)::type>());
         duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
     };