instance-less pools for empty components
This commit is contained in:
@@ -76,10 +76,17 @@ class basic_registry {
|
||||
{}
|
||||
|
||||
template<typename... Args>
|
||||
Component & assign(basic_registry ®istry, const Entity entt, Args &&... args) {
|
||||
auto &component = storage<Entity, Component>::construct(entt, std::forward<Args>(args)...);
|
||||
on_construct.publish(registry, entt, component);
|
||||
return component;
|
||||
decltype(auto) assign(basic_registry ®istry, const Entity entt, Args &&... args) {
|
||||
if constexpr(std::is_empty_v<Component>) {
|
||||
Component component{std::forward<Args>(args)...};
|
||||
storage<Entity, Component>::construct(entt);
|
||||
on_construct.publish(registry, entt, component);
|
||||
return std::move(component);
|
||||
} else {
|
||||
auto &component = storage<Entity, Component>::construct(entt, std::forward<Args>(args)...);
|
||||
on_construct.publish(registry, entt, component);
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename It>
|
||||
@@ -87,9 +94,15 @@ class basic_registry {
|
||||
auto *component = storage<Entity, Component>::batch(first, last);
|
||||
|
||||
if(!on_construct.empty()) {
|
||||
std::for_each(first, last, [®istry, component, this](const auto entt) mutable {
|
||||
on_construct.publish(registry, entt, *(component++));
|
||||
});
|
||||
if constexpr(std::is_empty_v<Component>) {
|
||||
std::for_each(first, last, [®istry, component = Component{}, this](const auto entt) {
|
||||
on_construct.publish(registry, entt, component);
|
||||
});
|
||||
} else {
|
||||
std::for_each(first, last, [®istry, component, this](const auto entt) mutable {
|
||||
on_construct.publish(registry, entt, *(component++));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return component;
|
||||
@@ -101,10 +114,15 @@ class basic_registry {
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
Component & replace(basic_registry ®istry, const Entity entt, Args &&... args) {
|
||||
decltype(auto) replace(basic_registry ®istry, const Entity entt, [[maybe_unused]] Args &&... args) {
|
||||
Component component{std::forward<Args>(args)...};
|
||||
on_replace.publish(registry, entt, component);
|
||||
return (storage<Entity, Component>::get(entt) = std::move(component));
|
||||
|
||||
if constexpr(std::is_empty_v<Component>) {
|
||||
return std::move(component);
|
||||
} else {
|
||||
return (storage<Entity, Component>::get(entt) = std::move(component));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,25 +163,21 @@ class basic_registry {
|
||||
void maybe_valid_if(basic_registry ®, const Entity entt, const Args &...) {
|
||||
const auto cpools = std::make_tuple(reg.pool<Owned>()...);
|
||||
|
||||
auto construct = [&cpools, entt, this]() {
|
||||
const auto pos = this->owned++;
|
||||
(std::swap(std::get<pool_type<Owned> *>(cpools)->get(entt), std::get<pool_type<Owned> *>(cpools)->raw()[pos]), ...);
|
||||
(std::get<pool_type<Owned> *>(cpools)->swap(std::get<pool_type<Owned> *>(cpools)->sparse_set<Entity>::get(entt), pos), ...);
|
||||
};
|
||||
|
||||
if constexpr(std::disjunction_v<std::is_same<Owned, Component>..., std::is_same<Get, Component>...>) {
|
||||
if(((std::is_same_v<Component, Owned> || std::get<pool_type<Owned> *>(cpools)->has(entt)) && ...)
|
||||
&& ((std::is_same_v<Component, Get> || reg.pool<Get>()->has(entt)) && ...)
|
||||
&& !(reg.pool<Exclude>()->has(entt) || ...))
|
||||
{
|
||||
construct();
|
||||
const auto pos = this->owned++;
|
||||
(reg.swap<Owned>(0, std::get<pool_type<Owned> *>(cpools), entt, pos), ...);
|
||||
}
|
||||
} else if constexpr(std::disjunction_v<std::is_same<Exclude, Component>...>) {
|
||||
if((std::get<pool_type<Owned> *>(cpools)->has(entt) && ...)
|
||||
&& (reg.pool<Get>()->has(entt) && ...)
|
||||
&& ((std::is_same_v<Exclude, Component> || !reg.pool<Exclude>()->has(entt)) && ...))
|
||||
{
|
||||
construct();
|
||||
const auto pos = this->owned++;
|
||||
(reg.swap<Owned>(0, std::get<pool_type<Owned> *>(cpools), entt, pos), ...);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -174,8 +188,7 @@ class basic_registry {
|
||||
|
||||
if(std::get<0>(cpools)->has(entt) && std::get<0>(cpools)->sparse_set<Entity>::get(entt) < this->owned) {
|
||||
const auto pos = --this->owned;
|
||||
(std::swap(std::get<pool_type<Owned> *>(cpools)->get(entt), std::get<pool_type<Owned> *>(cpools)->raw()[pos]), ...);
|
||||
(std::get<pool_type<Owned> *>(cpools)->swap(std::get<pool_type<Owned> *>(cpools)->sparse_set<Entity>::get(entt), pos), ...);
|
||||
(reg.swap<Owned>(0, std::get<pool_type<Owned> *>(cpools), entt, pos), ...);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -217,6 +230,18 @@ class basic_registry {
|
||||
++available;
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline auto swap(int, pool_type<Component> *cpool, const Entity entt, const std::size_t pos)
|
||||
-> decltype(std::swap(cpool->get(entt), cpool->get(cpool->data()[pos])), void()) {
|
||||
std::swap(cpool->get(entt), cpool->get(cpool->data()[pos]));
|
||||
cpool->swap(cpool->sparse_set<Entity>::get(entt), pos);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline void swap(char, pool_type<Component> *cpool, const Entity entt, const std::size_t pos) {
|
||||
cpool->swap(cpool->sparse_set<Entity>::get(entt), pos);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
inline const auto * pool() const ENTT_NOEXCEPT {
|
||||
const auto ctype = type<Component>();
|
||||
@@ -694,7 +719,7 @@ public:
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
Component & assign(const entity_type entity, Args &&... args) {
|
||||
decltype(auto) assign(const entity_type entity, [[maybe_unused]] Args &&... args) {
|
||||
ENTT_ASSERT(valid(entity));
|
||||
return assure<Component>()->assign(*this, entity, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -797,7 +822,7 @@ public:
|
||||
* @return Reference to the component owned by the entity.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
Component & get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT {
|
||||
decltype(auto) get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(valid(entity));
|
||||
auto *cpool = assure<Component>();
|
||||
auto *comp = cpool->try_get(entity);
|
||||
@@ -859,7 +884,7 @@ public:
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
Component & replace(const entity_type entity, Args &&... args) {
|
||||
decltype(auto) replace(const entity_type entity, Args &&... args) {
|
||||
return pool<Component>()->replace(*this, entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -886,7 +911,7 @@ public:
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
Component & assign_or_replace(const entity_type entity, Args &&... args) {
|
||||
decltype(auto) assign_or_replace(const entity_type entity, Args &&... args) {
|
||||
auto *cpool = assure<Component>();
|
||||
return cpool->has(entity) ? cpool->replace(*this, entity, std::forward<Args>(args)...) : cpool->assign(*this, entity, std::forward<Args>(args)...);
|
||||
}
|
||||
@@ -1290,6 +1315,7 @@ public:
|
||||
inline entt::basic_group<Entity, get_t<Get...>, Owned...> group(get_t<Get...>, exclude_t<Exclude...> = {}) {
|
||||
static_assert(sizeof...(Owned) + sizeof...(Get) > 0);
|
||||
static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1);
|
||||
|
||||
using handler_type = group_handler<type_list<Exclude...>, type_list<Get...>, Owned...>;
|
||||
|
||||
const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) };
|
||||
@@ -1336,7 +1362,7 @@ public:
|
||||
});
|
||||
|
||||
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
|
||||
std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools](const auto entity) {
|
||||
std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr, &cpools, this](const auto entity) {
|
||||
if((std::get<pool_type<Owned> *>(cpools)->has(entity) && ...)
|
||||
&& (std::get<pool_type<Get> *>(cpools)->has(entity) && ...)
|
||||
&& !(std::get<pool_type<Exclude> *>(cpools)->has(entity) || ...))
|
||||
@@ -1345,8 +1371,7 @@ public:
|
||||
curr->construct(entity);
|
||||
} else {
|
||||
const auto pos = curr->owned++;
|
||||
(std::swap(std::get<pool_type<Owned> *>(cpools)->get(entity), std::get<pool_type<Owned> *>(cpools)->raw()[pos]), ...);
|
||||
(std::get<pool_type<Owned> *>(cpools)->swap(std::get<pool_type<Owned> *>(cpools)->sparse_set<Entity>::get(entity), pos), ...);
|
||||
(swap<Owned>(0, std::get<pool_type<Owned> *>(cpools), entity, pos), ...);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,18 +226,19 @@ class basic_snapshot_loader {
|
||||
archive(length);
|
||||
|
||||
while(length--) {
|
||||
static constexpr auto destroyed = false;
|
||||
Entity entt{};
|
||||
Type instance{};
|
||||
|
||||
if constexpr(std::is_empty_v<Type>) {
|
||||
archive(entt);
|
||||
force(*reg, entt, destroyed);
|
||||
reg->template assign<Type>(args..., entt);
|
||||
} else {
|
||||
Type instance{};
|
||||
archive(entt, instance);
|
||||
force(*reg, entt, destroyed);
|
||||
reg->template assign<Type>(args..., entt, std::as_const(instance));
|
||||
}
|
||||
|
||||
static constexpr auto destroyed = false;
|
||||
force(*reg, entt, destroyed);
|
||||
reg->template assign<Type>(args..., entt, std::as_const(instance));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,24 +406,25 @@ class basic_continuous_loader {
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Other, typename Archive, typename Func, typename... Type, typename... Member>
|
||||
void assign(Archive &archive, Func func, Member Type:: *... member) {
|
||||
template<typename Other, typename Archive, typename... Type, typename... Member>
|
||||
void assign(Archive &archive, [[maybe_unused]] Member Type:: *... member) {
|
||||
Entity length{};
|
||||
archive(length);
|
||||
|
||||
while(length--) {
|
||||
Entity entt{};
|
||||
Other instance{};
|
||||
|
||||
if constexpr(std::is_empty_v<Other>) {
|
||||
archive(entt);
|
||||
restore(entt);
|
||||
reg->template assign_or_replace<Other>(map(entt));
|
||||
} else {
|
||||
Other instance{};
|
||||
archive(entt, instance);
|
||||
(update(instance, member), ...);
|
||||
restore(entt);
|
||||
reg->template assign_or_replace<Other>(map(entt), std::as_const(instance));
|
||||
}
|
||||
|
||||
restore(entt);
|
||||
(update(instance, member), ...);
|
||||
func(map(entt), instance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,12 +499,8 @@ public:
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename... Type, typename... Member>
|
||||
basic_continuous_loader & component(Archive &archive, Member Type:: *... member) {
|
||||
auto apply = [this](const auto entt, const auto &component) {
|
||||
reg->template assign_or_replace<std::decay_t<decltype(component)>>(entt, component);
|
||||
};
|
||||
|
||||
(reset<Component>(), ...);
|
||||
(assign<Component>(archive, apply, member...), ...);
|
||||
(assign<Component>(archive, member...), ...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@ class sparse_set {
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = const Entity;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using value_type = Entity;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace entt {
|
||||
* @brief Basic storage implementation.
|
||||
*
|
||||
* This class is a refinement of a sparse set that associates an object to an
|
||||
* entity. The main purpose of this class is to use sparse sets to store
|
||||
* entity. The main purpose of this class is to extend sparse sets to store
|
||||
* components in a registry. It guarantees fast access both to the elements and
|
||||
* to the entities.
|
||||
*
|
||||
@@ -36,19 +36,24 @@ namespace entt {
|
||||
* iterate directly the internal packed array (see `raw` and `size` member
|
||||
* functions for that). Use `begin` and `end` instead.
|
||||
*
|
||||
* @warning
|
||||
* Empty types aren't explicitly instantiated. Temporary objects are returned in
|
||||
* place of the instances of the components and raw access isn't available for
|
||||
* them.
|
||||
*
|
||||
* @sa sparse_set<Entity>
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
*/
|
||||
template<typename Entity, typename Type>
|
||||
class storage: public sparse_set<Entity> {
|
||||
template<typename Entity, typename Type, typename = std::void_t<>>
|
||||
class basic_storage: public sparse_set<Entity> {
|
||||
using underlying_type = sparse_set<Entity>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<bool Const, bool = std::is_empty_v<Type>>
|
||||
template<bool Const>
|
||||
class iterator {
|
||||
friend class storage<Entity, Type>;
|
||||
friend class basic_storage<Entity, Type>;
|
||||
|
||||
using instance_type = std::conditional_t<Const, const std::vector<Type>, std::vector<Type>>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
@@ -59,9 +64,9 @@ class storage: public sparse_set<Entity> {
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = std::conditional_t<Const, const Type, Type>;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using value_type = Type;
|
||||
using pointer = std::conditional_t<Const, const value_type *, value_type *>;
|
||||
using reference = std::conditional_t<Const, const value_type &, value_type &>;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
@@ -148,106 +153,6 @@ class storage: public sparse_set<Entity> {
|
||||
index_type index;
|
||||
};
|
||||
|
||||
template<bool Const>
|
||||
class iterator<Const, true> {
|
||||
friend class storage<Entity, Type>;
|
||||
|
||||
using instance_type = std::conditional_t<Const, const Type, Type>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
iterator(instance_type *ref, const index_type idx) ENTT_NOEXCEPT
|
||||
: instance{ref}, index{idx}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = std::conditional_t<Const, const Type, Type>;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return iterator{instance, index-value};
|
||||
}
|
||||
|
||||
inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
reference operator[](const difference_type) const ENTT_NOEXCEPT {
|
||||
return *instance;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return instance;
|
||||
}
|
||||
|
||||
inline reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
instance_type *instance;
|
||||
index_type index;
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Type of the objects associated with the entities. */
|
||||
using object_type = Type;
|
||||
@@ -270,25 +175,13 @@ public:
|
||||
*/
|
||||
void reserve(const size_type cap) override {
|
||||
underlying_type::reserve(cap);
|
||||
|
||||
if constexpr(!std::is_empty_v<object_type>) {
|
||||
instances.reserve(cap);
|
||||
}
|
||||
instances.reserve(cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Requests the removal of unused capacity.
|
||||
*
|
||||
* @note
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function does nothing.
|
||||
*/
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() override {
|
||||
underlying_type::shrink_to_fit();
|
||||
|
||||
if constexpr(!std::is_empty_v<object_type>) {
|
||||
instances.shrink_to_fit();
|
||||
}
|
||||
instances.shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,19 +197,10 @@ public:
|
||||
* performance boost but less guarantees. Use `begin` and `end` if you want
|
||||
* to iterate the storage in the expected order.
|
||||
*
|
||||
* @note
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function always returns a pointer
|
||||
* to that instance.
|
||||
*
|
||||
* @return A pointer to the array of objects.
|
||||
*/
|
||||
const object_type * raw() const ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_empty_v<object_type>) {
|
||||
return &instances;
|
||||
} else {
|
||||
return instances.data();
|
||||
}
|
||||
return instances.data();
|
||||
}
|
||||
|
||||
/*! @copydoc raw */
|
||||
@@ -392,13 +276,8 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object associated with the entity.
|
||||
*/
|
||||
const object_type & get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_empty_v<object_type>) {
|
||||
ENTT_ASSERT(underlying_type::has(entt));
|
||||
return instances;
|
||||
} else {
|
||||
return instances[underlying_type::get(entt)];
|
||||
}
|
||||
const object_type & get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
return instances[underlying_type::get(entt)];
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
@@ -412,11 +291,7 @@ public:
|
||||
* @return The object associated with the entity, if any.
|
||||
*/
|
||||
const object_type * try_get(const entity_type entt) const ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_empty_v<object_type>) {
|
||||
return underlying_type::has(entt) ? &instances : nullptr;
|
||||
} else {
|
||||
return underlying_type::has(entt) ? (instances.data() + underlying_type::get(entt)) : nullptr;
|
||||
}
|
||||
return underlying_type::has(entt) ? (instances.data() + underlying_type::get(entt)) : nullptr;
|
||||
}
|
||||
|
||||
/*! @copydoc try_get */
|
||||
@@ -443,21 +318,16 @@ public:
|
||||
* @return The object associated with the entity.
|
||||
*/
|
||||
template<typename... Args>
|
||||
object_type & construct(const entity_type entt, [[maybe_unused]] Args &&... args) {
|
||||
if constexpr(std::is_empty_v<object_type>) {
|
||||
underlying_type::construct(entt);
|
||||
return instances;
|
||||
object_type & construct(const entity_type entt, Args &&... args) {
|
||||
if constexpr(std::is_aggregate_v<object_type>) {
|
||||
instances.emplace_back(Type{std::forward<Args>(args)...});
|
||||
} else {
|
||||
if constexpr(std::is_aggregate_v<object_type>) {
|
||||
instances.emplace_back(Type{std::forward<Args>(args)...});
|
||||
} else {
|
||||
instances.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// entity goes after component in case constructor throws
|
||||
underlying_type::construct(entt);
|
||||
return instances.back();
|
||||
instances.emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// entity goes after component in case constructor throws
|
||||
underlying_type::construct(entt);
|
||||
return instances.back();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -466,11 +336,6 @@ public:
|
||||
*
|
||||
* The object type must be at least default constructible.
|
||||
*
|
||||
* @note
|
||||
* Empty components aren't explicitly instantiated. Only one instance of the
|
||||
* given type is created. Therefore, this function always returns a pointer
|
||||
* to that instance.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to assign an entity that already belongs to the storage
|
||||
* results in undefined behavior.<br/>
|
||||
@@ -485,17 +350,12 @@ public:
|
||||
*/
|
||||
template<typename It>
|
||||
object_type * batch(It first, It last) {
|
||||
if constexpr(std::is_empty_v<object_type>) {
|
||||
underlying_type::batch(first, last);
|
||||
return &instances;
|
||||
} else {
|
||||
static_assert(std::is_default_constructible_v<object_type>);
|
||||
const auto skip = instances.size();
|
||||
instances.insert(instances.end(), last-first, {});
|
||||
// entity goes after component in case constructor throws
|
||||
underlying_type::batch(first, last);
|
||||
return instances.data() + skip;
|
||||
}
|
||||
static_assert(std::is_default_constructible_v<object_type>);
|
||||
const auto skip = instances.size();
|
||||
instances.insert(instances.end(), last-first, {});
|
||||
// entity goes after component in case constructor throws
|
||||
underlying_type::batch(first, last);
|
||||
return instances.data() + skip;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -510,16 +370,13 @@ public:
|
||||
* @param entt A valid entity identifier.
|
||||
*/
|
||||
void destroy(const entity_type entt) override {
|
||||
if constexpr(!std::is_empty_v<object_type>) {
|
||||
std::swap(instances[underlying_type::get(entt)], instances.back());
|
||||
instances.pop_back();
|
||||
}
|
||||
|
||||
std::swap(instances[underlying_type::get(entt)], instances.back());
|
||||
instances.pop_back();
|
||||
underlying_type::destroy(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort components according to the given comparison function.
|
||||
* @brief Sort instances according to the given comparison function.
|
||||
*
|
||||
* Sort the elements so that iterating the storage with a couple of
|
||||
* iterators returns them in the expected order. See `begin` and `end` for
|
||||
@@ -549,10 +406,6 @@ public:
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Empty components aren't explicitly instantiated. Therefore, the
|
||||
* comparison function must necessarily accept entity identifiers.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* either `data` or `raw` gives no guarantees on the order, even though
|
||||
* `sort` has been invoked.
|
||||
@@ -588,11 +441,7 @@ public:
|
||||
while(curr != next) {
|
||||
const auto lhs = copy[curr];
|
||||
const auto rhs = copy[next];
|
||||
|
||||
if constexpr(!std::is_empty_v<object_type>) {
|
||||
std::swap(instances[lhs], instances[rhs]);
|
||||
}
|
||||
|
||||
std::swap(instances[lhs], instances[rhs]);
|
||||
underlying_type::swap(lhs, rhs);
|
||||
copy[curr] = curr;
|
||||
curr = next;
|
||||
@@ -602,14 +451,13 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort components according to the order of the entities in another
|
||||
* @brief Sort instances according to the order of the entities in another
|
||||
* sparse set.
|
||||
*
|
||||
* Entities that are part of both the storage are ordered internally
|
||||
* according to the order they have in `other`. All the other entities goes
|
||||
* to the end of the list and there are no guarantess on their order.
|
||||
* Components are sorted according to the entities to which they
|
||||
* belong.<br/>
|
||||
* Instances are sorted according to the entities to which they belong.<br/>
|
||||
* In other terms, this function can be used to impose the same order on two
|
||||
* sets by using one of them as a master and the other one as a slave.
|
||||
*
|
||||
@@ -625,47 +473,253 @@ public:
|
||||
* @param other The sparse sets that imposes the order of the entities.
|
||||
*/
|
||||
void respect(const sparse_set<Entity> &other) ENTT_NOEXCEPT override {
|
||||
if constexpr(std::is_empty_v<object_type>) {
|
||||
underlying_type::respect(other);
|
||||
} else {
|
||||
const auto to = other.end();
|
||||
auto from = other.begin();
|
||||
const auto to = other.end();
|
||||
auto from = other.begin();
|
||||
|
||||
size_type pos = underlying_type::size() - 1;
|
||||
const auto *local = underlying_type::data();
|
||||
size_type pos = underlying_type::size() - 1;
|
||||
const auto *local = underlying_type::data();
|
||||
|
||||
while(pos && from != to) {
|
||||
const auto curr = *from;
|
||||
while(pos && from != to) {
|
||||
const auto curr = *from;
|
||||
|
||||
if(underlying_type::has(curr)) {
|
||||
if(curr != *(local + pos)) {
|
||||
auto candidate = underlying_type::get(curr);
|
||||
std::swap(instances[pos], instances[candidate]);
|
||||
underlying_type::swap(pos, candidate);
|
||||
}
|
||||
|
||||
--pos;
|
||||
if(underlying_type::has(curr)) {
|
||||
if(curr != *(local + pos)) {
|
||||
auto candidate = underlying_type::get(curr);
|
||||
std::swap(instances[pos], instances[candidate]);
|
||||
underlying_type::swap(pos, candidate);
|
||||
}
|
||||
|
||||
++from;
|
||||
--pos;
|
||||
}
|
||||
|
||||
++from;
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Resets a storage. */
|
||||
void reset() override {
|
||||
underlying_type::reset();
|
||||
|
||||
if constexpr(!std::is_empty_v<object_type>) {
|
||||
instances.clear();
|
||||
}
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::conditional_t<std::is_empty_v<object_type>, object_type, std::vector<object_type>> instances;
|
||||
std::vector<object_type> instances;
|
||||
};
|
||||
|
||||
|
||||
/*! @copydoc basic_storage */
|
||||
template<typename Entity, typename Type>
|
||||
class basic_storage<Entity, Type, std::enable_if_t<std::is_empty_v<Type>>>: public sparse_set<Entity> {
|
||||
using underlying_type = sparse_set<Entity>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
class iterator {
|
||||
friend class basic_storage<Entity, Type>;
|
||||
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
iterator(const index_type idx) ENTT_NOEXCEPT
|
||||
: index{idx}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = Type;
|
||||
using pointer = const value_type *;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
iterator operator--(int) ENTT_NOEXCEPT {
|
||||
iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
}
|
||||
|
||||
iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return iterator{index-value};
|
||||
}
|
||||
|
||||
inline iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
inline iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
reference operator[](const difference_type) const ENTT_NOEXCEPT {
|
||||
return {};
|
||||
}
|
||||
|
||||
bool operator==(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
inline bool operator!=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
inline bool operator<=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
inline bool operator>=(const iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline reference operator*() const ENTT_NOEXCEPT {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
index_type index;
|
||||
};
|
||||
|
||||
public:
|
||||
/*! @brief Type of the objects associated with the entities. */
|
||||
using object_type = Type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename underlying_type::size_type;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator_type = iterator;
|
||||
|
||||
/**
|
||||
* @brief Direct access to the array of objects.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, this function
|
||||
* always returns a null pointer.
|
||||
*
|
||||
* @return A pointer to the array of objects.
|
||||
*/
|
||||
const object_type * raw() const ENTT_NOEXCEPT {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the given type. If
|
||||
* the storage is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed by a call to either `sort`
|
||||
* or `respect`.
|
||||
*
|
||||
* @return An iterator to the first instance of the given type.
|
||||
*/
|
||||
iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const typename traits_type::difference_type pos = underlying_type::size();
|
||||
return iterator_type{pos};
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
inline iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the given type. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed by a call to either `sort`
|
||||
* or `respect`.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* given type.
|
||||
*/
|
||||
iterator_type cend() const ENTT_NOEXCEPT {
|
||||
return iterator_type{};
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
inline iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object associated with an entity.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, this function
|
||||
* always returns a temporary object.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the storage results in
|
||||
* undefined behavior.<br/>
|
||||
* An assertion will abort the execution at runtime in debug mode if the
|
||||
* storage doesn't contain the given entity.
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object associated with the entity.
|
||||
*/
|
||||
object_type get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(underlying_type::has(entt));
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the object associated with an entity, if any.
|
||||
*
|
||||
* @note
|
||||
* Empty types aren't explicitly instantiated. Therefore, this function
|
||||
* always returns a null pointer.
|
||||
*
|
||||
* @param entt A valid entity identifier.
|
||||
* @return The object associated with the entity, if any.
|
||||
*/
|
||||
const object_type * try_get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(underlying_type::has(entt));
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/*! @copydoc basic_storage */
|
||||
template<typename Entity, typename Type>
|
||||
struct storage: basic_storage<Entity, Type> {};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -161,20 +161,28 @@ class basic_view {
|
||||
return other;
|
||||
}
|
||||
|
||||
template<typename Comp, typename Other>
|
||||
inline decltype(auto) get([[maybe_unused]] component_iterator_type<Comp> it, [[maybe_unused]] pool_type<Other> *cpool, [[maybe_unused]] const Entity entt) const ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_same_v<Comp, Other>) {
|
||||
return *it;
|
||||
} else {
|
||||
return cpool->get(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Comp, typename... Other, typename Func>
|
||||
void each(type_list<Other...>, Func func) const {
|
||||
const auto end = std::get<pool_type<Comp> *>(pools)->sparse_set<Entity>::end();
|
||||
auto begin = std::get<pool_type<Comp> *>(pools)->sparse_set<Entity>::begin();
|
||||
|
||||
std::for_each(begin, end, [&func, raw = std::get<pool_type<Comp> *>(pools)->begin(), this](const auto entity) mutable {
|
||||
std::tuple<component_iterator_type<Comp>, Other *...> pack;
|
||||
std::get<component_iterator_type<Comp>>(pack) = raw++;
|
||||
std::for_each(begin, end, [raw = std::get<pool_type<Comp> *>(pools)->begin(), &func, this](const auto entity) mutable {
|
||||
auto curr = raw++;
|
||||
|
||||
if(((std::get<Other *>(pack) = std::get<pool_type<Other> *>(pools)->try_get(entity)) && ...)) {
|
||||
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
|
||||
if constexpr(std::is_invocable_v<Func, std::add_lvalue_reference_t<Component>...>) {
|
||||
func(*std::get<std::conditional_t<std::is_same_v<Comp, Component>, component_iterator_type<Component>, Component *>>(pack)...);
|
||||
func(get<Comp, Component>(curr, std::get<pool_type<Component> *>(pools), entity)...);
|
||||
} else {
|
||||
func(entity, *std::get<std::conditional_t<std::is_same_v<Comp, Component>, component_iterator_type<Component>, Component *>>(pack)...);
|
||||
func(entity, get<Comp, Component>(curr, std::get<pool_type<Component> *>(pools), entity)...);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -131,8 +131,7 @@ class meta_factory {
|
||||
|
||||
template<auto Member>
|
||||
auto unregister_all(int)
|
||||
-> decltype((internal::meta_info<Type>::type->*Member)->prop, void())
|
||||
{
|
||||
-> decltype((internal::meta_info<Type>::type->*Member)->prop, void()) {
|
||||
while(internal::meta_info<Type>::type->*Member) {
|
||||
auto node = internal::meta_info<Type>::type->*Member;
|
||||
internal::meta_info<Type>::type->*Member = node->next;
|
||||
|
||||
@@ -222,8 +222,7 @@ auto find_if(Op op, const Node *curr) ENTT_NOEXCEPT {
|
||||
|
||||
template<auto Member, typename Op>
|
||||
auto find_if(Op op, const meta_type_node *node) ENTT_NOEXCEPT
|
||||
-> decltype(find_if(op, node->*Member))
|
||||
{
|
||||
-> decltype(find_if(op, node->*Member)) {
|
||||
decltype(find_if(op, node->*Member)) ret = nullptr;
|
||||
|
||||
if(node) {
|
||||
@@ -311,8 +310,7 @@ class meta_any {
|
||||
|
||||
template<typename Type>
|
||||
inline static auto compare(int, const Type &lhs, const Type &rhs)
|
||||
-> decltype(lhs == rhs, bool{})
|
||||
{
|
||||
-> decltype(lhs == rhs, bool{}) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
|
||||
@@ -366,12 +366,11 @@ TEST(NonOwningGroup, EmptyAndNonEmptyTypes) {
|
||||
ASSERT_TRUE(entity == e0 || entity == e1);
|
||||
}
|
||||
|
||||
group.each([e0, e1](const auto entity, const int &, const empty_type &) {
|
||||
group.each([e0, e1](const auto entity, const int &, empty_type) {
|
||||
ASSERT_TRUE(entity == e0 || entity == e1);
|
||||
});
|
||||
|
||||
ASSERT_EQ(group.size(), typename decltype(group)::size_type{2});
|
||||
ASSERT_EQ(&group.get<empty_type>(e0), &group.get<empty_type>(e1));
|
||||
}
|
||||
|
||||
TEST(NonOwningGroup, TrackEntitiesOnComponentDestruction) {
|
||||
@@ -872,12 +871,11 @@ TEST(OwningGroup, EmptyAndNonEmptyTypes) {
|
||||
ASSERT_TRUE(entity == e0 || entity == e1);
|
||||
}
|
||||
|
||||
group.each([e0, e1](const auto entity, const empty_type &, const int &) {
|
||||
group.each([e0, e1](const auto entity, empty_type, const int &) {
|
||||
ASSERT_TRUE(entity == e0 || entity == e1);
|
||||
});
|
||||
|
||||
ASSERT_EQ(group.size(), typename decltype(group)::size_type{2});
|
||||
ASSERT_EQ(&group.get<empty_type>(e0), &group.get<empty_type>(e1));
|
||||
}
|
||||
|
||||
TEST(OwningGroup, TrackEntitiesOnComponentDestruction) {
|
||||
|
||||
@@ -248,7 +248,6 @@ TEST(Snapshot, Continuous) {
|
||||
|
||||
using storage_type = std::tuple<
|
||||
std::queue<entt::entity>,
|
||||
std::queue<a_component>,
|
||||
std::queue<another_component>,
|
||||
std::queue<what_a_component>,
|
||||
std::queue<double>
|
||||
@@ -432,10 +431,7 @@ TEST(Snapshot, MoreOnShrink) {
|
||||
|
||||
entt::continuous_loader loader{dst};
|
||||
|
||||
using storage_type = std::tuple<
|
||||
std::queue<entt::entity>,
|
||||
std::queue<a_component>
|
||||
>;
|
||||
using storage_type = std::tuple<std::queue<entt::entity>>;
|
||||
|
||||
storage_type storage;
|
||||
output_archive<storage_type> output{storage};
|
||||
|
||||
@@ -19,7 +19,7 @@ struct throwing_component {
|
||||
int data;
|
||||
};
|
||||
|
||||
TEST(SparseSetWithType, Functionalities) {
|
||||
TEST(Storage, Functionalities) {
|
||||
entt::storage<std::uint64_t, int> set;
|
||||
|
||||
set.reserve(42);
|
||||
@@ -79,16 +79,23 @@ TEST(SparseSetWithType, Functionalities) {
|
||||
other = std::move(set);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, EmptyType) {
|
||||
TEST(Storage, EmptyType) {
|
||||
entt::storage<std::uint64_t, empty_type> set;
|
||||
|
||||
ASSERT_EQ(&set.construct(42), &set.construct(99));
|
||||
ASSERT_EQ(&set.get(42), set.try_get(42));
|
||||
ASSERT_EQ(&set.get(42), &set.get(99));
|
||||
set.construct(42);
|
||||
set.construct(99);
|
||||
|
||||
ASSERT_TRUE(set.has(42));
|
||||
ASSERT_TRUE(set.has(99));
|
||||
ASSERT_EQ(set.try_get(42), nullptr);
|
||||
ASSERT_EQ(std::as_const(set).try_get(42), std::as_const(set).try_get(99));
|
||||
|
||||
auto &&component = set.get(42);
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, BatchAdd) {
|
||||
TEST(Storage, BatchAdd) {
|
||||
entt::storage<std::uint64_t, int> set;
|
||||
entt::storage<std::uint64_t, int>::entity_type entities[2];
|
||||
|
||||
@@ -121,7 +128,7 @@ TEST(SparseSetWithType, BatchAdd) {
|
||||
ASSERT_EQ(set.get(entities[1]), 2);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, BatchAddEmptyType) {
|
||||
TEST(Storage, BatchAddEmptyType) {
|
||||
entt::storage<std::uint64_t, empty_type> set;
|
||||
entt::storage<std::uint64_t, empty_type>::entity_type entities[2];
|
||||
|
||||
@@ -130,7 +137,7 @@ TEST(SparseSetWithType, BatchAddEmptyType) {
|
||||
|
||||
set.reserve(4);
|
||||
set.construct(12);
|
||||
auto *component = set.batch(std::begin(entities), std::end(entities));
|
||||
set.batch(std::begin(entities), std::end(entities));
|
||||
set.construct(24);
|
||||
|
||||
ASSERT_TRUE(set.has(entities[0]));
|
||||
@@ -142,26 +149,26 @@ TEST(SparseSetWithType, BatchAddEmptyType) {
|
||||
|
||||
ASSERT_FALSE(set.empty());
|
||||
ASSERT_EQ(set.size(), 4u);
|
||||
ASSERT_EQ(&set.get(entities[0]), &set.get(entities[1]));
|
||||
ASSERT_EQ(&set.get(entities[0]), &set.get(12));
|
||||
ASSERT_EQ(&set.get(entities[0]), &set.get(24));
|
||||
ASSERT_EQ(&set.get(entities[0]), component);
|
||||
|
||||
auto &&component = set.get(entities[0]);
|
||||
|
||||
ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, AggregatesMustWork) {
|
||||
TEST(Storage, AggregatesMustWork) {
|
||||
struct aggregate_type { int value; };
|
||||
// the goal of this test is to enforce the requirements for aggregate types
|
||||
entt::storage<std::uint64_t, aggregate_type>{}.construct(0, 42);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, TypesFromStandardTemplateLibraryMustWork) {
|
||||
TEST(Storage, TypesFromStandardTemplateLibraryMustWork) {
|
||||
// see #37 - this test shouldn't crash, that's all
|
||||
entt::storage<std::uint64_t, std::unordered_set<int>> set;
|
||||
set.construct(0).insert(42);
|
||||
set.destroy(0);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, Iterator) {
|
||||
TEST(Storage, Iterator) {
|
||||
using iterator_type = typename entt::storage<std::uint64_t, boxed_int>::iterator_type;
|
||||
|
||||
entt::storage<std::uint64_t, boxed_int> set;
|
||||
@@ -203,7 +210,7 @@ TEST(SparseSetWithType, Iterator) {
|
||||
ASSERT_GE(end, set.end());
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, ConstIterator) {
|
||||
TEST(Storage, ConstIterator) {
|
||||
using iterator_type = typename entt::storage<std::uint64_t, boxed_int>::const_iterator_type;
|
||||
|
||||
entt::storage<std::uint64_t, boxed_int> set;
|
||||
@@ -245,7 +252,7 @@ TEST(SparseSetWithType, ConstIterator) {
|
||||
ASSERT_GE(cend, set.cend());
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, IteratorEmptyType) {
|
||||
TEST(Storage, IteratorEmptyType) {
|
||||
using iterator_type = typename entt::storage<std::uint64_t, empty_type>::iterator_type;
|
||||
entt::storage<std::uint64_t, empty_type> set;
|
||||
set.construct(3);
|
||||
@@ -277,7 +284,7 @@ TEST(SparseSetWithType, IteratorEmptyType) {
|
||||
ASSERT_EQ(end - (end - begin), set.begin());
|
||||
ASSERT_EQ(end + (begin - end), set.begin());
|
||||
|
||||
ASSERT_EQ(&begin[0], set.begin().operator->());
|
||||
ASSERT_EQ(set.begin().operator->(), nullptr);
|
||||
|
||||
ASSERT_LT(begin, end);
|
||||
ASSERT_LE(begin, set.begin());
|
||||
@@ -286,60 +293,12 @@ TEST(SparseSetWithType, IteratorEmptyType) {
|
||||
ASSERT_GE(end, set.end());
|
||||
|
||||
set.construct(33);
|
||||
auto &&component = *begin;
|
||||
|
||||
ASSERT_EQ(&*begin, &*(begin+1));
|
||||
ASSERT_EQ(&begin[0], &begin[1]);
|
||||
ASSERT_EQ(begin.operator->(), (end-1).operator->());
|
||||
ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, ConstIteratorEmptyType) {
|
||||
using iterator_type = typename entt::storage<std::uint64_t, empty_type>::const_iterator_type;
|
||||
entt::storage<std::uint64_t, empty_type> set;
|
||||
set.construct(3);
|
||||
|
||||
iterator_type cend{set.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = set.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, set.cbegin());
|
||||
ASSERT_EQ(cend, set.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin++, set.cbegin());
|
||||
ASSERT_EQ(cbegin--, set.cend());
|
||||
|
||||
ASSERT_EQ(cbegin+1, set.cend());
|
||||
ASSERT_EQ(cend-1, set.cbegin());
|
||||
|
||||
ASSERT_EQ(++cbegin, set.cend());
|
||||
ASSERT_EQ(--cbegin, set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin += 1, set.cend());
|
||||
ASSERT_EQ(cbegin -= 1, set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin + (cend - cbegin), set.cend());
|
||||
ASSERT_EQ(cbegin - (cbegin - cend), set.cend());
|
||||
|
||||
ASSERT_EQ(cend - (cend - cbegin), set.cbegin());
|
||||
ASSERT_EQ(cend + (cbegin - cend), set.cbegin());
|
||||
|
||||
ASSERT_EQ(&cbegin[0], set.cbegin().operator->());
|
||||
|
||||
ASSERT_LT(cbegin, cend);
|
||||
ASSERT_LE(cbegin, set.cbegin());
|
||||
|
||||
ASSERT_GT(cend, cbegin);
|
||||
ASSERT_GE(cend, set.cend());
|
||||
|
||||
set.construct(33);
|
||||
|
||||
ASSERT_EQ(&*cbegin, &*(cbegin+1));
|
||||
ASSERT_EQ(&cbegin[0], &cbegin[1]);
|
||||
ASSERT_EQ(cbegin.operator->(), (cend-1).operator->());
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, Raw) {
|
||||
TEST(Storage, Raw) {
|
||||
entt::storage<std::uint64_t, int> set;
|
||||
|
||||
set.construct(3, 3);
|
||||
@@ -355,7 +314,7 @@ TEST(SparseSetWithType, Raw) {
|
||||
ASSERT_EQ(*(set.raw() + 2u), 9);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RawEmptyType) {
|
||||
TEST(Storage, RawEmptyType) {
|
||||
entt::storage<std::uint64_t, empty_type> set;
|
||||
|
||||
set.construct(3);
|
||||
@@ -364,7 +323,7 @@ TEST(SparseSetWithType, RawEmptyType) {
|
||||
ASSERT_EQ(set.try_get(3), set.raw());
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, SortOrdered) {
|
||||
TEST(Storage, SortOrdered) {
|
||||
entt::storage<std::uint64_t, boxed_int> set;
|
||||
|
||||
set.construct(12, boxed_int{12});
|
||||
@@ -400,7 +359,7 @@ TEST(SparseSetWithType, SortOrdered) {
|
||||
ASSERT_EQ(begin, end);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, SortReverse) {
|
||||
TEST(Storage, SortReverse) {
|
||||
entt::storage<std::uint64_t, boxed_int> set;
|
||||
|
||||
set.construct(12, boxed_int{1});
|
||||
@@ -436,7 +395,7 @@ TEST(SparseSetWithType, SortReverse) {
|
||||
ASSERT_EQ(begin, end);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, SortUnordered) {
|
||||
TEST(Storage, SortUnordered) {
|
||||
entt::storage<std::uint64_t, boxed_int> set;
|
||||
|
||||
set.construct(12, boxed_int{6});
|
||||
@@ -472,7 +431,7 @@ TEST(SparseSetWithType, SortUnordered) {
|
||||
ASSERT_EQ(begin, end);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectDisjoint) {
|
||||
TEST(Storage, RespectDisjoint) {
|
||||
entt::storage<std::uint64_t, int> lhs;
|
||||
entt::storage<std::uint64_t, int> rhs;
|
||||
|
||||
@@ -499,7 +458,7 @@ TEST(SparseSetWithType, RespectDisjoint) {
|
||||
ASSERT_EQ(begin, end);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectOverlap) {
|
||||
TEST(Storage, RespectOverlap) {
|
||||
entt::storage<std::uint64_t, int> lhs;
|
||||
entt::storage<std::uint64_t, int> rhs;
|
||||
|
||||
@@ -528,7 +487,7 @@ TEST(SparseSetWithType, RespectOverlap) {
|
||||
ASSERT_EQ(begin, end);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectOrdered) {
|
||||
TEST(Storage, RespectOrdered) {
|
||||
entt::storage<std::uint64_t, int> lhs;
|
||||
entt::storage<std::uint64_t, int> rhs;
|
||||
|
||||
@@ -574,7 +533,7 @@ TEST(SparseSetWithType, RespectOrdered) {
|
||||
ASSERT_EQ(*(rhs.data() + 5u), 5u);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectReverse) {
|
||||
TEST(Storage, RespectReverse) {
|
||||
entt::storage<std::uint64_t, int> lhs;
|
||||
entt::storage<std::uint64_t, int> rhs;
|
||||
|
||||
@@ -620,7 +579,7 @@ TEST(SparseSetWithType, RespectReverse) {
|
||||
ASSERT_EQ(*(rhs.data() + 5u), 5u);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectUnordered) {
|
||||
TEST(Storage, RespectUnordered) {
|
||||
entt::storage<std::uint64_t, int> lhs;
|
||||
entt::storage<std::uint64_t, int> rhs;
|
||||
|
||||
@@ -666,7 +625,7 @@ TEST(SparseSetWithType, RespectUnordered) {
|
||||
ASSERT_EQ(*(rhs.data() + 5u), 5u);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectOverlapEmptyType) {
|
||||
TEST(Storage, RespectOverlapEmptyType) {
|
||||
entt::storage<std::uint64_t, empty_type> lhs;
|
||||
entt::storage<std::uint64_t, empty_type> rhs;
|
||||
|
||||
@@ -687,7 +646,7 @@ TEST(SparseSetWithType, RespectOverlapEmptyType) {
|
||||
ASSERT_EQ(std::as_const(lhs).sparse_set<std::uint64_t>::get(42), 1u);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, CanModifyDuringIteration) {
|
||||
TEST(Storage, CanModifyDuringIteration) {
|
||||
entt::storage<std::uint64_t, int> set;
|
||||
set.construct(0, 42);
|
||||
|
||||
@@ -703,7 +662,7 @@ TEST(SparseSetWithType, CanModifyDuringIteration) {
|
||||
(void)entity;
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, ReferencesGuaranteed) {
|
||||
TEST(Storage, ReferencesGuaranteed) {
|
||||
entt::storage<std::uint64_t, boxed_int> set;
|
||||
|
||||
set.construct(0, 0);
|
||||
@@ -731,13 +690,13 @@ TEST(SparseSetWithType, ReferencesGuaranteed) {
|
||||
ASSERT_EQ(set.get(1).value, 3);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, MoveOnlyComponent) {
|
||||
TEST(Storage, MoveOnlyComponent) {
|
||||
// the purpose is to ensure that move only components are always accepted
|
||||
entt::storage<std::uint64_t, std::unique_ptr<int>> set;
|
||||
(void)set;
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, ConstructorExceptionDoesNotAddToSet) {
|
||||
TEST(Storage, ConstructorExceptionDoesNotAddToSet) {
|
||||
entt::storage<std::uint64_t, throwing_component> set;
|
||||
|
||||
try {
|
||||
|
||||
@@ -368,7 +368,7 @@ TEST(Mod, Duktape) {
|
||||
ASSERT_EQ(registry.view<position>().size(), 3u);
|
||||
ASSERT_EQ(registry.view<renderable>().size(), 2u);
|
||||
|
||||
registry.view<duktape_runtime>().each([](auto, const duktape_runtime &runtime) {
|
||||
registry.view<duktape_runtime>().each([](const duktape_runtime &runtime) {
|
||||
ASSERT_EQ(runtime.components.size(), 2u);
|
||||
});
|
||||
|
||||
@@ -387,7 +387,7 @@ TEST(Mod, Duktape) {
|
||||
ASSERT_EQ(registry.view<position>().size(), 3u);
|
||||
ASSERT_EQ(registry.view<renderable>().size(), 2u);
|
||||
|
||||
registry.view<position, renderable, duktape_runtime>().each([](auto, const position &position, const auto &...) {
|
||||
registry.view<position, renderable, duktape_runtime>().each([](const position &position, auto &&...) {
|
||||
ASSERT_EQ(position.x, -100.);
|
||||
ASSERT_EQ(position.y, -100.);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user