is now possible to sort non-owning groups using a custom comparison function

This commit is contained in:
Michele Caini
2019-07-12 15:41:07 +02:00
parent 9126651cb9
commit 87d901b1ae
9 changed files with 655 additions and 329 deletions

4
TODO
View File

@@ -17,13 +17,11 @@
* add take functionality, eg registry.take(entity, other); where it takes the entity and all its components from registry and move them to other
* add opaque input iterators to views and groups that return tuples <entity, T &...> (proxy), multi-pass guaranteed
* add fast lane for raw iterations, extend mt doc to describe allowed add/remove with pre-allocations on fast lanes
* registry.each<T...>(first, last) by iterators, entities/components guaranteed
* multi component registry::remove and some others?
* can I use opaque connection also for the emitter?
* built-in support for dual (or N-) buffering
* meta: opaque references and pointers
TODO
* add sort by type to the non-owning group
* custom (decoupled) pools ==> double buffering, shared components, multi-model
* registry.each<T...>(first, last) by iterators, entities/components guaranteed
* non-owning groups shouldn't store entities, but positions => optimized each

View File

@@ -384,6 +384,64 @@ public:
traverse(std::move(func), type_list_cat_t<get_type_list, other_type_list>{});
}
/**
* @brief Sort a group according to the given comparison function.
*
* Sort the group so that iterating it with a couple of iterators returns
* entities and components in the expected order. See `begin` and `end` for
* more details.
*
* The comparison function object must return `true` if the first element
* is _less_ than the second one, `false` otherwise. The signature of the
* comparison function should be equivalent to one of the following:
*
* @code{.cpp}
* bool(std::tuple<Component &...>, std::tuple<Component &...>);
* bool(const Component &..., const Component &...);
* bool(const Entity, const Entity);
* @endcode
*
* Where `Component` are such that they are iterated by the group.<br/>
* Moreover, the comparison function object shall induce a
* _strict weak ordering_ on the values.
*
* The sort function oject must offer a member function template
* `operator()` that accepts three arguments:
*
* * An iterator to the first element of the range to sort.
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* The comparison function object received by the sort function object
* hasn't necessarily the type of the one passed along with the other
* parameters to this member function.
*
* @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.
*
* @tparam Component Optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
* @param compare A valid comparison function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename... Component, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
if constexpr(sizeof...(Component) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>);
handler->sort(handler->begin(), handler->end(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
// useless this-> used to suppress a warning with clang
return compare(this->get<Component...>(lhs), this->get<Component...>(rhs));
}, std::move(algo), std::forward<Args>(args)...);
}
}
/**
* @brief Sort the shared pool of entities according to the given component.
*
@@ -763,7 +821,8 @@ public:
* comparison function should be equivalent to one of the following:
*
* @code{.cpp}
* bool(const Component &..., const Component &...);
* bool(std::tuple<Component &...>, std::tuple<Component &...>);
* bool(const Component &, const Component &);
* bool(const Entity, const Entity);
* @endcode
*
@@ -804,9 +863,9 @@ public:
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>);
cpool->sort(cpool->end()-*length, cpool->end(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
cpool->sort(cpool->end()-*length, cpool->end(), [compare = std::move(compare), this](const entity_type lhs, const entity_type rhs) {
cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
// useless this-> used to suppress a warning with clang
return compare(this->get<Component>(lhs)..., this->get<Component>(rhs)...);
return compare(this->get<Component...>(lhs), this->get<Component...>(rhs));
}, std::move(algo), std::forward<Args>(args)...);
}

View File

@@ -92,7 +92,7 @@ class basic_registry {
storage<Entity, Component>::batch(first, last);
if(!construction.empty()) {
std::for_each(first, last, [&registry, this](const auto entt) {
std::for_each(first, last, [this, &registry](const auto entt) {
construction.publish(registry, entt, Component{});
});
}
@@ -100,7 +100,7 @@ class basic_registry {
component = storage<Entity, Component>::batch(first, last);
if(!construction.empty()) {
std::for_each(first, last, [&registry, component, this](const auto entt) mutable {
std::for_each(first, last, [this, &registry, component](const auto entt) mutable {
construction.publish(registry, entt, *(component++));
});
}
@@ -600,7 +600,7 @@ public:
available -= sz;
const auto tail = std::generate_n(first, sz, [&candidate, this]() mutable {
const auto tail = std::generate_n(first, sz, [this, &candidate]() mutable {
if constexpr(sizeof...(Component) > 0) {
candidate = entity_type{std::max(candidate, next)};
} else {
@@ -1219,7 +1219,7 @@ public:
void orphans(Func func) const {
static_assert(std::is_invocable_v<Func, entity_type>);
each([&func, this](const auto entity) {
each([this, &func](const auto entity) {
if(orphan(entity)) {
func(entity);
}

View File

@@ -8,8 +8,10 @@
#include <vector>
#include <memory>
#include <cstddef>
#include <numeric>
#include <type_traits>
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "entity.hpp"
#include "fwd.hpp"
@@ -415,7 +417,7 @@ public:
*/
template<typename It>
void batch(It first, It last) {
std::for_each(first, last, [next = direct.size(), this](const auto entt) mutable {
std::for_each(first, last, [this, next = direct.size()](const auto entt) mutable {
ENTT_ASSERT(!has(entt));
auto [page, offset] = index(entt);
assure(page);
@@ -470,6 +472,74 @@ public:
std::swap(direct[lhs], direct[rhs]);
}
/**
* @brief Sort elements according to the given comparison function.
*
* Sort the elements so that iterating the range with a couple of iterators
* returns them in the expected order. See `begin` and `end` for more
* details.
*
* The comparison function object must return `true` if the first element
* is _less_ than the second one, `false` otherwise. The signature of the
* comparison function should be equivalent to the following:
*
* @code{.cpp}
* bool(const Entity, const Entity);
* @endcode
*
* Moreover, the comparison function object shall induce a
* _strict weak ordering_ on the values.
*
* The sort function oject must offer a member function template
* `operator()` that accepts three arguments:
*
* * An iterator to the first element of the range to sort.
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* The comparison function object received by the sort function object
* hasn't necessarily the type of the one passed along with the other
* parameters to this member function.
*
* @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.
*
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
* @param first An iterator to the first element of the range to sort.
* @param last An iterator past the last element of the range to sort.
* @param compare A valid comparison function object.
* @param algo A valid sort function object.
* @param args Arguments to forward to the sort function object, if any.
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!(first > last));
std::vector<size_type> copy(last - first);
const auto offset = std::distance(last, end());
std::iota(copy.begin(), copy.end(), size_type{});
algo(copy.rbegin(), copy.rend(), [this, offset, compare = std::move(compare)](const auto lhs, const auto rhs) {
return compare(std::as_const(direct[lhs+offset]), std::as_const(direct[rhs+offset]));
}, std::forward<Args>(args)...);
for(size_type pos{}, length = copy.size(); pos < length; ++pos) {
auto curr = pos;
auto next = copy[curr];
while(curr != next) {
swap(copy[curr] + offset, copy[next] + offset);
copy[curr] = curr;
curr = next;
next = copy[curr];
}
}
}
/**
* @brief Sort entities according to their order in another sparse set.
*

View File

@@ -443,32 +443,17 @@ public:
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
ENTT_ASSERT(!(first > last));
std::vector<size_type> copy(last - first);
const auto offset = std::distance(last, end());
std::iota(copy.begin(), copy.end(), size_type{});
const auto from = underlying_type::begin() + std::distance(begin(), first);
const auto to = from + std::distance(first, last);
if constexpr(std::is_invocable_v<Compare, const object_type &, const object_type &>) {
static_assert(!std::is_empty_v<object_type>);
algo(copy.rbegin(), copy.rend(), [this, offset, compare = std::move(compare)](const auto lhs, const auto rhs) {
return compare(std::as_const(instances[lhs + offset]), std::as_const(instances[rhs + offset]));
}, std::forward<Args>(args)...);
underlying_type::sort(from, to, [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
return compare(std::as_const(instances[underlying_type::get(lhs)]), std::as_const(instances[underlying_type::get(rhs)]));
}, std::move(algo), std::forward<Args>(args)...);
} else {
algo(copy.rbegin(), copy.rend(), [offset, compare = std::move(compare), data = underlying_type::data()](const auto lhs, const auto rhs) {
return compare(std::as_const(data[lhs+offset]), std::as_const(data[rhs+offset]));
}, std::forward<Args>(args)...);
}
for(size_type pos{}, length = copy.size(); pos < length; ++pos) {
auto curr = pos;
auto next = copy[curr];
while(curr != next) {
swap(copy[curr] + offset, copy[next] + offset);
copy[curr] = curr;
curr = next;
next = copy[curr];
}
underlying_type::sort(from, to, std::move(compare), std::move(algo), std::forward<Args>(args)...);
}
}

View File

@@ -177,7 +177,7 @@ class basic_view {
auto begin = std::get<pool_type<Comp> *>(pools)->sparse_set<Entity>::begin();
if constexpr(std::disjunction_v<std::is_same<Comp, Type>...>) {
std::for_each(begin, end, [raw = std::get<pool_type<Comp> *>(pools)->begin(), &func, this](const auto entity) mutable {
std::for_each(begin, end, [this, raw = std::get<pool_type<Comp> *>(pools)->begin(), &func](const auto entity) mutable {
auto curr = raw++;
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
@@ -189,7 +189,7 @@ class basic_view {
}
});
} else {
std::for_each(begin, end, [&func, this](const auto entity) mutable {
std::for_each(begin, end, [this, &func](const auto entity) mutable {
if((std::get<pool_type<Other> *>(pools)->has(entity) && ...)) {
if constexpr(std::is_invocable_v<Func, decltype(get<Type>({}))...>) {
func(std::get<pool_type<Type> *>(pools)->get(entity)...);

View File

@@ -1,6 +1,7 @@
#include <utility>
#include <iterator>
#include <algorithm>
#include <type_traits>
#include <gtest/gtest.h>
#include <entt/entity/helper.hpp>
#include <entt/entity/registry.hpp>
@@ -132,15 +133,8 @@ TEST(NonOwningGroup, Empty) {
registry.assign<char>(e1);
registry.assign<float>(e1);
for(auto entity: registry.group(entt::get<char, int, float>)) {
(void)entity;
FAIL();
}
for(auto entity: registry.group(entt::get<double, char, int, float>)) {
(void)entity;
FAIL();
}
ASSERT_TRUE(registry.group(entt::get<char, int, float>).empty());
ASSERT_TRUE(registry.group(entt::get<double, char, int, float>).empty());
}
TEST(NonOwningGroup, Each) {
@@ -177,6 +171,59 @@ TEST(NonOwningGroup, Sort) {
const auto e1 = registry.create();
const auto e2 = registry.create();
registry.assign<unsigned int>(e0, 0u);
registry.assign<unsigned int>(e1, 1u);
registry.assign<unsigned int>(e2, 2u);
registry.assign<int>(e0, 0);
registry.assign<int>(e1, 1);
registry.assign<int>(e2, 2);
ASSERT_EQ(*(group.raw<unsigned int>() + 0u), 0u);
ASSERT_EQ(*(group.raw<unsigned int>() + 1u), 1u);
ASSERT_EQ(*(group.raw<unsigned int>() + 2u), 2u);
ASSERT_EQ(*(group.raw<const int>() + 0u), 0);
ASSERT_EQ(*(group.raw<const int>() + 1u), 1);
ASSERT_EQ(*(group.raw<const int>() + 2u), 2);
ASSERT_EQ(*(group.data() + 0u), e0);
ASSERT_EQ(*(group.data() + 1u), e1);
ASSERT_EQ(*(group.data() + 2u), e2);
group.sort([](const entt::entity lhs, const entt::entity rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(group.raw<unsigned int>() + 0u), 0u);
ASSERT_EQ(*(group.raw<unsigned int>() + 1u), 1u);
ASSERT_EQ(*(group.raw<unsigned int>() + 2u), 2u);
ASSERT_EQ(*(group.raw<const int>() + 0u), 0);
ASSERT_EQ(*(group.raw<const int>() + 1u), 1);
ASSERT_EQ(*(group.raw<const int>() + 2u), 2);
ASSERT_EQ(*(group.data() + 0u), e2);
ASSERT_EQ(*(group.data() + 1u), e1);
ASSERT_EQ(*(group.data() + 2u), e0);
group.sort<const int>([](const int lhs, const int rhs) {
return lhs > rhs;
});
ASSERT_EQ(*(group.data() + 0u), e0);
ASSERT_EQ(*(group.data() + 1u), e1);
ASSERT_EQ(*(group.data() + 2u), e2);
}
TEST(NonOwningGroup, SortAsAPool) {
entt::registry registry;
auto group = registry.group(entt::get<const int, unsigned int>);
const auto e0 = registry.create();
const auto e1 = registry.create();
const auto e2 = registry.create();
auto uval = 0u;
auto ival = 0;
@@ -533,15 +580,8 @@ TEST(OwningGroup, Empty) {
registry.assign<char>(e1);
registry.assign<float>(e1);
for(auto entity: registry.group<char, int>(entt::get<float>)) {
(void)entity;
FAIL();
}
for(auto entity: registry.group<double, float>(entt::get<char, int>)) {
(void)entity;
FAIL();
}
ASSERT_TRUE((registry.group<char, int>(entt::get<float>).empty()));
ASSERT_TRUE((registry.group<double, float>(entt::get<char, int>).empty()));
}
TEST(OwningGroup, Each) {

View File

@@ -221,6 +221,180 @@ TEST(SparseSet, Data) {
ASSERT_EQ(*(set.data() + 2u), entt::entity{42});
}
TEST(SparseSet, SortOrdered) {
entt::sparse_set<entt::entity> set;
set.construct(entt::entity{42});
set.construct(entt::entity{12});
set.construct(entt::entity{9});
set.construct(entt::entity{7});
set.construct(entt::entity{3});
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
set.sort(set.begin(), set.end(), [](const auto lhs, const auto rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
auto begin = set.begin();
auto end = set.end();
ASSERT_EQ(*(begin++), entt::entity{3});
ASSERT_EQ(*(begin++), entt::entity{7});
ASSERT_EQ(*(begin++), entt::entity{9});
ASSERT_EQ(*(begin++), entt::entity{12});
ASSERT_EQ(*(begin++), entt::entity{42});
ASSERT_EQ(begin, end);
}
TEST(SparseSet, SortReverse) {
entt::sparse_set<entt::entity> set;
set.construct(entt::entity{3});
set.construct(entt::entity{7});
set.construct(entt::entity{9});
set.construct(entt::entity{12});
set.construct(entt::entity{42});
ASSERT_EQ(*(set.data() + 0u), entt::entity{3});
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
set.sort(set.begin(), set.end(), [](const auto lhs, const auto rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
auto begin = set.begin();
auto end = set.end();
ASSERT_EQ(*(begin++), entt::entity{3});
ASSERT_EQ(*(begin++), entt::entity{7});
ASSERT_EQ(*(begin++), entt::entity{9});
ASSERT_EQ(*(begin++), entt::entity{12});
ASSERT_EQ(*(begin++), entt::entity{42});
ASSERT_EQ(begin, end);
}
TEST(SparseSet, SortUnordered) {
entt::sparse_set<entt::entity> set;
set.construct(entt::entity{9});
set.construct(entt::entity{7});
set.construct(entt::entity{3});
set.construct(entt::entity{12});
set.construct(entt::entity{42});
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
set.sort(set.begin(), set.end(), [](const auto lhs, const auto rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
auto begin = set.begin();
auto end = set.end();
ASSERT_EQ(*(begin++), entt::entity{3});
ASSERT_EQ(*(begin++), entt::entity{7});
ASSERT_EQ(*(begin++), entt::entity{9});
ASSERT_EQ(*(begin++), entt::entity{12});
ASSERT_EQ(*(begin++), entt::entity{42});
ASSERT_EQ(begin, end);
}
TEST(SparseSet, SortRange) {
entt::sparse_set<entt::entity> set;
set.construct(entt::entity{9});
set.construct(entt::entity{7});
set.construct(entt::entity{3});
set.construct(entt::entity{12});
set.construct(entt::entity{42});
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
set.sort(set.end(), set.end(), [](const auto lhs, const auto rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
set.sort(set.begin(), set.begin(), [](const auto lhs, const auto rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
set.sort(set.begin()+2, set.begin()+3, [](const auto lhs, const auto rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
set.sort(++set.begin(), --set.end(), [](const auto lhs, const auto rhs) {
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
});
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
ASSERT_EQ(*(set.data() + 2u), entt::entity{7});
ASSERT_EQ(*(set.data() + 3u), entt::entity{3});
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
auto begin = set.begin();
auto end = set.end();
ASSERT_EQ(*(begin++), entt::entity{42});
ASSERT_EQ(*(begin++), entt::entity{3});
ASSERT_EQ(*(begin++), entt::entity{7});
ASSERT_EQ(*(begin++), entt::entity{12});
ASSERT_EQ(*(begin++), entt::entity{9});
ASSERT_EQ(begin, end);
}
TEST(SparseSet, RespectDisjoint) {
entt::sparse_set<entt::entity> lhs;
entt::sparse_set<entt::entity> rhs;

View File

@@ -21,135 +21,135 @@ struct throwing_component {
};
TEST(Storage, Functionalities) {
entt::storage<entt::entity, int> set;
entt::storage<entt::entity, int> pool;
set.reserve(42);
pool.reserve(42);
ASSERT_EQ(set.capacity(), 42);
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(std::as_const(set).begin(), std::as_const(set).end());
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(entt::entity{0}));
ASSERT_FALSE(set.has(entt::entity{41}));
ASSERT_EQ(pool.capacity(), 42);
ASSERT_TRUE(pool.empty());
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(std::as_const(pool).begin(), std::as_const(pool).end());
ASSERT_EQ(pool.begin(), pool.end());
ASSERT_FALSE(pool.has(entt::entity{0}));
ASSERT_FALSE(pool.has(entt::entity{41}));
set.construct(entt::entity{41}, 3);
pool.construct(entt::entity{41}, 3);
ASSERT_FALSE(set.empty());
ASSERT_EQ(set.size(), 1u);
ASSERT_NE(std::as_const(set).begin(), std::as_const(set).end());
ASSERT_NE(set.begin(), set.end());
ASSERT_FALSE(set.has(entt::entity{0}));
ASSERT_TRUE(set.has(entt::entity{41}));
ASSERT_EQ(set.get(entt::entity{41}), 3);
ASSERT_EQ(*set.try_get(entt::entity{41}), 3);
ASSERT_EQ(set.try_get(entt::entity{99}), nullptr);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.size(), 1u);
ASSERT_NE(std::as_const(pool).begin(), std::as_const(pool).end());
ASSERT_NE(pool.begin(), pool.end());
ASSERT_FALSE(pool.has(entt::entity{0}));
ASSERT_TRUE(pool.has(entt::entity{41}));
ASSERT_EQ(pool.get(entt::entity{41}), 3);
ASSERT_EQ(*pool.try_get(entt::entity{41}), 3);
ASSERT_EQ(pool.try_get(entt::entity{99}), nullptr);
set.destroy(entt::entity{41});
pool.destroy(entt::entity{41});
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(std::as_const(set).begin(), std::as_const(set).end());
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(entt::entity{0}));
ASSERT_FALSE(set.has(entt::entity{41}));
ASSERT_TRUE(pool.empty());
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(std::as_const(pool).begin(), std::as_const(pool).end());
ASSERT_EQ(pool.begin(), pool.end());
ASSERT_FALSE(pool.has(entt::entity{0}));
ASSERT_FALSE(pool.has(entt::entity{41}));
set.construct(entt::entity{41}, 12);
pool.construct(entt::entity{41}, 12);
ASSERT_EQ(set.get(entt::entity{41}), 12);
ASSERT_EQ(*set.try_get(entt::entity{41}), 12);
ASSERT_EQ(set.try_get(entt::entity{99}), nullptr);
ASSERT_EQ(pool.get(entt::entity{41}), 12);
ASSERT_EQ(*pool.try_get(entt::entity{41}), 12);
ASSERT_EQ(pool.try_get(entt::entity{99}), nullptr);
set.reset();
pool.reset();
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(std::as_const(set).begin(), std::as_const(set).end());
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(entt::entity{0}));
ASSERT_FALSE(set.has(entt::entity{41}));
ASSERT_TRUE(pool.empty());
ASSERT_EQ(pool.size(), 0u);
ASSERT_EQ(std::as_const(pool).begin(), std::as_const(pool).end());
ASSERT_EQ(pool.begin(), pool.end());
ASSERT_FALSE(pool.has(entt::entity{0}));
ASSERT_FALSE(pool.has(entt::entity{41}));
ASSERT_EQ(set.capacity(), 42);
ASSERT_EQ(pool.capacity(), 42);
set.shrink_to_fit();
pool.shrink_to_fit();
ASSERT_EQ(set.capacity(), 0);
ASSERT_EQ(pool.capacity(), 0);
(void)entt::storage<entt::entity, int>{std::move(set)};
(void)entt::storage<entt::entity, int>{std::move(pool)};
entt::storage<entt::entity, int> other;
other = std::move(set);
other = std::move(pool);
}
TEST(Storage, EmptyType) {
entt::storage<entt::entity, empty_type> set;
entt::storage<entt::entity, empty_type> pool;
set.construct(entt::entity{42});
set.construct(entt::entity{99});
pool.construct(entt::entity{42});
pool.construct(entt::entity{99});
ASSERT_TRUE(set.has(entt::entity{42}));
ASSERT_TRUE(set.has(entt::entity{99}));
ASSERT_TRUE(pool.has(entt::entity{42}));
ASSERT_TRUE(pool.has(entt::entity{99}));
auto &&component = set.get(entt::entity{42});
auto &&component = pool.get(entt::entity{42});
ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
}
TEST(Storage, BatchAdd) {
entt::storage<entt::entity, int> set;
entt::storage<entt::entity, int> pool;
entt::storage<entt::entity, int>::entity_type entities[2];
entities[0] = entt::entity{3};
entities[1] = entt::entity{42};
set.reserve(4);
set.construct(entt::entity{12}, 21);
auto *component = set.batch(std::begin(entities), std::end(entities));
set.construct(entt::entity{24}, 42);
pool.reserve(4);
pool.construct(entt::entity{12}, 21);
auto *component = pool.batch(std::begin(entities), std::end(entities));
pool.construct(entt::entity{24}, 42);
ASSERT_TRUE(set.has(entities[0]));
ASSERT_TRUE(set.has(entities[1]));
ASSERT_FALSE(set.has(entt::entity{0}));
ASSERT_FALSE(set.has(entt::entity{9}));
ASSERT_TRUE(set.has(entt::entity{12}));
ASSERT_TRUE(set.has(entt::entity{24}));
ASSERT_TRUE(pool.has(entities[0]));
ASSERT_TRUE(pool.has(entities[1]));
ASSERT_FALSE(pool.has(entt::entity{0}));
ASSERT_FALSE(pool.has(entt::entity{9}));
ASSERT_TRUE(pool.has(entt::entity{12}));
ASSERT_TRUE(pool.has(entt::entity{24}));
ASSERT_FALSE(set.empty());
ASSERT_EQ(set.size(), 4u);
ASSERT_EQ(set.get(entt::entity{12}), 21);
ASSERT_EQ(set.get(entities[0]), 0);
ASSERT_EQ(set.get(entities[1]), 0);
ASSERT_EQ(set.get(entt::entity{24}), 42);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.size(), 4u);
ASSERT_EQ(pool.get(entt::entity{12}), 21);
ASSERT_EQ(pool.get(entities[0]), 0);
ASSERT_EQ(pool.get(entities[1]), 0);
ASSERT_EQ(pool.get(entt::entity{24}), 42);
component[0] = 1;
component[1] = 2;
ASSERT_EQ(set.get(entities[0]), 1);
ASSERT_EQ(set.get(entities[1]), 2);
ASSERT_EQ(pool.get(entities[0]), 1);
ASSERT_EQ(pool.get(entities[1]), 2);
}
TEST(Storage, BatchAddEmptyType) {
entt::storage<entt::entity, empty_type> set;
entt::storage<entt::entity, empty_type> pool;
entt::storage<entt::entity, empty_type>::entity_type entities[2];
entities[0] = entt::entity{3};
entities[1] = entt::entity{42};
set.reserve(4);
set.construct(entt::entity{12});
set.batch(std::begin(entities), std::end(entities));
set.construct(entt::entity{24});
pool.reserve(4);
pool.construct(entt::entity{12});
pool.batch(std::begin(entities), std::end(entities));
pool.construct(entt::entity{24});
ASSERT_TRUE(set.has(entities[0]));
ASSERT_TRUE(set.has(entities[1]));
ASSERT_FALSE(set.has(entt::entity{0}));
ASSERT_FALSE(set.has(entt::entity{9}));
ASSERT_TRUE(set.has(entt::entity{12}));
ASSERT_TRUE(set.has(entt::entity{24}));
ASSERT_TRUE(pool.has(entities[0]));
ASSERT_TRUE(pool.has(entities[1]));
ASSERT_FALSE(pool.has(entt::entity{0}));
ASSERT_FALSE(pool.has(entt::entity{9}));
ASSERT_TRUE(pool.has(entt::entity{12}));
ASSERT_TRUE(pool.has(entt::entity{24}));
ASSERT_FALSE(set.empty());
ASSERT_EQ(set.size(), 4u);
ASSERT_FALSE(pool.empty());
ASSERT_EQ(pool.size(), 4u);
auto &&component = set.get(entities[0]);
auto &&component = pool.get(entities[0]);
ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
}
@@ -162,184 +162,184 @@ TEST(Storage, AggregatesMustWork) {
TEST(Storage, TypesFromStandardTemplateLibraryMustWork) {
// see #37 - this test shouldn't crash, that's all
entt::storage<entt::entity, std::unordered_set<int>> set;
set.construct(entt::entity{0}).insert(42);
set.destroy(entt::entity{0});
entt::storage<entt::entity, std::unordered_set<int>> pool;
pool.construct(entt::entity{0}).insert(42);
pool.destroy(entt::entity{0});
}
TEST(Storage, Iterator) {
using iterator_type = typename entt::storage<entt::entity, boxed_int>::iterator_type;
entt::storage<entt::entity, boxed_int> set;
set.construct(entt::entity{3}, 42);
entt::storage<entt::entity, boxed_int> pool;
pool.construct(entt::entity{3}, 42);
iterator_type end{set.begin()};
iterator_type end{pool.begin()};
iterator_type begin{};
begin = set.end();
begin = pool.end();
std::swap(begin, end);
ASSERT_EQ(begin, set.begin());
ASSERT_EQ(end, set.end());
ASSERT_EQ(begin, pool.begin());
ASSERT_EQ(end, pool.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, set.begin());
ASSERT_EQ(begin--, set.end());
ASSERT_EQ(begin++, pool.begin());
ASSERT_EQ(begin--, pool.end());
ASSERT_EQ(begin+1, set.end());
ASSERT_EQ(end-1, set.begin());
ASSERT_EQ(begin+1, pool.end());
ASSERT_EQ(end-1, pool.begin());
ASSERT_EQ(++begin, set.end());
ASSERT_EQ(--begin, set.begin());
ASSERT_EQ(++begin, pool.end());
ASSERT_EQ(--begin, pool.begin());
ASSERT_EQ(begin += 1, set.end());
ASSERT_EQ(begin -= 1, set.begin());
ASSERT_EQ(begin += 1, pool.end());
ASSERT_EQ(begin -= 1, pool.begin());
ASSERT_EQ(begin + (end - begin), set.end());
ASSERT_EQ(begin - (begin - end), set.end());
ASSERT_EQ(begin + (end - begin), pool.end());
ASSERT_EQ(begin - (begin - end), pool.end());
ASSERT_EQ(end - (end - begin), set.begin());
ASSERT_EQ(end + (begin - end), set.begin());
ASSERT_EQ(end - (end - begin), pool.begin());
ASSERT_EQ(end + (begin - end), pool.begin());
ASSERT_EQ(begin[0].value, set.begin()->value);
ASSERT_EQ(begin[0].value, pool.begin()->value);
ASSERT_LT(begin, end);
ASSERT_LE(begin, set.begin());
ASSERT_LE(begin, pool.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, set.end());
ASSERT_GE(end, pool.end());
}
TEST(Storage, ConstIterator) {
using iterator_type = typename entt::storage<entt::entity, boxed_int>::const_iterator_type;
entt::storage<entt::entity, boxed_int> set;
set.construct(entt::entity{3}, 42);
entt::storage<entt::entity, boxed_int> pool;
pool.construct(entt::entity{3}, 42);
iterator_type cend{set.cbegin()};
iterator_type cend{pool.cbegin()};
iterator_type cbegin{};
cbegin = set.cend();
cbegin = pool.cend();
std::swap(cbegin, cend);
ASSERT_EQ(cbegin, set.cbegin());
ASSERT_EQ(cend, set.cend());
ASSERT_EQ(cbegin, pool.cbegin());
ASSERT_EQ(cend, pool.cend());
ASSERT_NE(cbegin, cend);
ASSERT_EQ(cbegin++, set.cbegin());
ASSERT_EQ(cbegin--, set.cend());
ASSERT_EQ(cbegin++, pool.cbegin());
ASSERT_EQ(cbegin--, pool.cend());
ASSERT_EQ(cbegin+1, set.cend());
ASSERT_EQ(cend-1, set.cbegin());
ASSERT_EQ(cbegin+1, pool.cend());
ASSERT_EQ(cend-1, pool.cbegin());
ASSERT_EQ(++cbegin, set.cend());
ASSERT_EQ(--cbegin, set.cbegin());
ASSERT_EQ(++cbegin, pool.cend());
ASSERT_EQ(--cbegin, pool.cbegin());
ASSERT_EQ(cbegin += 1, set.cend());
ASSERT_EQ(cbegin -= 1, set.cbegin());
ASSERT_EQ(cbegin += 1, pool.cend());
ASSERT_EQ(cbegin -= 1, pool.cbegin());
ASSERT_EQ(cbegin + (cend - cbegin), set.cend());
ASSERT_EQ(cbegin - (cbegin - cend), set.cend());
ASSERT_EQ(cbegin + (cend - cbegin), pool.cend());
ASSERT_EQ(cbegin - (cbegin - cend), pool.cend());
ASSERT_EQ(cend - (cend - cbegin), set.cbegin());
ASSERT_EQ(cend + (cbegin - cend), set.cbegin());
ASSERT_EQ(cend - (cend - cbegin), pool.cbegin());
ASSERT_EQ(cend + (cbegin - cend), pool.cbegin());
ASSERT_EQ(cbegin[0].value, set.cbegin()->value);
ASSERT_EQ(cbegin[0].value, pool.cbegin()->value);
ASSERT_LT(cbegin, cend);
ASSERT_LE(cbegin, set.cbegin());
ASSERT_LE(cbegin, pool.cbegin());
ASSERT_GT(cend, cbegin);
ASSERT_GE(cend, set.cend());
ASSERT_GE(cend, pool.cend());
}
TEST(Storage, IteratorEmptyType) {
using iterator_type = typename entt::storage<entt::entity, empty_type>::iterator_type;
entt::storage<entt::entity, empty_type> set;
set.construct(entt::entity{3});
entt::storage<entt::entity, empty_type> pool;
pool.construct(entt::entity{3});
iterator_type end{set.begin()};
iterator_type end{pool.begin()};
iterator_type begin{};
begin = set.end();
begin = pool.end();
std::swap(begin, end);
ASSERT_EQ(begin, set.begin());
ASSERT_EQ(end, set.end());
ASSERT_EQ(begin, pool.begin());
ASSERT_EQ(end, pool.end());
ASSERT_NE(begin, end);
ASSERT_EQ(begin++, set.begin());
ASSERT_EQ(begin--, set.end());
ASSERT_EQ(begin++, pool.begin());
ASSERT_EQ(begin--, pool.end());
ASSERT_EQ(begin+1, set.end());
ASSERT_EQ(end-1, set.begin());
ASSERT_EQ(begin+1, pool.end());
ASSERT_EQ(end-1, pool.begin());
ASSERT_EQ(++begin, set.end());
ASSERT_EQ(--begin, set.begin());
ASSERT_EQ(++begin, pool.end());
ASSERT_EQ(--begin, pool.begin());
ASSERT_EQ(begin += 1, set.end());
ASSERT_EQ(begin -= 1, set.begin());
ASSERT_EQ(begin += 1, pool.end());
ASSERT_EQ(begin -= 1, pool.begin());
ASSERT_EQ(begin + (end - begin), set.end());
ASSERT_EQ(begin - (begin - end), set.end());
ASSERT_EQ(begin + (end - begin), pool.end());
ASSERT_EQ(begin - (begin - end), pool.end());
ASSERT_EQ(end - (end - begin), set.begin());
ASSERT_EQ(end + (begin - end), set.begin());
ASSERT_EQ(end - (end - begin), pool.begin());
ASSERT_EQ(end + (begin - end), pool.begin());
ASSERT_EQ(set.begin().operator->(), nullptr);
ASSERT_EQ(pool.begin().operator->(), nullptr);
ASSERT_LT(begin, end);
ASSERT_LE(begin, set.begin());
ASSERT_LE(begin, pool.begin());
ASSERT_GT(end, begin);
ASSERT_GE(end, set.end());
ASSERT_GE(end, pool.end());
set.construct(entt::entity{33});
pool.construct(entt::entity{33});
auto &&component = *begin;
ASSERT_TRUE((std::is_same_v<decltype(component), empty_type &&>));
}
TEST(Storage, Raw) {
entt::storage<entt::entity, int> set;
entt::storage<entt::entity, int> pool;
set.construct(entt::entity{3}, 3);
set.construct(entt::entity{12}, 6);
set.construct(entt::entity{42}, 9);
pool.construct(entt::entity{3}, 3);
pool.construct(entt::entity{12}, 6);
pool.construct(entt::entity{42}, 9);
ASSERT_EQ(set.get(entt::entity{3}), 3);
ASSERT_EQ(std::as_const(set).get(entt::entity{12}), 6);
ASSERT_EQ(set.get(entt::entity{42}), 9);
ASSERT_EQ(pool.get(entt::entity{3}), 3);
ASSERT_EQ(std::as_const(pool).get(entt::entity{12}), 6);
ASSERT_EQ(pool.get(entt::entity{42}), 9);
ASSERT_EQ(*(set.raw() + 0u), 3);
ASSERT_EQ(*(std::as_const(set).raw() + 1u), 6);
ASSERT_EQ(*(set.raw() + 2u), 9);
ASSERT_EQ(*(pool.raw() + 0u), 3);
ASSERT_EQ(*(std::as_const(pool).raw() + 1u), 6);
ASSERT_EQ(*(pool.raw() + 2u), 9);
}
TEST(Storage, SortOrdered) {
entt::storage<entt::entity, boxed_int> set;
entt::storage<entt::entity, boxed_int> pool;
set.construct(entt::entity{12}, boxed_int{12});
set.construct(entt::entity{42}, boxed_int{9});
set.construct(entt::entity{7}, boxed_int{6});
set.construct(entt::entity{3}, boxed_int{3});
set.construct(entt::entity{9}, boxed_int{1});
pool.construct(entt::entity{12}, boxed_int{12});
pool.construct(entt::entity{42}, boxed_int{9});
pool.construct(entt::entity{7}, boxed_int{6});
pool.construct(entt::entity{3}, boxed_int{3});
pool.construct(entt::entity{9}, boxed_int{1});
ASSERT_EQ(set.get(entt::entity{12}).value, 12);
ASSERT_EQ(set.get(entt::entity{42}).value, 9);
ASSERT_EQ(set.get(entt::entity{7}).value, 6);
ASSERT_EQ(set.get(entt::entity{3}).value, 3);
ASSERT_EQ(set.get(entt::entity{9}).value, 1);
ASSERT_EQ(pool.get(entt::entity{12}).value, 12);
ASSERT_EQ(pool.get(entt::entity{42}).value, 9);
ASSERT_EQ(pool.get(entt::entity{7}).value, 6);
ASSERT_EQ(pool.get(entt::entity{3}).value, 3);
ASSERT_EQ(pool.get(entt::entity{9}).value, 1);
set.sort(set.begin(), set.end(), [](auto lhs, auto rhs) {
pool.sort(pool.begin(), pool.end(), [](auto lhs, auto rhs) {
return lhs.value < rhs.value;
});
ASSERT_EQ((set.raw() + 0u)->value, 12);
ASSERT_EQ((set.raw() + 1u)->value, 9);
ASSERT_EQ((set.raw() + 2u)->value, 6);
ASSERT_EQ((set.raw() + 3u)->value, 3);
ASSERT_EQ((set.raw() + 4u)->value, 1);
ASSERT_EQ((pool.raw() + 0u)->value, 12);
ASSERT_EQ((pool.raw() + 1u)->value, 9);
ASSERT_EQ((pool.raw() + 2u)->value, 6);
ASSERT_EQ((pool.raw() + 3u)->value, 3);
ASSERT_EQ((pool.raw() + 4u)->value, 1);
auto begin = set.begin();
auto end = set.end();
auto begin = pool.begin();
auto end = pool.end();
ASSERT_EQ((begin++)->value, 1);
ASSERT_EQ((begin++)->value, 3);
@@ -350,32 +350,32 @@ TEST(Storage, SortOrdered) {
}
TEST(Storage, SortReverse) {
entt::storage<entt::entity, boxed_int> set;
entt::storage<entt::entity, boxed_int> pool;
set.construct(entt::entity{12}, boxed_int{1});
set.construct(entt::entity{42}, boxed_int{3});
set.construct(entt::entity{7}, boxed_int{6});
set.construct(entt::entity{3}, boxed_int{9});
set.construct(entt::entity{9}, boxed_int{12});
pool.construct(entt::entity{12}, boxed_int{1});
pool.construct(entt::entity{42}, boxed_int{3});
pool.construct(entt::entity{7}, boxed_int{6});
pool.construct(entt::entity{3}, boxed_int{9});
pool.construct(entt::entity{9}, boxed_int{12});
ASSERT_EQ(set.get(entt::entity{12}).value, 1);
ASSERT_EQ(set.get(entt::entity{42}).value, 3);
ASSERT_EQ(set.get(entt::entity{7}).value, 6);
ASSERT_EQ(set.get(entt::entity{3}).value, 9);
ASSERT_EQ(set.get(entt::entity{9}).value, 12);
ASSERT_EQ(pool.get(entt::entity{12}).value, 1);
ASSERT_EQ(pool.get(entt::entity{42}).value, 3);
ASSERT_EQ(pool.get(entt::entity{7}).value, 6);
ASSERT_EQ(pool.get(entt::entity{3}).value, 9);
ASSERT_EQ(pool.get(entt::entity{9}).value, 12);
set.sort(set.begin(), set.end(), [&set](entt::entity lhs, entt::entity rhs) {
return set.get(lhs).value < set.get(rhs).value;
pool.sort(pool.begin(), pool.end(), [&pool](entt::entity lhs, entt::entity rhs) {
return pool.get(lhs).value < pool.get(rhs).value;
});
ASSERT_EQ((set.raw() + 0u)->value, 12);
ASSERT_EQ((set.raw() + 1u)->value, 9);
ASSERT_EQ((set.raw() + 2u)->value, 6);
ASSERT_EQ((set.raw() + 3u)->value, 3);
ASSERT_EQ((set.raw() + 4u)->value, 1);
ASSERT_EQ((pool.raw() + 0u)->value, 12);
ASSERT_EQ((pool.raw() + 1u)->value, 9);
ASSERT_EQ((pool.raw() + 2u)->value, 6);
ASSERT_EQ((pool.raw() + 3u)->value, 3);
ASSERT_EQ((pool.raw() + 4u)->value, 1);
auto begin = set.begin();
auto end = set.end();
auto begin = pool.begin();
auto end = pool.end();
ASSERT_EQ((begin++)->value, 1);
ASSERT_EQ((begin++)->value, 3);
@@ -386,32 +386,32 @@ TEST(Storage, SortReverse) {
}
TEST(Storage, SortUnordered) {
entt::storage<entt::entity, boxed_int> set;
entt::storage<entt::entity, boxed_int> pool;
set.construct(entt::entity{12}, boxed_int{6});
set.construct(entt::entity{42}, boxed_int{3});
set.construct(entt::entity{7}, boxed_int{1});
set.construct(entt::entity{3}, boxed_int{9});
set.construct(entt::entity{9}, boxed_int{12});
pool.construct(entt::entity{12}, boxed_int{6});
pool.construct(entt::entity{42}, boxed_int{3});
pool.construct(entt::entity{7}, boxed_int{1});
pool.construct(entt::entity{3}, boxed_int{9});
pool.construct(entt::entity{9}, boxed_int{12});
ASSERT_EQ(set.get(entt::entity{12}).value, 6);
ASSERT_EQ(set.get(entt::entity{42}).value, 3);
ASSERT_EQ(set.get(entt::entity{7}).value, 1);
ASSERT_EQ(set.get(entt::entity{3}).value, 9);
ASSERT_EQ(set.get(entt::entity{9}).value, 12);
ASSERT_EQ(pool.get(entt::entity{12}).value, 6);
ASSERT_EQ(pool.get(entt::entity{42}).value, 3);
ASSERT_EQ(pool.get(entt::entity{7}).value, 1);
ASSERT_EQ(pool.get(entt::entity{3}).value, 9);
ASSERT_EQ(pool.get(entt::entity{9}).value, 12);
set.sort(set.begin(), set.end(), [](auto lhs, auto rhs) {
pool.sort(pool.begin(), pool.end(), [](auto lhs, auto rhs) {
return lhs.value < rhs.value;
});
ASSERT_EQ((set.raw() + 0u)->value, 12);
ASSERT_EQ((set.raw() + 1u)->value, 9);
ASSERT_EQ((set.raw() + 2u)->value, 6);
ASSERT_EQ((set.raw() + 3u)->value, 3);
ASSERT_EQ((set.raw() + 4u)->value, 1);
ASSERT_EQ((pool.raw() + 0u)->value, 12);
ASSERT_EQ((pool.raw() + 1u)->value, 9);
ASSERT_EQ((pool.raw() + 2u)->value, 6);
ASSERT_EQ((pool.raw() + 3u)->value, 3);
ASSERT_EQ((pool.raw() + 4u)->value, 1);
auto begin = set.begin();
auto end = set.end();
auto begin = pool.begin();
auto end = pool.end();
ASSERT_EQ((begin++)->value, 1);
ASSERT_EQ((begin++)->value, 3);
@@ -422,62 +422,62 @@ TEST(Storage, SortUnordered) {
}
TEST(Storage, SortRange) {
entt::storage<entt::entity, boxed_int> set;
entt::storage<entt::entity, boxed_int> pool;
set.construct(entt::entity{12}, boxed_int{6});
set.construct(entt::entity{42}, boxed_int{3});
set.construct(entt::entity{7}, boxed_int{1});
set.construct(entt::entity{3}, boxed_int{9});
set.construct(entt::entity{9}, boxed_int{12});
pool.construct(entt::entity{12}, boxed_int{6});
pool.construct(entt::entity{42}, boxed_int{3});
pool.construct(entt::entity{7}, boxed_int{1});
pool.construct(entt::entity{3}, boxed_int{9});
pool.construct(entt::entity{9}, boxed_int{12});
ASSERT_EQ(set.get(entt::entity{12}).value, 6);
ASSERT_EQ(set.get(entt::entity{42}).value, 3);
ASSERT_EQ(set.get(entt::entity{7}).value, 1);
ASSERT_EQ(set.get(entt::entity{3}).value, 9);
ASSERT_EQ(set.get(entt::entity{9}).value, 12);
ASSERT_EQ(pool.get(entt::entity{12}).value, 6);
ASSERT_EQ(pool.get(entt::entity{42}).value, 3);
ASSERT_EQ(pool.get(entt::entity{7}).value, 1);
ASSERT_EQ(pool.get(entt::entity{3}).value, 9);
ASSERT_EQ(pool.get(entt::entity{9}).value, 12);
set.sort(set.end(), set.end(), [](auto lhs, auto rhs) {
pool.sort(pool.end(), pool.end(), [](auto lhs, auto rhs) {
return lhs.value < rhs.value;
});
ASSERT_EQ(set.get(entt::entity{12}).value, 6);
ASSERT_EQ(set.get(entt::entity{42}).value, 3);
ASSERT_EQ(set.get(entt::entity{7}).value, 1);
ASSERT_EQ(set.get(entt::entity{3}).value, 9);
ASSERT_EQ(set.get(entt::entity{9}).value, 12);
ASSERT_EQ(pool.get(entt::entity{12}).value, 6);
ASSERT_EQ(pool.get(entt::entity{42}).value, 3);
ASSERT_EQ(pool.get(entt::entity{7}).value, 1);
ASSERT_EQ(pool.get(entt::entity{3}).value, 9);
ASSERT_EQ(pool.get(entt::entity{9}).value, 12);
set.sort(set.begin(), set.begin(), [](auto lhs, auto rhs) {
pool.sort(pool.begin(), pool.begin(), [](auto lhs, auto rhs) {
return lhs.value < rhs.value;
});
ASSERT_EQ(set.get(entt::entity{12}).value, 6);
ASSERT_EQ(set.get(entt::entity{42}).value, 3);
ASSERT_EQ(set.get(entt::entity{7}).value, 1);
ASSERT_EQ(set.get(entt::entity{3}).value, 9);
ASSERT_EQ(set.get(entt::entity{9}).value, 12);
ASSERT_EQ(pool.get(entt::entity{12}).value, 6);
ASSERT_EQ(pool.get(entt::entity{42}).value, 3);
ASSERT_EQ(pool.get(entt::entity{7}).value, 1);
ASSERT_EQ(pool.get(entt::entity{3}).value, 9);
ASSERT_EQ(pool.get(entt::entity{9}).value, 12);
set.sort(set.begin()+2, set.begin()+3, [](auto lhs, auto rhs) {
pool.sort(pool.begin()+2, pool.begin()+3, [](auto lhs, auto rhs) {
return lhs.value < rhs.value;
});
ASSERT_EQ(set.get(entt::entity{12}).value, 6);
ASSERT_EQ(set.get(entt::entity{42}).value, 3);
ASSERT_EQ(set.get(entt::entity{7}).value, 1);
ASSERT_EQ(set.get(entt::entity{3}).value, 9);
ASSERT_EQ(set.get(entt::entity{9}).value, 12);
ASSERT_EQ(pool.get(entt::entity{12}).value, 6);
ASSERT_EQ(pool.get(entt::entity{42}).value, 3);
ASSERT_EQ(pool.get(entt::entity{7}).value, 1);
ASSERT_EQ(pool.get(entt::entity{3}).value, 9);
ASSERT_EQ(pool.get(entt::entity{9}).value, 12);
set.sort(++set.begin(), --set.end(), [](auto lhs, auto rhs) {
pool.sort(++pool.begin(), --pool.end(), [](auto lhs, auto rhs) {
return lhs.value < rhs.value;
});
ASSERT_EQ((set.raw() + 0u)->value, 6);
ASSERT_EQ((set.raw() + 1u)->value, 9);
ASSERT_EQ((set.raw() + 2u)->value, 3);
ASSERT_EQ((set.raw() + 3u)->value, 1);
ASSERT_EQ((set.raw() + 4u)->value, 12);
ASSERT_EQ((pool.raw() + 0u)->value, 6);
ASSERT_EQ((pool.raw() + 1u)->value, 9);
ASSERT_EQ((pool.raw() + 2u)->value, 3);
ASSERT_EQ((pool.raw() + 3u)->value, 1);
ASSERT_EQ((pool.raw() + 4u)->value, 12);
auto begin = set.begin();
auto end = set.end();
auto begin = pool.begin();
auto end = pool.end();
ASSERT_EQ((begin++)->value, 12);
ASSERT_EQ((begin++)->value, 1);
@@ -703,15 +703,15 @@ TEST(Storage, RespectOverlapEmptyType) {
}
TEST(Storage, CanModifyDuringIteration) {
entt::storage<entt::entity, int> set;
set.construct(entt::entity{0}, 42);
entt::storage<entt::entity, int> pool;
pool.construct(entt::entity{0}, 42);
ASSERT_EQ(set.capacity(), (entt::storage<entt::entity, int>::size_type{1}));
ASSERT_EQ(pool.capacity(), (entt::storage<entt::entity, int>::size_type{1}));
const auto it = set.cbegin();
set.reserve(entt::storage<entt::entity, int>::size_type{2});
const auto it = pool.cbegin();
pool.reserve(entt::storage<entt::entity, int>::size_type{2});
ASSERT_EQ(set.capacity(), (entt::storage<entt::entity, int>::size_type{2}));
ASSERT_EQ(pool.capacity(), (entt::storage<entt::entity, int>::size_type{2}));
// this should crash with asan enabled if we break the constraint
const auto entity = *it;
@@ -719,46 +719,46 @@ TEST(Storage, CanModifyDuringIteration) {
}
TEST(Storage, ReferencesGuaranteed) {
entt::storage<entt::entity, boxed_int> set;
entt::storage<entt::entity, boxed_int> pool;
set.construct(entt::entity{0}, 0);
set.construct(entt::entity{1}, 1);
pool.construct(entt::entity{0}, 0);
pool.construct(entt::entity{1}, 1);
ASSERT_EQ(set.get(entt::entity{0}).value, 0);
ASSERT_EQ(set.get(entt::entity{1}).value, 1);
ASSERT_EQ(pool.get(entt::entity{0}).value, 0);
ASSERT_EQ(pool.get(entt::entity{1}).value, 1);
for(auto &&type: set) {
for(auto &&type: pool) {
if(type.value) {
type.value = 42;
}
}
ASSERT_EQ(set.get(entt::entity{0}).value, 0);
ASSERT_EQ(set.get(entt::entity{1}).value, 42);
ASSERT_EQ(pool.get(entt::entity{0}).value, 0);
ASSERT_EQ(pool.get(entt::entity{1}).value, 42);
auto begin = set.begin();
auto begin = pool.begin();
while(begin != set.end()) {
while(begin != pool.end()) {
(begin++)->value = 3;
}
ASSERT_EQ(set.get(entt::entity{0}).value, 3);
ASSERT_EQ(set.get(entt::entity{1}).value, 3);
ASSERT_EQ(pool.get(entt::entity{0}).value, 3);
ASSERT_EQ(pool.get(entt::entity{1}).value, 3);
}
TEST(Storage, MoveOnlyComponent) {
// the purpose is to ensure that move only components are always accepted
entt::storage<entt::entity, std::unique_ptr<int>> set;
(void)set;
entt::storage<entt::entity, std::unique_ptr<int>> pool;
(void)pool;
}
TEST(Storage, ConstructorExceptionDoesNotAddToSet) {
entt::storage<entt::entity, throwing_component> set;
TEST(Storage, ConstructorExceptionDoesNotAddToStorage) {
entt::storage<entt::entity, throwing_component> pool;
try {
set.construct(entt::entity{0});
pool.construct(entt::entity{0});
FAIL() << "Expected constructor_exception to be thrown";
} catch (const throwing_component::constructor_exception &) {
ASSERT_TRUE(set.empty());
ASSERT_TRUE(pool.empty());
}
}