From 96f267cbfdc615f874fc8846dcd6f7cd2c89edb9 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Fri, 5 Apr 2019 23:00:58 +0200 Subject: [PATCH] sort allows to work with entities --- TODO | 2 +- docs/md/entity.md | 45 ++++++----- src/entt/entity/registry.hpp | 6 +- src/entt/entity/sparse_set.hpp | 33 +++++--- test/entt/entity/sparse_set.cpp | 135 ++++++++++++++++---------------- 5 files changed, 119 insertions(+), 102 deletions(-) diff --git a/TODO b/TODO index 43858eed6..cf44dd3a9 100644 --- a/TODO +++ b/TODO @@ -16,11 +16,11 @@ - each components only return actual component, so shared components are returned only once * types defined at runtime that refer to the same compile-time type (but to different pools) are possible, the library is almost there * 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 entity function to views/groups (component -> owner, see sparse sets) * add opaque input iterators to views and groups that return tuples (proxy), multi-pass guaranteed * add fast lane for raw iterations, extend mt doc to describe allowed add/remove with pre-allocations on fast lanes * review sparse set to allow customization (mix pack in the spec, base is position only) - non-owning groups can iterate pages and skip empty ones, this should mitigate the lack of the packed array * review 64 bit id: user defined area + dedicated member on the registry to set it * add NIO to the EnTT in Action list (link to my CV/LinkedIN) +* remove entity function from sparse sets (no longer required) * reactive systems diff --git a/docs/md/entity.md b/docs/md/entity.md index b74132559..09676b545 100644 --- a/docs/md/entity.md +++ b/docs/md/entity.md @@ -338,14 +338,10 @@ To be notified when components are destroyed, use the `destruction` member function instead. The function type of a listener is the same in both the cases and should be -equivalent to: +equivalent to the following: ```cpp -// when the default entity and the default registry are used void(registry &, entt::entity); - -// when a different specialization of the registry is used -void(basic_registry &, Entity); ``` In other terms, a listener is provided with the registry that triggered the @@ -431,7 +427,7 @@ It goes without saying that sorting entities and components is possible with `EnTT`.
In fact, there are two functions that respond to slightly different needs: -* Components can be sorted directly: +* Components can be sorted either directly: ```cpp registry.sort([](const auto &lhs, const auto &rhs) { @@ -440,11 +436,20 @@ In fact, there are two functions that respond to slightly different needs: }); ``` + Or by accessing their entities: + + ```cpp + registry.sort([](const entt::entity &lhs, const entt::entity &rhs) { + return lhs.z < rhs.z; + + }); + ``` + There exists also the possibility to use a custom sort function object, as long as it adheres to the requirements described in the inline documentation.
This is possible mainly because users can get much more with a custom sort - function object if the pattern of usage is known. As an example, in case of an + function object if the usage pattern is known. As an example, in case of an almost sorted pool, quick sort could be much, much slower than insertion sort. * Components can be sorted according to the order imposed by another component: @@ -594,7 +599,7 @@ temporary object. Example of use: ```cpp -entt::continuous_loader loader{registry}; +entt::continuous_loader loader{registry}; input_archive input; loader.entities(input) @@ -618,7 +623,7 @@ one. The `component` member function restores all and only the components specified and assigns them to the right entities.
In case the component contains entities itself (either as data members of type -`entity_type` or as containers of entities), the loader can update them +`entt::entity` or as containers of entities), the loader can update them automatically. To do that, it's enough to specify the data members to update as shown in the example. @@ -642,18 +647,18 @@ In particular: function call operator with the following signature to store entities: ```cpp - void operator()(Entity); + void operator()(entt::entity); ``` - Where `Entity` is the type of the entities used by the registry. Note that all - the member functions of the snapshot class make also an initial call to this - endpoint to save the _size_ of the set they are going to store.
+ Where `entt::entity` is the type of the entities used by the registry. Note + that all the member functions of the snapshot class make also an initial call + to this endpoint to save the _size_ of the set they are going to store.
In addition, an archive must accept a pair of entity and component for each type to be serialized. Therefore, given a type `T`, the archive must contain a function call operator with the following signature: ```cpp - void operator()(Entity, const T &); + void operator()(entt::entity, const T &); ``` The output archive can freely decide how to serialize the data. The register @@ -663,11 +668,11 @@ In particular: function call operator with the following signature to load entities: ```cpp - void operator()(Entity &); + void operator()(entt::entity &); ``` - Where `Entity` is the type of the entities used by the registry. Each time the - function is invoked, the archive must read the next element from the + Where `entt::entity` is the type of the entities used by the registry. Each + time the function is invoked, the archive must read the next element from the underlying storage and copy it in the given variable. Note that all the member functions of a loader class make also an initial call to this endpoint to read the _size_ of the set they are going to load.
@@ -676,7 +681,7 @@ In particular: function call operator with the following signature: ```cpp - void operator()(Entity &, T &); + void operator()(entt::entity &, T &); ``` Every time such an operator is invoked, the archive must read the next @@ -822,7 +827,7 @@ other than defining the null entity itself. However, there exist implicit conversions from the null entity to identifiers of any allowed type: ```cpp -typename entt::registry::entity_type null = entt::null; +entt::entity null = entt::null; ``` Similarly, the null entity can be compared to any other identifier: @@ -1309,7 +1314,7 @@ also references will continue to be valid.
Consider the following example: ```cpp -registry.view([&](auto entity, auto &pos) { +registry.view([&](const auto entity, auto &pos) { registry.assign(registry.create(), 0., 0.); pos.x = 0.; // warning: dangling pointer }); diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index 504061ac9..ea3da54d2 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -1003,13 +1003,13 @@ public: * @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 sort A valid sort function object. + * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template - void sort(Compare compare, Sort sort = Sort{}, Args &&... args) { + void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { ENTT_ASSERT(!owned()); - assure()->sort(std::move(compare), std::move(sort), std::forward(args)...); + assure()->sort(std::move(compare), std::move(algo), std::forward(args)...); } /** diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index 475b3302a..71cf21e71 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -1075,9 +1075,10 @@ public: * * 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: + * comparison function should be equivalent to one of the following: * * @code{.cpp} + * bool(const Entity, const Entity); * bool(const Type &, const Type &); * @endcode * @@ -1096,8 +1097,8 @@ public: * this member function. * * @note - * Empty components aren't explicitly instantiated. Therefore, this function - * isn't available for them. + * 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 @@ -1108,19 +1109,25 @@ public: * @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 sort A valid sort function object. + * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template - void sort(Compare compare, Sort sort = Sort{}, Args &&... args) { - static_assert(!std::is_empty_v); - + inline void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { std::vector copy(instances.size()); std::iota(copy.begin(), copy.end(), 0); - sort(copy.begin(), copy.end(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { - return compare(std::as_const(instances[rhs]), std::as_const(instances[lhs])); - }, std::forward(args)...); + if constexpr(std::is_invocable_v) { + static_assert(!std::is_empty_v); + + algo(copy.rbegin(), copy.rend(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + return compare(std::as_const(instances[lhs]), std::as_const(instances[rhs])); + }, std::forward(args)...); + } else { + algo(copy.rbegin(), copy.rend(), [this, compare = std::move(compare), entities = underlying_type::data()](const auto lhs, const auto rhs) { + return compare(entities[lhs], entities[rhs]); + }, std::forward(args)...); + } for(size_type pos = 0, last = copy.size(); pos < last; ++pos) { auto curr = pos; @@ -1129,7 +1136,11 @@ public: while(curr != next) { const auto lhs = copy[curr]; const auto rhs = copy[next]; - std::swap(instances[lhs], instances[rhs]); + + if constexpr(!std::is_empty_v) { + std::swap(instances[lhs], instances[rhs]); + } + underlying_type::swap(lhs, rhs); copy[curr] = curr; curr = next; diff --git a/test/entt/entity/sparse_set.cpp b/test/entt/entity/sparse_set.cpp index f8196edba..905c1975f 100644 --- a/test/entt/entity/sparse_set.cpp +++ b/test/entt/entity/sparse_set.cpp @@ -9,6 +9,7 @@ #include struct empty_type {}; +struct boxed_int { int value; }; TEST(SparseSetNoType, Functionalities) { entt::sparse_set set; @@ -800,110 +801,110 @@ TEST(SparseSetWithType, RawEmptyType) { } TEST(SparseSetWithType, SortOrdered) { - entt::sparse_set set; + entt::sparse_set set; - set.construct(12, 12); - set.construct(42, 9); - set.construct(7, 6); - set.construct(3, 3); - set.construct(9, 1); + set.construct(12, boxed_int{12}); + set.construct(42, boxed_int{9}); + set.construct(7, boxed_int{6}); + set.construct(3, boxed_int{3}); + set.construct(9, boxed_int{1}); - ASSERT_EQ(set.get(12), 12); - ASSERT_EQ(set.get(42), 9); - ASSERT_EQ(set.get(7), 6); - ASSERT_EQ(set.get(3), 3); - ASSERT_EQ(set.get(9), 1); + ASSERT_EQ(set.get(12).value, 12); + ASSERT_EQ(set.get(42).value, 9); + ASSERT_EQ(set.get(7).value, 6); + ASSERT_EQ(set.get(3).value, 3); + ASSERT_EQ(set.get(9).value, 1); set.sort([](auto lhs, auto rhs) { - return lhs < rhs; + return lhs.value < rhs.value; }); - ASSERT_EQ(*(set.raw() + 0u), 12); - ASSERT_EQ(*(set.raw() + 1u), 9); - ASSERT_EQ(*(set.raw() + 2u), 6); - ASSERT_EQ(*(set.raw() + 3u), 3); - ASSERT_EQ(*(set.raw() + 4u), 1); + 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); auto begin = set.begin(); auto end = set.end(); - ASSERT_EQ(*(begin++), 1); - ASSERT_EQ(*(begin++), 3); - ASSERT_EQ(*(begin++), 6); - ASSERT_EQ(*(begin++), 9); - ASSERT_EQ(*(begin++), 12); + ASSERT_EQ((begin++)->value, 1); + ASSERT_EQ((begin++)->value, 3); + ASSERT_EQ((begin++)->value, 6); + ASSERT_EQ((begin++)->value, 9); + ASSERT_EQ((begin++)->value, 12); ASSERT_EQ(begin, end); } TEST(SparseSetWithType, SortReverse) { - entt::sparse_set set; + entt::sparse_set set; - set.construct(12, 1); - set.construct(42, 3); - set.construct(7, 6); - set.construct(3, 9); - set.construct(9, 12); + set.construct(12, boxed_int{1}); + set.construct(42, boxed_int{3}); + set.construct(7, boxed_int{6}); + set.construct(3, boxed_int{9}); + set.construct(9, boxed_int{12}); - ASSERT_EQ(set.get(12), 1); - ASSERT_EQ(set.get(42), 3); - ASSERT_EQ(set.get(7), 6); - ASSERT_EQ(set.get(3), 9); - ASSERT_EQ(set.get(9), 12); + ASSERT_EQ(set.get(12).value, 1); + ASSERT_EQ(set.get(42).value, 3); + ASSERT_EQ(set.get(7).value, 6); + ASSERT_EQ(set.get(3).value, 9); + ASSERT_EQ(set.get(9).value, 12); - set.sort([](auto lhs, auto rhs) { - return lhs < rhs; + set.sort([&set](std::uint64_t lhs, std::uint64_t rhs) { + return set.get(lhs).value < set.get(rhs).value; }); - ASSERT_EQ(*(set.raw() + 0u), 12); - ASSERT_EQ(*(set.raw() + 1u), 9); - ASSERT_EQ(*(set.raw() + 2u), 6); - ASSERT_EQ(*(set.raw() + 3u), 3); - ASSERT_EQ(*(set.raw() + 4u), 1); + 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); auto begin = set.begin(); auto end = set.end(); - ASSERT_EQ(*(begin++), 1); - ASSERT_EQ(*(begin++), 3); - ASSERT_EQ(*(begin++), 6); - ASSERT_EQ(*(begin++), 9); - ASSERT_EQ(*(begin++), 12); + ASSERT_EQ((begin++)->value, 1); + ASSERT_EQ((begin++)->value, 3); + ASSERT_EQ((begin++)->value, 6); + ASSERT_EQ((begin++)->value, 9); + ASSERT_EQ((begin++)->value, 12); ASSERT_EQ(begin, end); } TEST(SparseSetWithType, SortUnordered) { - entt::sparse_set set; + entt::sparse_set set; - set.construct(12, 6); - set.construct(42, 3); - set.construct(7, 1); - set.construct(3, 9); - set.construct(9, 12); + set.construct(12, boxed_int{6}); + set.construct(42, boxed_int{3}); + set.construct(7, boxed_int{1}); + set.construct(3, boxed_int{9}); + set.construct(9, boxed_int{12}); - ASSERT_EQ(set.get(12), 6); - ASSERT_EQ(set.get(42), 3); - ASSERT_EQ(set.get(7), 1); - ASSERT_EQ(set.get(3), 9); - ASSERT_EQ(set.get(9), 12); + ASSERT_EQ(set.get(12).value, 6); + ASSERT_EQ(set.get(42).value, 3); + ASSERT_EQ(set.get(7).value, 1); + ASSERT_EQ(set.get(3).value, 9); + ASSERT_EQ(set.get(9).value, 12); set.sort([](auto lhs, auto rhs) { - return lhs < rhs; + return lhs.value < rhs.value; }); - ASSERT_EQ(*(set.raw() + 0u), 12); - ASSERT_EQ(*(set.raw() + 1u), 9); - ASSERT_EQ(*(set.raw() + 2u), 6); - ASSERT_EQ(*(set.raw() + 3u), 3); - ASSERT_EQ(*(set.raw() + 4u), 1); + 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); auto begin = set.begin(); auto end = set.end(); - ASSERT_EQ(*(begin++), 1); - ASSERT_EQ(*(begin++), 3); - ASSERT_EQ(*(begin++), 6); - ASSERT_EQ(*(begin++), 9); - ASSERT_EQ(*(begin++), 12); + ASSERT_EQ((begin++)->value, 1); + ASSERT_EQ((begin++)->value, 3); + ASSERT_EQ((begin++)->value, 6); + ASSERT_EQ((begin++)->value, 9); + ASSERT_EQ((begin++)->value, 12); ASSERT_EQ(begin, end); }