Compare commits

...

7 Commits

Author SHA1 Message Date
Michele Caini
653ded0e6f updated version 2018-06-04 08:52:10 +02:00
Michele Caini
e34bec7dee cloning an entity is no longer allowed 2018-06-04 08:49:13 +02:00
Michele Caini
610b560fb5 typo 2018-06-03 22:24:45 +02:00
Michele Caini
0a03ddb8a7 typo 2018-06-03 22:17:06 +02:00
Michele Caini
f31790631a fixed include 2018-06-03 22:10:33 +02:00
Michele Caini
e07128760e review: prototype (#89) 2018-06-03 19:06:12 +02:00
Michele Caini
dd02ae313d minor changes 2018-06-02 17:06:44 +02:00
18 changed files with 386 additions and 328 deletions

View File

@@ -16,7 +16,7 @@ endif()
# Project configuration
#
project(entt VERSION 2.6.0)
project(entt VERSION 2.6.1)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)

View File

@@ -396,25 +396,19 @@ Entities are represented by _entity identifiers_. An entity identifier is an
opaque type that users should not inspect or modify in any way. It carries
information about the entity itself and its version.
A registry can be used both to construct and destroy entities, as well as to
clone already existing entities:
A registry can be used both to construct and destroy entities:
```cpp
// constructs a naked entity with no components and returns its identifier
auto entity = registry.create();
// clones an entity and assigns all its components by copy to the newly created one
auto other = registry.clone(entity);
// destroys an entity and all its components
registry.destroy(entity);
```
Be aware that cloning an entity can lead to unexpected results under certain
conditions. Please refer to the inline documentation for more details.<br/>
Once an entity is deleted, the registry can freely reuse it internally with a
When an entity is destroyed, the registry can freely reuse it internally with a
slightly different identifier. In particular, the version of an entity is
increased each and every time it's destroyed.<br/>
increased each and every time it's discarded.<br/>
In case entity identifiers are stored around, the registry offers all the
functionalities required to test them and get out of the them all the
information they carry:
@@ -1057,16 +1051,15 @@ how many prototypes they want, each one initialized differently from the others.
The following is an example of use of a prototype:
```cpp
entt::DefaultPrototype prototype;
entt::DefaultRegistry registry;
entt::DefaultPrototype prototype{registry};
prototype.set<Position>(100.f, 100.f);
prototype.set<Velocity>(0.f, 0.f);
// ...
entt::DefaultRegistry registry;
const auto entity = prototype(registry);
const auto entity = prototype();
```
To assign and remove components from a prototype, it offers two dedicated member
@@ -1079,21 +1072,21 @@ Creating an entity from a prototype is straightforward:
* To create a new entity from scratch and assign it a prototype, this is the way
to go:
```cpp
const auto entity = prototype(registry);
const auto entity = prototype();
```
It is equivalent to the following invokation:
```cpp
const auto entity = prototype.create(registry);
const auto entity = prototype.create();
```
* In case we want to initialize an already existing entity, we can provide the
`operator()` directly with the entity identifier:
```cpp
prototype(registry, entity);
prototype(entity);
```
It is equivalent to the following invokation:
```cpp
prototype.assign(registry, entity);
prototype.assign(entity);
```
Note that existing components aren't overwritten in this case. Only those
components that the entity doesn't own yet are copied over. All the other
@@ -1102,9 +1095,14 @@ Creating an entity from a prototype is straightforward:
* Finally, to assign or replace all the components for an entity, thus
overwriting existing ones:
```cpp
prototype.accommodate(registry, entity);
prototype.accommodate(entity);
```
In the examples above, the prototype uses its underlying registry to create
entities and components both for its purposes and when it's cloned. To use a
different repository to clone a prototype, all the member functions accept also
a reference to a valid registry as a first argument.
Prototypes are a very useful tool that can save a lot of typing sometimes.
Furthermore, the codebase may be easier to maintain, since updating a prototype
is much less error prone than jumping around in the codebase to update all the

View File

@@ -31,7 +31,7 @@ struct StdSort {
* @param compare A valid comparison function object.
*/
template<typename It, typename Compare = std::less<>>
void operator()(It first, It last, Compare compare = Compare{}) {
void operator()(It first, It last, Compare compare = Compare{}) const {
std::sort(std::move(first), std::move(last), std::move(compare));
}
};
@@ -51,7 +51,7 @@ struct InsertionSort {
* @param compare A valid comparison function object.
*/
template<typename It, typename Compare = std::less<>>
void operator()(It first, It last, Compare compare = Compare{}) {
void operator()(It first, It last, Compare compare = Compare{}) const {
auto it = first + 1;
while(it != last) {

View File

@@ -2,9 +2,9 @@
#define ENTT_CORE_FAMILY_HPP
#include<type_traits>
#include<cstddef>
#include<atomic>
#include <type_traits>
#include <cstddef>
#include <atomic>
#include "../config/config.h"

View File

@@ -2,9 +2,10 @@
#define ENTT_CORE_IDENT_HPP
#include<type_traits>
#include<cstddef>
#include<utility>
#include <type_traits>
#include <cstddef>
#include <utility>
#include "../config/config.h"
namespace entt {
@@ -24,12 +25,12 @@ struct Identifier final: Identifier<Types>... {
using identifier_type = std::size_t;
template<std::size_t... Indexes>
constexpr Identifier(std::index_sequence<Indexes...>)
constexpr Identifier(std::index_sequence<Indexes...>) ENTT_NOEXCEPT
: Identifier<Types>{std::index_sequence<Indexes>{}}...
{}
template<typename Type>
constexpr std::size_t get() const {
constexpr std::size_t get() const ENTT_NOEXCEPT {
return Identifier<std::decay_t<Type>>::get();
}
};
@@ -40,11 +41,11 @@ struct Identifier<Type> {
using identifier_type = std::size_t;
template<std::size_t Index>
constexpr Identifier(std::index_sequence<Index>)
constexpr Identifier(std::index_sequence<Index>) ENTT_NOEXCEPT
: index{Index}
{}
constexpr std::size_t get() const {
constexpr std::size_t get() const ENTT_NOEXCEPT {
return index;
}

View File

@@ -3,11 +3,11 @@
#include <tuple>
#include <memory>
#include <vector>
#include <utility>
#include <cstddef>
#include <algorithm>
#include <type_traits>
#include <unordered_map>
#include "../config/config.h"
#include "registry.hpp"
@@ -24,43 +24,28 @@ namespace entt {
* entities of a registry at once.
*
* @note
* Components used along with prototypes must be copy constructible.
* Components used along with prototypes must be copy constructible. Prototypes
* wrap component types with custom types, so they do not interfere with other
* users of the registry they were built with.
*
* @warning
* Prototypes directly use their underlying registries to store entities and
* components for their purposes. Users must ensure that the lifetime of a
* registry and its contents exceed that of the prototypes that use it.
*
* @tparam Entity A valid entity type (see entt_traits for more details).
*/
template<typename Entity>
class Prototype {
class Prototype final {
using fn_type = void(*)(const Prototype &, Registry<Entity> &, const Entity);
using component_type = typename Registry<Entity>::component_type;
using fn_type = void(*)(Registry<Entity> &, const Entity, const void *);
using deleter_type = void(*)(void *);
using ptr_type = std::unique_ptr<void, deleter_type>;
template<typename Component>
static void accommodate(Registry<Entity> &registry, const Entity entity, const void *component) {
const auto &ref = *static_cast<const Component *>(component);
registry.template accommodate<Component>(entity, ref);
}
struct Wrapper { Component component; };
template<typename Component>
static void assign(Registry<Entity> &registry, const Entity entity, const void *component) {
if(!registry.template has<Component>(entity)) {
const auto &ref = *static_cast<const Component *>(component);
registry.template assign<Component>(entity, ref);
}
}
struct Handler final {
Handler(ptr_type component, const fn_type accommodate, const fn_type assign, const component_type type)
: component{std::move(component)},
accommodate{accommodate},
assign{assign},
type{type}
{}
ptr_type component{nullptr, +[](void *) {}};
fn_type accommodate{nullptr};
fn_type assign{nullptr};
component_type type;
struct Handler {
fn_type accommodate;
fn_type assign;
};
public:
@@ -71,6 +56,22 @@ public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/**
* @brief Constructs a prototype that is bound to a given registry.
* @param registry A valid reference to a registry.
*/
Prototype(Registry<Entity> &registry)
: registry{registry},
entity{registry.create()}
{}
/**
* @brief Releases all its resources.
*/
~Prototype() {
registry.destroy(entity);
}
/**
* @brief Assigns to or replaces the given component of a prototype.
* @tparam Component Type of component to assign or replace.
@@ -80,22 +81,21 @@ public:
*/
template<typename Component, typename... Args>
Component & set(Args &&... args) {
const auto ctype = registry_type::template type<Component>();
fn_type accommodate = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
const auto &wrapper = prototype.registry.template get<Wrapper<Component>>(prototype.entity);
other.template accommodate<Component>(dst, wrapper.component);
};
auto it = std::find_if(handlers.begin(), handlers.end(), [ctype](const auto &handler) {
return handler.type == ctype;
});
fn_type assign = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
if(!other.template has<Component>(dst)) {
const auto &wrapper = prototype.registry.template get<Wrapper<Component>>(prototype.entity);
other.template accommodate<Component>(dst, wrapper.component);
}
};
const auto deleter = +[](void *component) { delete static_cast<Component *>(component); };
ptr_type component{new Component{std::forward<Args>(args)...}, deleter};
if(it == handlers.cend()) {
handlers.emplace_back(std::move(component), &Prototype::accommodate<Component>, &Prototype::assign<Component>, ctype);
} else {
it->component = std::move(component);
}
return get<Component>();
handlers[registry.template type<Component>()] = Handler{accommodate, assign};
auto &wrapper = registry.template accommodate<Wrapper<Component>>(entity, Component{std::forward<Args>(args)...});
return wrapper.component;
}
/**
@@ -104,9 +104,8 @@ public:
*/
template<typename Component>
void unset() ENTT_NOEXCEPT {
handlers.erase(std::remove_if(handlers.begin(), handlers.end(), [](const auto &handler) {
return handler.type == registry_type::template type<Component>();
}), handlers.end());
registry.template reset<Wrapper<Component>>(entity);
handlers.erase(registry.template type<Component>());
}
/**
@@ -116,17 +115,7 @@ public:
*/
template<typename... Component>
bool has() const ENTT_NOEXCEPT {
auto found = [this](const auto ctype) {
return std::find_if(handlers.cbegin(), handlers.cend(), [ctype](const auto &handler) {
return handler.type == ctype;
}) != handlers.cend();
};
bool all = true;
using accumulator_type = bool[];
accumulator_type accumulator = { all, (all = all && found(registry_type::template type<Component>()))... };
(void)accumulator;
return all;
return registry.template has<Wrapper<Component>...>(entity);
}
/**
@@ -143,13 +132,7 @@ public:
*/
template<typename Component>
const Component & get() const ENTT_NOEXCEPT {
assert(has<Component>());
auto it = std::find_if(handlers.cbegin(), handlers.cend(), [](const auto &handler) {
return handler.type == registry_type::template type<Component>();
});
return *static_cast<Component *>(it->component.get());
return registry.template get<Wrapper<Component>>(entity).component;
}
/**
@@ -182,7 +165,7 @@ public:
* @return References to the components owned by the prototype.
*/
template<typename... Component>
std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
get() const ENTT_NOEXCEPT {
return std::tuple<const Component &...>{get<Component>()...};
}
@@ -200,7 +183,7 @@ public:
* @return References to the components owned by the prototype.
*/
template<typename... Component>
std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
get() ENTT_NOEXCEPT {
return std::tuple<Component &...>{get<Component>()...};
}
@@ -215,15 +198,38 @@ public:
* prototype(registry, entity);
* @endcode
*
* @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.
* @note
* The registry may or may not be different from the one already used by
* the prototype. There is also an overload that directly uses the
* underlying registry.
*
* @param registry A valid reference to a registry.
* @param other A valid reference to a registry.
* @return A valid entity identifier.
*/
entity_type create(registry_type &registry) {
entity_type create(registry_type &other) const {
const auto entity = other.create();
assign(other, entity);
return entity;
}
/**
* @brief Creates a new entity using a given prototype.
*
* Utility shortcut, equivalent to the following snippet:
*
* @code{.cpp}
* const auto entity = registry.create();
* prototype(entity);
* @endcode
*
* @note
* This overload directly uses the underlying registry as a working space.
* Therefore, the components of the prototype and of the entity will share
* the same registry.
*
* @return A valid entity identifier.
*/
entity_type create() const {
const auto entity = registry.create();
assign(registry, entity);
return entity;
@@ -237,18 +243,49 @@ public:
* In other words, only those components that the entity doesn't own yet are
* copied over. All the other components remain unchanged.
*
* @note
* The registry may or may not be different from the one already used by
* the prototype. There is also an overload that directly uses the
* underlying registry.
*
* @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.
*
* @param registry A valid reference to a registry.
* @param entity A valid entity identifier.
* @param other A valid reference to a registry.
* @param dst A valid entity identifier.
*/
void assign(registry_type &registry, const entity_type entity) {
std::for_each(handlers.begin(), handlers.end(), [&registry, entity](auto &&handler) {
handler.assign(registry, entity, handler.component.get());
});
void assign(registry_type &other, const entity_type dst) const {
for(auto &handler: handlers) {
handler.second.assign(*this, other, dst);
}
}
/**
* @brief Assigns the components of a prototype to a given entity.
*
* Assigning a prototype to an entity won't overwrite existing components
* under any circumstances.<br/>
* In other words, only those components that the entity doesn't own yet are
* copied over. All the other components remain unchanged.
*
* @note
* This overload directly uses the underlying registry as a working space.
* Therefore, the components of the prototype and of the entity will share
* the same registry.
*
* @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.
*
* @param dst A valid entity identifier.
*/
void assign(const entity_type dst) const {
for(auto &handler: handlers) {
handler.second.assign(*this, registry, dst);
}
}
/**
@@ -257,18 +294,47 @@ public:
* Existing components are overwritten, if any. All the other components
* will be copied over to the target entity.
*
* @note
* The registry may or may not be different from the one already used by
* the prototype. There is also an overload that directly uses the
* underlying registry.
*
* @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.
*
* @param registry A valid reference to a registry.
* @param entity A valid entity identifier.
* @param other A valid reference to a registry.
* @param dst A valid entity identifier.
*/
void accommodate(registry_type &registry, const entity_type entity) {
std::for_each(handlers.begin(), handlers.end(), [&registry, entity](auto &&handler) {
handler.accommodate(registry, entity, handler.component.get());
});
void accommodate(registry_type &other, const entity_type dst) const {
for(auto &handler: handlers) {
handler.second.accommodate(*this, other, dst);
}
}
/**
* @brief Assigns or replaces the components of a prototype for an entity.
*
* Existing components are overwritten, if any. All the other components
* will be copied over to the target entity.
*
* @note
* This overload directly uses the underlying registry as a working space.
* Therefore, the components of the prototype and of the entity will share
* the same registry.
*
* @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.
*
* @param dst A valid entity identifier.
*/
void accommodate(const entity_type dst) const {
for(auto &handler: handlers) {
handler.second.accommodate(*this, registry, dst);
}
}
/**
@@ -279,16 +345,45 @@ public:
* In other words, only the components that the entity doesn't own yet are
* copied over. All the other components remain unchanged.
*
* @note
* The registry may or may not be different from the one already used by
* the prototype. There is also an overload that directly uses the
* underlying registry.
*
* @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.
*
* @param registry A valid reference to a registry.
* @param entity A valid entity identifier.
* @param other A valid reference to a registry.
* @param dst A valid entity identifier.
*/
inline void operator()(registry_type &registry, const entity_type entity) ENTT_NOEXCEPT {
assign(registry, entity);
inline void operator()(registry_type &other, const entity_type dst) const ENTT_NOEXCEPT {
assign(other, dst);
}
/**
* @brief Assigns the components of a prototype to an entity.
*
* Assigning a prototype to an entity won't overwrite existing components
* under any circumstances.<br/>
* In other words, only the components that the entity doesn't own yet are
* copied over. All the other components remain unchanged.
*
* @note
* This overload directly uses the underlying registry as a working space.
* Therefore, the components of the prototype and of the entity will share
* the same registry.
*
* @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.
*
* @param dst A valid entity identifier.
*/
inline void operator()(const entity_type dst) const ENTT_NOEXCEPT {
assign(registry, dst);
}
/**
@@ -301,27 +396,54 @@ public:
* prototype(registry, entity);
* @endcode
*
* @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.
* @note
* The registry may or may not be different from the one already used by
* the prototype. There is also an overload that directly uses the
* underlying registry.
*
* @param registry A valid reference to a registry.
* @param other A valid reference to a registry.
* @return A valid entity identifier.
*/
inline entity_type operator()(registry_type &registry) ENTT_NOEXCEPT {
inline entity_type operator()(registry_type &other) const ENTT_NOEXCEPT {
return create(other);
}
/**
* @brief Creates a new entity using a given prototype.
*
* Utility shortcut, equivalent to the following snippet:
*
* @code{.cpp}
* const auto entity = registry.create();
* prototype(entity);
* @endcode
*
* @note
* This overload directly uses the underlying registry as a working space.
* Therefore, the components of the prototype and of the entity will share
* the same registry.
*
* @return A valid entity identifier.
*/
inline entity_type operator()() const ENTT_NOEXCEPT {
return create(registry);
}
private:
std::vector<Handler> handlers;
std::unordered_map<component_type, Handler> handlers;
Registry<Entity> &registry;
entity_type entity;
};
/**
* @brief Default prototype
*
* The default prototype is the best choice for almost all the
* applications.<br/>
* Users should have a really good reason to choose something different.
*/
using DefaultPrototype = Prototype<uint32_t>;
using DefaultPrototype = Prototype<std::uint32_t>;
}

View File

@@ -408,51 +408,6 @@ public:
return entity;
}
/**
* @brief Clones an entity and returns the newly created one.
*
* There are two kinds of entity identifiers:
*
* * Newly created ones in case no entities have been previously destroyed.
* * Recycled ones with updated versions.
*
* Users should not care about the type of the returned entity identifier.
* In case entity identifers are stored around, the `valid` member
* function can be used to know if they are still valid or the entity has
* been destroyed and potentially recycled.
*
* @warning
* In case there are listeners that observe the construction of components
* and assign other components to the entity in their bodies, the result of
* invoking this function may not be as expected. In the worst case, it
* could lead to undefined behavior. An assertion will abort the execution
* at runtime in debug mode if a violation is detected.
*
* @warning
* Attempting to clone an invalid entity results in undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode in case of
* invalid entity.
*
* @param entity A valid entity identifier
* @return A valid entity identifier.
*/
entity_type clone(const entity_type entity) ENTT_NOEXCEPT {
assert(valid(entity));
const auto other = create();
for(auto pos = pools.size(); pos; --pos) {
auto &tup = pools[pos-1];
auto &cpool = std::get<0>(tup);
if(cpool && cpool->has(entity)) {
cpool->clone(other, entity);
std::get<1>(tup).publish(*this, other);
}
}
return other;
}
/**
* @brief Destroys an entity and lets the registry recycle the identifier.
*
@@ -756,7 +711,7 @@ public:
* @return References to the components owned by the entity.
*/
template<typename... Component>
std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
get(const entity_type entity) const ENTT_NOEXCEPT {
return std::tuple<const Component &...>{get<Component>(entity)...};
}
@@ -776,7 +731,7 @@ public:
* @return References to the components owned by the entity.
*/
template<typename... Component>
std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
inline std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
get(const entity_type entity) ENTT_NOEXCEPT {
return std::tuple<Component &...>{get<Component>(entity)...};
}

View File

@@ -48,7 +48,7 @@ class Snapshot final {
{}
template<typename Component, typename Archive, typename It>
void get(Archive &archive, std::size_t sz, It first, It last) {
void get(Archive &archive, std::size_t sz, It first, It last) const {
archive(static_cast<Entity>(sz));
while(first != last) {
@@ -61,7 +61,7 @@ class Snapshot final {
}
template<typename... Component, typename Archive, typename It, std::size_t... Indexes>
void component(Archive &archive, It first, It last, std::index_sequence<Indexes...>) {
void component(Archive &archive, It first, It last, std::index_sequence<Indexes...>) const {
std::array<std::size_t, sizeof...(Indexes)> size{};
auto begin = first;
@@ -99,7 +99,7 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
Snapshot & entities(Archive &archive) {
const Snapshot & entities(Archive &archive) const {
archive(static_cast<Entity>(registry.size()));
registry.each([&archive](const auto entity) { archive(entity); });
return *this;
@@ -116,7 +116,7 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename Archive>
Snapshot & destroyed(Archive &archive) {
const Snapshot & destroyed(Archive &archive) const {
auto size = registry.capacity() - registry.size();
archive(static_cast<Entity>(size));
auto curr = seed;
@@ -141,7 +141,7 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename Component, typename Archive>
Snapshot & component(Archive &archive) {
const Snapshot & component(Archive &archive) const {
const auto sz = registry.template size<Component>();
const auto *entities = registry.template data<Component>();
@@ -167,8 +167,8 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive>
std::enable_if_t<(sizeof...(Component) > 1), Snapshot &>
component(Archive &archive) {
std::enable_if_t<(sizeof...(Component) > 1), const Snapshot &>
component(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (component<Component>(archive), 0)... };
(void)accumulator;
@@ -190,7 +190,7 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Component, typename Archive, typename It>
Snapshot & component(Archive &archive, It first, It last) {
const Snapshot & component(Archive &archive, It first, It last) const {
component<Component...>(archive, first, last, std::make_index_sequence<sizeof...(Component)>{});
return *this;
}
@@ -207,7 +207,7 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename Tag, typename Archive>
Snapshot & tag(Archive &archive) {
const Snapshot & tag(Archive &archive) const {
const bool has = registry.template has<Tag>();
// numerical length is forced for tags to facilitate loading
@@ -232,8 +232,8 @@ public:
* @return An object of this type to continue creating the snapshot.
*/
template<typename... Tag, typename Archive>
std::enable_if_t<(sizeof...(Tag) > 1), Snapshot &>
tag(Archive &archive) {
std::enable_if_t<(sizeof...(Tag) > 1), const Snapshot &>
tag(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (tag<Tag>(archive), 0)... };
(void)accumulator;
@@ -273,7 +273,7 @@ class SnapshotLoader final {
}
template<typename Archive>
void assure(Archive &archive, bool destroyed) {
void assure(Archive &archive, bool destroyed) const {
Entity length{};
archive(length);
@@ -285,7 +285,7 @@ class SnapshotLoader final {
}
template<typename Type, typename Archive, typename... Args>
void assign(Archive &archive, Args... args) {
void assign(Archive &archive, Args... args) const {
Entity length{};
archive(length);
@@ -321,7 +321,7 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
SnapshotLoader & entities(Archive &archive) {
const SnapshotLoader & entities(Archive &archive) const {
static constexpr auto destroyed = false;
assure(archive, destroyed);
return *this;
@@ -338,7 +338,7 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename Archive>
SnapshotLoader & destroyed(Archive &archive) {
const SnapshotLoader & destroyed(Archive &archive) const {
static constexpr auto destroyed = true;
assure(archive, destroyed);
return *this;
@@ -358,7 +358,7 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename... Component, typename Archive>
SnapshotLoader & component(Archive &archive) {
const SnapshotLoader & component(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (assign<Component>(archive), 0)... };
(void)accumulator;
@@ -379,7 +379,7 @@ public:
* @return A valid loader to continue restoring data.
*/
template<typename... Tag, typename Archive>
SnapshotLoader & tag(Archive &archive) {
const SnapshotLoader & tag(Archive &archive) const {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (assign<Tag>(archive, tag_t{}), 0)... };
(void)accumulator;
@@ -396,7 +396,7 @@ public:
*
* @return A valid loader to continue restoring data.
*/
SnapshotLoader & orphans() {
const SnapshotLoader & orphans() const {
registry.orphans([this](const auto entity) {
registry.destroy(entity);
});
@@ -687,7 +687,7 @@ public:
* @param entity An entity identifier.
* @return True if `entity` is managed by the loader, false otherwise.
*/
bool has(entity_type entity) {
bool has(entity_type entity) const ENTT_NOEXCEPT {
return (remloc.find(entity) != remloc.cend());
}
@@ -703,9 +703,9 @@ public:
* @param entity An entity identifier.
* @return The identifier to which `entity` refers in the target registry.
*/
entity_type map(entity_type entity) {
entity_type map(entity_type entity) const ENTT_NOEXCEPT {
assert(has(entity));
return remloc[entity].first;
return remloc.find(entity)->second.first;
}
private:

View File

@@ -377,28 +377,6 @@ public:
direct.push_back(entity);
}
/**
* @brief Assigns an entity to a sparse set by cloning another entity.
*
* @warning
* Attempting to clone an entity that doesn't belong to the sparse set or to
* assign an entity that already belongs to the sparse set results in
* undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode if the
* sparse set doesn't contain the entity to clone or if it already contains
* the given entity.
*
* @param entity A valid entity identifier.
* @param source A valid entity identifier from which to clone.
*/
inline virtual void clone(const entity_type entity, const entity_type source) {
assert(has(source));
assert(!has(entity));
construct(entity);
// useful to suppress warnings when asserts are disabled
(void)source;
}
/**
* @brief Removes an entity from a sparse set.
*
@@ -842,24 +820,6 @@ public:
return instances.back();
}
/**
* @brief Assigns an entity to a sparse set by cloning another entity.
*
* @warning
* Attempting to clone an entity that doesn't belong to the sparse set or to
* assign an entity that already belongs to the sparse set results in
* undefined behavior.<br/>
* An assertion will abort the execution at runtime in debug mode if the
* sparse set doesn't contain the entity to clone or if it already contains
* the given entity.
*
* @param entity A valid entity identifier.
* @param source A valid entity identifier from which to clone.
*/
inline void clone(const entity_type entity, const entity_type source) override {
construct(entity, get(source));
}
/**
* @brief Removes an entity from a sparse set and destroies its object.
*

View File

@@ -306,7 +306,7 @@ public:
* @return The components assigned to the entity.
*/
template<typename... Comp>
std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
get(const entity_type entity) const ENTT_NOEXCEPT {
assert(contains(entity));
return std::tuple<const Comp &...>{get<Comp>(entity)...};
@@ -330,7 +330,7 @@ public:
* @return The components assigned to the entity.
*/
template<typename... Comp>
std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
get(const entity_type entity) ENTT_NOEXCEPT {
assert(contains(entity));
return std::tuple<Comp &...>{get<Comp>(entity)...};
@@ -819,7 +819,7 @@ public:
* @return The components assigned to the entity.
*/
template<typename... Comp>
std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
get(const entity_type entity) const ENTT_NOEXCEPT {
assert(contains(entity));
return std::tuple<const Comp &...>{get<Comp>(entity)...};
@@ -843,7 +843,7 @@ public:
* @return The components assigned to the entity.
*/
template<typename... Comp>
std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
inline std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
get(const entity_type entity) ENTT_NOEXCEPT {
assert(contains(entity));
return std::tuple<Comp &...>{get<Comp>(entity)...};

View File

@@ -115,7 +115,7 @@ class Process {
}
template<State S, typename... Args>
void tick(char, tag<S>, Args &&...) {}
void tick(char, tag<S>, Args &&...) const ENTT_NOEXCEPT {}
protected:
/**

View File

@@ -94,7 +94,7 @@ public:
* @param args Arguments to use to invoke the underlying function.
* @return The value returned by the underlying function.
*/
Ret operator()(Args... args) {
Ret operator()(Args... args) const {
return stub.second(stub.first, args...);
}

View File

@@ -167,7 +167,7 @@ public:
* delivered to the registered listeners. It's responsibility of the users
* to reduce at a minimum the time spent in the bodies of the listeners.
*/
inline void update() {
inline void update() const {
for(auto pos = wrappers.size(); pos; --pos) {
auto &wrapper = wrappers[pos-1];

View File

@@ -11,6 +11,7 @@
#include <vector>
#include <list>
#include "../config/config.h"
#include "../core/family.hpp"
namespace entt {
@@ -39,6 +40,8 @@ namespace entt {
*/
template<typename Derived>
class Emitter {
using handler_family = Family<struct InternalEmitterHandlerFamily>;
struct BaseHandler {
virtual ~BaseHandler() = default;
virtual bool empty() const ENTT_NOEXCEPT = 0;
@@ -112,20 +115,9 @@ class Emitter {
container_type onL{};
};
static std::size_t next() ENTT_NOEXCEPT {
static std::size_t counter = 0;
return counter++;
}
template<typename>
static std::size_t type() ENTT_NOEXCEPT {
static std::size_t value = next();
return value;
}
template<typename Event>
Handler<Event> & handler() ENTT_NOEXCEPT {
const std::size_t family = type<Event>();
const std::size_t family = handler_family::type<Event>();
if(!(family < handlers.size())) {
handlers.resize(family+1);
@@ -304,8 +296,9 @@ public:
* results in undefined behavior.
*/
void clear() ENTT_NOEXCEPT {
std::for_each(handlers.begin(), handlers.end(),
[](auto &&handler) { if(handler) { handler->clear(); } });
std::for_each(handlers.begin(), handlers.end(), [](auto &&handler) {
return handler ? handler->clear() : void();
});
}
/**
@@ -315,7 +308,7 @@ public:
*/
template<typename Event>
bool empty() const ENTT_NOEXCEPT {
const std::size_t family = type<Event>();
const std::size_t family = handler_family::type<Event>();
return (!(family < handlers.size()) ||
!handlers[family] ||
@@ -327,8 +320,9 @@ public:
* @return True if there are no listeners registered, false otherwise.
*/
bool empty() const ENTT_NOEXCEPT {
return std::all_of(handlers.cbegin(), handlers.cend(),
[](auto &&handler) { return !handler || handler->empty(); });
return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&handler) {
return !handler || handler->empty();
});
}
private:

View File

@@ -31,7 +31,7 @@ struct Invoker<Ret(Args...), Collector> {
virtual ~Invoker() = default;
bool invoke(Collector &collector, proto_type proto, void *instance, Args... args) {
bool invoke(Collector &collector, proto_type proto, void *instance, Args... args) const {
return collector(proto(instance, args...));
}
};
@@ -44,7 +44,7 @@ struct Invoker<void(Args...), Collector> {
virtual ~Invoker() = default;
bool invoke(Collector &, proto_type proto, void *instance, Args... args) {
bool invoke(Collector &, proto_type proto, void *instance, Args... args) const {
return (proto(instance, args...), true);
}
};
@@ -306,7 +306,7 @@ public:
*
* @param args Arguments to use to invoke listeners.
*/
void publish(Args... args) {
void publish(Args... args) const {
for(auto pos = calls.size(); pos; --pos) {
auto &call = calls[pos-1];
call.second(call.first, args...);
@@ -318,7 +318,7 @@ public:
* @param args Arguments to use to invoke listeners.
* @return An instance of the collector filled with collected data.
*/
collector_type collect(Args... args) {
collector_type collect(Args... args) const {
collector_type collector;
for(auto &&call: calls) {

View File

@@ -2,13 +2,73 @@
#include <entt/entity/prototype.hpp>
#include <entt/entity/registry.hpp>
TEST(Prototype, Functionalities) {
TEST(Prototype, SameRegistry) {
entt::DefaultRegistry registry;
entt::DefaultPrototype prototype;
entt::DefaultPrototype prototype{registry};
const auto &cprototype = prototype;
ASSERT_FALSE(registry.empty());
ASSERT_FALSE((prototype.has<int, char>()));
ASSERT_EQ(prototype.set<int>(2), 2);
ASSERT_EQ(prototype.set<int>(3), 3);
ASSERT_EQ(prototype.set<char>('c'), 'c');
ASSERT_EQ(prototype.get<int>(), 3);
ASSERT_EQ(cprototype.get<char>(), 'c');
ASSERT_EQ(std::get<0>(prototype.get<int, char>()), 3);
ASSERT_EQ(std::get<1>(cprototype.get<int, char>()), 'c');
const auto e0 = prototype();
ASSERT_TRUE((prototype.has<int, char>()));
ASSERT_FALSE(registry.orphan(e0));
const auto e1 = prototype();
prototype(e0);
ASSERT_FALSE(registry.orphan(e0));
ASSERT_FALSE(registry.orphan(e1));
ASSERT_TRUE((registry.has<int, char>(e0)));
ASSERT_TRUE((registry.has<int, char>(e1)));
registry.remove<int>(e0);
registry.remove<int>(e1);
prototype.unset<int>();
ASSERT_FALSE((prototype.has<int, char>()));
ASSERT_FALSE((prototype.has<int>()));
ASSERT_TRUE((prototype.has<char>()));
prototype(e0);
prototype(e1);
ASSERT_FALSE(registry.has<int>(e0));
ASSERT_FALSE(registry.has<int>(e1));
ASSERT_EQ(registry.get<char>(e0), 'c');
ASSERT_EQ(registry.get<char>(e1), 'c');
registry.get<char>(e0) = '*';
prototype.assign(e0);
ASSERT_EQ(registry.get<char>(e0), '*');
registry.get<char>(e1) = '*';
prototype.accommodate(e1);
ASSERT_EQ(registry.get<char>(e1), 'c');
}
TEST(Prototype, OtherRegistry) {
entt::DefaultRegistry registry;
entt::DefaultRegistry repository;
entt::DefaultPrototype prototype{repository};
const auto &cprototype = prototype;
ASSERT_TRUE(registry.empty());
ASSERT_FALSE((prototype.has<int, char>()));
ASSERT_EQ(prototype.set<int>(2), 2);
ASSERT_EQ(prototype.set<int>(3), 3);
@@ -23,7 +83,6 @@ TEST(Prototype, Functionalities) {
ASSERT_TRUE((prototype.has<int, char>()));
ASSERT_FALSE(registry.orphan(e0));
ASSERT_FALSE(registry.empty());
const auto e1 = prototype(registry);
prototype(registry, e0);
@@ -61,3 +120,16 @@ TEST(Prototype, Functionalities) {
ASSERT_EQ(registry.get<char>(e1), 'c');
}
TEST(Prototype, RAII) {
entt::DefaultRegistry registry;
{
entt::DefaultPrototype prototype{registry};
prototype.set<int>(0);
ASSERT_FALSE(registry.empty());
}
ASSERT_TRUE(registry.empty());
}

View File

@@ -354,23 +354,6 @@ TEST(DefaultRegistry, CreateDestroyEntities) {
ASSERT_EQ(registry.current(pre), registry.current(post));
}
TEST(DefaultRegistry, CloneEntities) {
entt::DefaultRegistry registry;
const auto entity = registry.create();
registry.assign<int>(entity, 42);
registry.assign<char>(entity, 'c');
const auto other = registry.clone(entity);
ASSERT_TRUE(registry.has<int>(other));
ASSERT_TRUE(registry.has<char>(other));
ASSERT_EQ(registry.get<int>(entity), registry.get<int>(other));
ASSERT_EQ(registry.get<char>(entity), registry.get<char>(other));
ASSERT_EQ(registry.get<int>(other), 42);
ASSERT_EQ(registry.get<char>(other), 'c');
}
TEST(DefaultRegistry, AttachSetRemoveTags) {
entt::DefaultRegistry registry;
const auto &cregistry = registry;
@@ -719,11 +702,6 @@ TEST(DefaultRegistry, ComponentSignals) {
ASSERT_EQ(listener.counter, 1);
ASSERT_EQ(listener.last, e1);
e1 = registry.clone(e0);
ASSERT_EQ(listener.counter, 2);
ASSERT_EQ(listener.last, e1);
}
TEST(DefaultRegistry, TagSignals) {

View File

@@ -54,23 +54,6 @@ TEST(SparseSetNoType, Functionalities) {
other = std::move(set);
}
TEST(SparseSetNoType, Clone) {
entt::SparseSet<unsigned int> set;
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
set.construct(0);
ASSERT_TRUE(set.has(0));
ASSERT_FALSE(set.has(42));
set.clone(42, 0);
ASSERT_TRUE(set.has(0));
ASSERT_TRUE(set.has(42));
}
TEST(SparseSetNoType, DataBeginEnd) {
entt::SparseSet<unsigned int> set;
@@ -324,26 +307,6 @@ TEST(SparseSetWithType, Functionalities) {
other = std::move(set);
}
TEST(SparseSetWithType, Clone) {
entt::SparseSet<unsigned int, int> set;
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
set.construct(0, 3);
ASSERT_TRUE(set.has(0));
ASSERT_FALSE(set.has(42));
ASSERT_EQ(set.get(0), 3);
set.clone(42, 0);
ASSERT_TRUE(set.has(0));
ASSERT_TRUE(set.has(42));
ASSERT_EQ(set.get(0), set.get(42));
ASSERT_EQ(set.get(42), 3);
}
TEST(SparseSetWithType, AggregatesMustWork) {
struct AggregateType { int value; };
// the goal of this test is to enforce the requirements for aggregate types
@@ -723,3 +686,18 @@ TEST(SparseSetWithType, ReferencesGuaranteed) {
ASSERT_EQ(set.get(0).value, 3);
ASSERT_EQ(set.get(1).value, 3);
}
TEST(SparseSetWithType, MoveOnlyComponent) {
struct MoveOnlyComponent {
MoveOnlyComponent() = default;
~MoveOnlyComponent() = default;
MoveOnlyComponent(const MoveOnlyComponent &) = delete;
MoveOnlyComponent(MoveOnlyComponent &&) = default;
MoveOnlyComponent & operator=(const MoveOnlyComponent &) = delete;
MoveOnlyComponent & operator=(MoveOnlyComponent &&) = default;
};
// it's purpose is to ensure that move only components are always accepted
entt::SparseSet<unsigned int, MoveOnlyComponent> set;
(void)set;
}