From 820178f0069b0f757f13a910eb601068559bca5b Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Sat, 7 Apr 2018 00:07:07 +0200 Subject: [PATCH] tag dispatching to disambiguate tags and components (#65) --- README.md | 13 +- TODO | 1 - src/entt/entity/registry.hpp | 275 +++++++++++++++++----------------- src/entt/entity/snapshot.hpp | 24 +-- src/entt/entity/utility.hpp | 20 +++ src/entt/entt.hpp | 1 + test/entt/entity/registry.cpp | 18 +-- test/entt/entity/snapshot.cpp | 10 +- test/mod/mod.cpp | 8 +- 9 files changed, 192 insertions(+), 178 deletions(-) create mode 100644 src/entt/entity/utility.hpp diff --git a/README.md b/README.md index a95f3e798..d6b60184d 100644 --- a/README.md +++ b/README.md @@ -578,7 +578,8 @@ data structures or more complex and movable data structures with a proper constructor.
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(player); +registry.assign(entt::tag_type_t{}, player); // attaches a tag to an entity and initializes it -registry.attach(camera, player); +registry.assign(entt::tag_type_t{}, camera, player); // removes tags from their owners registry.remove(); @@ -598,12 +599,12 @@ registry.remove(); ``` 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(1.f, 1.f); +Point &point = registry.replace(entt::tag_type_t{}, 1.f, 1.f); // transfers the ownership of the tag to another entity entity_type prev = registry.move(next); diff --git a/TODO b/TODO index 8848d63bb..ae8f9a1b7 100644 --- a/TODO +++ b/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 diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 78ad9c780..be6760734 100644 --- a/src/entt/entity/registry.hpp +++ b/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 - tag_type tag() const noexcept { + tag_type type(tag_type_t) const noexcept { return tag_family::type(); } @@ -199,7 +200,7 @@ public: * @return Runtime numeric identifier of the given type of component. */ template - component_type component() const noexcept { + component_type type() const noexcept { return component_family::type(); } @@ -430,7 +431,7 @@ public: * @return A reference to the newly created tag. */ template - Tag & attach(entity_type entity, Args &&... args) { + Tag & assign(tag_type_t, entity_type entity, Args &&... args) { assert(valid(entity)); assert(!has()); const auto ttype = tag_family::type(); @@ -444,6 +445,32 @@ public: return static_cast *>(tags[ttype].get())->tag; } + /** + * @brief Assigns the given component to an entity. + * + * A new instance of the given component is created and initialized with the + * arguments provided (the component must have a proper constructor or be of + * aggregate type). Then the component is assigned to the given entity. + * + * @warning + * Attempting to use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity already owns an instance of the given + * component. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid entity identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + Component & assign(entity_type entity, Args &&... args) { + assert(valid(entity)); + return assure().construct(entity, std::forward(args)...); + } + /** * @brief Removes the given tag from its owner, if any. * @tparam Tag Type of tag to remove. @@ -455,6 +482,25 @@ public: } } + /** + * @brief Removes the given component from an entity. + * + * @warning + * Attempting to use an invalid entity or to remove a component from an + * entity that doesn't own it results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity or if the entity doesn't own an instance of the given + * component. + * + * @tparam Component Type of component to remove. + * @param entity A valid entity identifier. + */ + template + void remove(entity_type entity) { + assert(valid(entity)); + pool().destroy(entity); + } + /** * @brief Checks if the given tag has an owner. * @tparam Tag Type of tag for which to perform the check. @@ -470,6 +516,28 @@ public: tags[ttype]->entity == (entities[tags[ttype]->entity & traits_type::entity_mask])); } + /** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior.
+ * An assertion will abort the execution at runtime in debug mode in case of + * invalid entity. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid entity identifier. + * @return True if the entity has all the components, false otherwise. + */ + template + bool has(entity_type entity) const noexcept { + assert(valid(entity)); + using accumulator_type = bool[]; + bool all = true; + accumulator_type accumulator = { all, (all = all && managed() && pool().has(entity))... }; + (void)accumulator; + return all; + } + /** * @brief Returns a reference to the given tag. * @@ -505,140 +573,6 @@ public: return const_cast(const_cast(this)->get()); } - /** - * @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.
- * 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 - Tag & set(Args &&... args) { - return get() = Tag{std::forward(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.
- * 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 - entity_type move(entity_type entity) { - assert(valid(entity)); - assert(has()); - const auto ttype = tag_family::type(); - 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.
- * 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 - entity_type attachee() const noexcept { - assert(has()); - return tags[tag_family::type()]->entity; - } - - /** - * @brief Assigns the given component to an entity. - * - * A new instance of the given component is created and initialized with the - * arguments provided (the component must have a proper constructor or be of - * aggregate type). Then the component is assigned to the given entity. - * - * @warning - * Attempting to use an invalid entity or to assign a component to an entity - * that already owns it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity already owns an instance of the given - * component. - * - * @tparam Component Type of component to create. - * @tparam Args Types of arguments to use to construct the component. - * @param entity A valid entity identifier. - * @param args Parameters to use to initialize the component. - * @return A reference to the newly created component. - */ - template - Component & assign(entity_type entity, Args &&... args) { - assert(valid(entity)); - return assure().construct(entity, std::forward(args)...); - } - - /** - * @brief Removes the given component from an entity. - * - * @warning - * Attempting to use an invalid entity or to remove a component from an - * entity that doesn't own it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. - * - * @tparam Component Type of component to remove. - * @param entity A valid entity identifier. - */ - template - void remove(entity_type entity) { - assert(valid(entity)); - pool().destroy(entity); - } - - /** - * @brief Checks if an entity has all the given components. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. - * - * @tparam Component Components for which to perform the check. - * @param entity A valid entity identifier. - * @return True if the entity has all the components, false otherwise. - */ - template - bool has(entity_type entity) const noexcept { - assert(valid(entity)); - using accumulator_type = bool[]; - bool all = true; - accumulator_type accumulator = { all, (all = all && managed() && pool().has(entity))... }; - (void)accumulator; - return all; - } - /** * @brief Returns a reference to the given component for an entity. * @@ -718,6 +652,29 @@ public: return std::tuple{get(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.
+ * 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 + Tag & replace(tag_type_t, Args &&... args) { + return get() = Tag{std::forward(args)...}; + } + /** * @brief Replaces the given component for an entity. * @@ -743,6 +700,50 @@ public: return (get(entity) = Component{std::forward(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.
+ * 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 + entity_type move(entity_type entity) { + assert(valid(entity)); + assert(has()); + const auto ttype = tag_family::type(); + 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.
+ * 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 + entity_type attachee() const noexcept { + assert(has()); + return tags[tag_family::type()]->entity; + } + /** * @brief Assigns or replaces the given component for an entity. * diff --git a/src/entt/entity/snapshot.hpp b/src/entt/entity/snapshot.hpp index 57d2fa3f6..342d2940e 100644 --- a/src/entt/entity/snapshot.hpp +++ b/src/entt/entity/snapshot.hpp @@ -10,6 +10,7 @@ #include #include #include "entt_traits.hpp" +#include "utility.hpp" namespace entt { @@ -50,7 +51,7 @@ class Snapshot final { template void get(Archive &archive, const Registry ®istry) { - const auto component = registry.template component(); + const auto component = registry.template type(); const auto sz = registry.template size(); const auto *entities = raw(registry, component); @@ -216,21 +217,12 @@ class SnapshotLoader final { } } - template - void assign(Archive &archive) { - each(archive, [&archive, this](auto entity) { + template + 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 assign(entity)); - }); - } - - template - void attach(Archive &archive) { - each(archive, [&archive, this](auto entity) { - static constexpr auto destroyed = false; - assure_fn(registry, entity, destroyed); - archive(registry.template attach(entity)); + archive(registry.template assign(args..., entity)); }); } @@ -322,7 +314,7 @@ public: template SnapshotLoader & tag(Archive &archive) { using accumulator_type = int[]; - accumulator_type accumulator = { 0, (attach(archive), 0)... }; + accumulator_type accumulator = { 0, (assign(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(entity)); + archive(registry.template assign(tag_type_t{}, entity)); }); } diff --git a/src/entt/entity/utility.hpp b/src/entt/entity/utility.hpp new file mode 100644 index 000000000..2e056a3b9 --- /dev/null +++ b/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 diff --git a/src/entt/entt.hpp b/src/entt/entt.hpp index ca0547686..86475e9e4 100644 --- a/src/entt/entt.hpp +++ b/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" diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index 61ab54042..e66bfe5ea 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -253,7 +253,7 @@ TEST(DefaultRegistry, Orphans) { registry.create(); registry.assign(registry.create()); registry.create(); - registry.attach(registry.create()); + registry.assign(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(), registry.tag()); - ASSERT_EQ(registry.component(), registry.component()); + ASSERT_EQ(registry.type(entt::tag_type_t{}), registry.type(entt::tag_type_t{})); + ASSERT_EQ(registry.type(), registry.type()); - ASSERT_NE(registry.tag(), registry.tag()); - ASSERT_NE(registry.component(), registry.component()); + ASSERT_NE(registry.type(entt::tag_type_t{}), registry.type(entt::tag_type_t{})); + ASSERT_NE(registry.type(), registry.type(entt::tag_type_t{})); } TEST(DefaultRegistry, CreateDestroyEntities) { @@ -317,14 +317,14 @@ TEST(DefaultRegistry, AttachSetRemoveTags) { ASSERT_FALSE(registry.has()); const auto entity = registry.create(); - registry.attach(entity, 42); + registry.assign(entt::tag_type_t{}, entity, 42); ASSERT_TRUE(registry.has()); ASSERT_EQ(registry.get(), 42); ASSERT_EQ(cregistry.get(), 42); ASSERT_EQ(registry.attachee(), entity); - registry.set(3); + registry.replace(entt::tag_type_t{}, 3); ASSERT_TRUE(registry.has()); ASSERT_EQ(registry.get(), 3); @@ -343,7 +343,7 @@ TEST(DefaultRegistry, AttachSetRemoveTags) { ASSERT_FALSE(registry.has()); - registry.attach(entity, 42); + registry.assign(entt::tag_type_t{}, entity, 42); registry.destroy(entity); ASSERT_FALSE(registry.has()); @@ -437,7 +437,7 @@ TEST(DefaultRegistry, CleanPersistentViewsAfterReset) { TEST(DefaultRegistry, CleanTagsAfterReset) { entt::DefaultRegistry registry; const auto entity = registry.create(); - registry.attach(entity); + registry.assign(entt::tag_type_t{}, entity); ASSERT_TRUE(registry.has()); diff --git a/test/entt/entity/snapshot.cpp b/test/entt/entity/snapshot.cpp index 67cd168f1..f2518955f 100644 --- a/test/entt/entity/snapshot.cpp +++ b/test/entt/entity/snapshot.cpp @@ -63,10 +63,10 @@ TEST(Snapshot, Dump) { const auto e3 = registry.create(); registry.assign(e3, '0'); - registry.attach(e3, .3f); + registry.assign(entt::tag_type_t{}, e3, .3f); const auto e4 = registry.create(); - registry.attach(e4); + registry.assign(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(e3, '0'); - registry.attach(e3, .3f); + registry.assign(entt::tag_type_t{}, e3, .3f); const auto e4 = registry.create(); - registry.attach(e4); + registry.assign(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(entity, entity); } else if(i == 2) { - src.attach(entity, .3); + src.assign(entt::tag_type_t{}, entity, .3); } } diff --git a/test/mod/mod.cpp b/test/mod/mod.cpp index 0a4c47101..cd0777035 100644 --- a/test/mod/mod.cpp +++ b/test/mod/mod.cpp @@ -157,7 +157,7 @@ class DuktapeRegistry { template void reg() { using accumulator_type = int[]; - accumulator_type acc = { (func[registry.component()] = { + accumulator_type acc = { (func[registry.type()] = { &::set, &::unset, &::has, @@ -186,7 +186,7 @@ class DuktapeRegistry { auto type = duk_require_uint(ctx, 1); if(type >= udef) { - type = registry.component(); + type = registry.type(); } 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(); + const auto ctype = registry.type(); 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 ®istry) { auto exportType = [](auto *ctx, auto ®istry, auto idx, auto type, const auto *name) { duk_push_string(ctx, name); - duk_push_uint(ctx, registry.template component()); + duk_push_uint(ctx, registry.template type()); duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE); };