Przeglądaj źródła

sort allows to work with entities

Michele Caini 7 lat temu
rodzic
commit
96f267cbfd

+ 1 - 1
TODO

@@ -16,11 +16,11 @@
   - each components only return actual component, so shared components are returned only once
   - 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
 * 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 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 <entity, T &...> (proxy), multi-pass guaranteed
 * 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
 * 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)
 * 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
   - 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
 * 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)
 * add NIO to the EnTT in Action list (link to my CV/LinkedIN)
+* remove entity function from sparse sets (no longer required)
 * reactive systems
 * reactive systems

+ 25 - 20
docs/md/entity.md

@@ -338,14 +338,10 @@ To be notified when components are destroyed, use the `destruction` member
 function instead.
 function instead.
 
 
 The function type of a listener is the same in both the cases and should be
 The function type of a listener is the same in both the cases and should be
-equivalent to:
+equivalent to the following:
 
 
 ```cpp
 ```cpp
-// when the default entity and the default registry are used
 void(registry &, entt::entity);
 void(registry &, entt::entity);
-
-// when a different specialization of the registry is used
-void(basic_registry<Entity> &, Entity);
 ```
 ```
 
 
 In other terms, a listener is provided with the registry that triggered the
 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`.<br/>
 `EnTT`.<br/>
 In fact, there are two functions that respond to slightly different needs:
 In fact, there are two functions that respond to slightly different needs:
 
 
-* Components can be sorted directly:
+* Components can be sorted either directly:
 
 
   ```cpp
   ```cpp
   registry.sort<renderable>([](const auto &lhs, const auto &rhs) {
   registry.sort<renderable>([](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<renderable>([](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
   There exists also the possibility to use a custom sort function object, as
   long as it adheres to the requirements described in the inline
   long as it adheres to the requirements described in the inline
   documentation.<br/>
   documentation.<br/>
   This is possible mainly because users can get much more with a custom sort
   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.
   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:
 * Components can be sorted according to the order imposed by another component:
@@ -594,7 +599,7 @@ temporary object.
 Example of use:
 Example of use:
 
 
 ```cpp
 ```cpp
-entt::continuous_loader<entity_type> loader{registry};
+entt::continuous_loader<entt::entity> loader{registry};
 input_archive input;
 input_archive input;
 
 
 loader.entities(input)
 loader.entities(input)
@@ -618,7 +623,7 @@ one.
 The `component` member function restores all and only the components specified
 The `component` member function restores all and only the components specified
 and assigns them to the right entities.<br/>
 and assigns them to the right entities.<br/>
 In case the component contains entities itself (either as data members of type
 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
 automatically. To do that, it's enough to specify the data members to update as
 shown in the example.
 shown in the example.
 
 
@@ -642,18 +647,18 @@ In particular:
   function call operator with the following signature to store entities:
   function call operator with the following signature to store entities:
 
 
   ```cpp
   ```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.<br/>
+  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.<br/>
   In addition, an archive must accept a pair of entity and component for each
   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
   type to be serialized. Therefore, given a type `T`, the archive must contain a
   function call operator with the following signature:
   function call operator with the following signature:
 
 
   ```cpp
   ```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
   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:
   function call operator with the following signature to load entities:
 
 
   ```cpp
   ```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
   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
   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.<br/>
   the _size_ of the set they are going to load.<br/>
@@ -676,7 +681,7 @@ In particular:
   function call operator with the following signature:
   function call operator with the following signature:
 
 
   ```cpp
   ```cpp
-  void operator()(Entity &, T &);
+  void operator()(entt::entity &, T &);
   ```
   ```
 
 
   Every time such an operator is invoked, the archive must read the next
   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:
 conversions from the null entity to identifiers of any allowed type:
 
 
 ```cpp
 ```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:
 Similarly, the null entity can be compared to any other identifier:
@@ -1309,7 +1314,7 @@ also references will continue to be valid.<br/>
 Consider the following example:
 Consider the following example:
 
 
 ```cpp
 ```cpp
-registry.view<position>([&](auto entity, auto &pos) {
+registry.view<position>([&](const auto entity, auto &pos) {
     registry.assign<position>(registry.create(), 0., 0.);
     registry.assign<position>(registry.create(), 0., 0.);
     pos.x = 0.; // warning: dangling pointer
     pos.x = 0.; // warning: dangling pointer
 });
 });

+ 3 - 3
src/entt/entity/registry.hpp

@@ -1003,13 +1003,13 @@ public:
      * @tparam Sort Type of sort function object.
      * @tparam Sort Type of sort function object.
      * @tparam Args Types of arguments to forward to the sort function object.
      * @tparam Args Types of arguments to forward to the sort function object.
      * @param compare A valid comparison 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.
      * @param args Arguments to forward to the sort function object, if any.
      */
      */
     template<typename Component, typename Compare, typename Sort = std_sort, typename... Args>
     template<typename Component, typename Compare, typename Sort = std_sort, typename... Args>
-    void sort(Compare compare, Sort sort = Sort{}, Args &&... args) {
+    void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
         ENTT_ASSERT(!owned<Component>());
         ENTT_ASSERT(!owned<Component>());
-        assure<Component>()->sort(std::move(compare), std::move(sort), std::forward<Args>(args)...);
+        assure<Component>()->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
     }
     }
 
 
     /**
     /**

+ 22 - 11
src/entt/entity/sparse_set.hpp

@@ -1075,9 +1075,10 @@ public:
      *
      *
      * The comparison function object must return `true` if the first element
      * The comparison function object must return `true` if the first element
      * is _less_ than the second one, `false` otherwise. The signature of the
      * 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}
      * @code{.cpp}
+     * bool(const Entity, const Entity);
      * bool(const Type &, const Type &);
      * bool(const Type &, const Type &);
      * @endcode
      * @endcode
      *
      *
@@ -1096,8 +1097,8 @@ public:
      * this member function.
      * this member function.
      *
      *
      * @note
      * @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
      * @note
      * Attempting to iterate elements using a raw pointer returned by a call to
      * 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 Sort Type of sort function object.
      * @tparam Args Types of arguments to forward to the sort function object.
      * @tparam Args Types of arguments to forward to the sort function object.
      * @param compare A valid comparison 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.
      * @param args Arguments to forward to the sort function object, if any.
      */
      */
     template<typename Compare, typename Sort = std_sort, typename... Args>
     template<typename Compare, typename Sort = std_sort, typename... Args>
-    void sort(Compare compare, Sort sort = Sort{}, Args &&... args) {
-        static_assert(!std::is_empty_v<object_type>);
-
+    inline void sort(Compare compare, Sort algo = Sort{}, Args &&... args) {
         std::vector<size_type> copy(instances.size());
         std::vector<size_type> copy(instances.size());
         std::iota(copy.begin(), copy.end(), 0);
         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>(args)...);
+        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, 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>(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>(args)...);
+        }
 
 
         for(size_type pos = 0, last = copy.size(); pos < last; ++pos) {
         for(size_type pos = 0, last = copy.size(); pos < last; ++pos) {
             auto curr = pos;
             auto curr = pos;
@@ -1129,7 +1136,11 @@ public:
             while(curr != next) {
             while(curr != next) {
                 const auto lhs = copy[curr];
                 const auto lhs = copy[curr];
                 const auto rhs = copy[next];
                 const auto rhs = copy[next];
-                std::swap(instances[lhs], instances[rhs]);
+
+                if constexpr(!std::is_empty_v<object_type>) {
+                    std::swap(instances[lhs], instances[rhs]);
+                }
+
                 underlying_type::swap(lhs, rhs);
                 underlying_type::swap(lhs, rhs);
                 copy[curr] = curr;
                 copy[curr] = curr;
                 curr = next;
                 curr = next;

+ 71 - 70
test/entt/entity/sparse_set.cpp

@@ -9,6 +9,7 @@
 #include <entt/entity/sparse_set.hpp>
 #include <entt/entity/sparse_set.hpp>
 
 
 struct empty_type {};
 struct empty_type {};
+struct boxed_int { int value; };
 
 
 TEST(SparseSetNoType, Functionalities) {
 TEST(SparseSetNoType, Functionalities) {
     entt::sparse_set<std::uint64_t> set;
     entt::sparse_set<std::uint64_t> set;
@@ -800,110 +801,110 @@ TEST(SparseSetWithType, RawEmptyType) {
 }
 }
 
 
 TEST(SparseSetWithType, SortOrdered) {
 TEST(SparseSetWithType, SortOrdered) {
-    entt::sparse_set<std::uint64_t, int> set;
+    entt::sparse_set<std::uint64_t, boxed_int> 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) {
     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 begin = set.begin();
     auto end = set.end();
     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);
     ASSERT_EQ(begin, end);
 }
 }
 
 
 TEST(SparseSetWithType, SortReverse) {
 TEST(SparseSetWithType, SortReverse) {
-    entt::sparse_set<std::uint64_t, int> set;
-
-    set.construct(12, 1);
-    set.construct(42, 3);
-    set.construct(7, 6);
-    set.construct(3, 9);
-    set.construct(9, 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);
-
-    set.sort([](auto lhs, auto rhs) {
-        return lhs < rhs;
+    entt::sparse_set<std::uint64_t, boxed_int> set;
+
+    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).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([&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 begin = set.begin();
     auto end = set.end();
     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);
     ASSERT_EQ(begin, end);
 }
 }
 
 
 TEST(SparseSetWithType, SortUnordered) {
 TEST(SparseSetWithType, SortUnordered) {
-    entt::sparse_set<std::uint64_t, int> set;
+    entt::sparse_set<std::uint64_t, boxed_int> 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) {
     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 begin = set.begin();
     auto end = set.end();
     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);
     ASSERT_EQ(begin, end);
 }
 }