Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
653ded0e6f | ||
|
|
e34bec7dee | ||
|
|
610b560fb5 | ||
|
|
0a03ddb8a7 | ||
|
|
f31790631a | ||
|
|
e07128760e | ||
|
|
dd02ae313d |
@@ -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)
|
||||
|
||||
34
README.md
34
README.md
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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> ®istry, 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> ®istry, 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> ®istry)
|
||||
: 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 ®istry) {
|
||||
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 ®istry, const entity_type entity) {
|
||||
std::for_each(handlers.begin(), handlers.end(), [®istry, 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 ®istry, const entity_type entity) {
|
||||
std::for_each(handlers.begin(), handlers.end(), [®istry, 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 ®istry, 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 ®istry) 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> ®istry;
|
||||
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>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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)...};
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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)...};
|
||||
|
||||
@@ -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:
|
||||
/**
|
||||
|
||||
@@ -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...);
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user