tag dispatching to disambiguate tags and components (#65)

This commit is contained in:
Michele Caini
2018-04-07 00:07:07 +02:00
parent 0a36a91e6d
commit 820178f006
9 changed files with 192 additions and 178 deletions

View File

@@ -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);

1
TODO
View File

@@ -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

View File

@@ -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,6 +445,32 @@ public:
return static_cast<Attaching<Tag> *>(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.<br/>
* 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<typename Component, typename... Args>
Component & assign(entity_type entity, Args &&... args) {
assert(valid(entity));
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.
@@ -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.<br/>
* 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<typename Component>
void remove(entity_type entity) {
assert(valid(entity));
pool<Component>().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.<br/>
* 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<typename... Component>
bool has(entity_type entity) const noexcept {
assert(valid(entity));
using accumulator_type = bool[];
bool all = true;
accumulator_type accumulator = { all, (all = all && managed<Component>() && pool<Component>().has(entity))... };
(void)accumulator;
return all;
}
/**
* @brief Returns a reference to the given tag.
*
@@ -505,140 +573,6 @@ public:
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.
*
* 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.<br/>
* 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<typename Component, typename... Args>
Component & assign(entity_type entity, Args &&... args) {
assert(valid(entity));
return assure<Component>().construct(entity, std::forward<Args>(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.<br/>
* 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<typename Component>
void remove(entity_type entity) {
assert(valid(entity));
pool<Component>().destroy(entity);
}
/**
* @brief Checks if an entity has all the given components.
*
* @warning
* Attempting to use an invalid entity results in undefined behavior.<br/>
* 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<typename... Component>
bool has(entity_type entity) const noexcept {
assert(valid(entity));
using accumulator_type = bool[];
bool all = true;
accumulator_type accumulator = { all, (all = all && managed<Component>() && pool<Component>().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<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.
*

View File

@@ -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) {
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 assign<Component>(entity));
});
}
template<typename Tag, typename Archive>
void attach(Archive &archive) {
each(archive, [&archive, this](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));
});
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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>());

View File

@@ -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);
}
}

View File

@@ -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);
};