Procházet zdrojové kódy

sparse_set/storage:
* exception safety guarantee (sort of)
* ::insert optimization for non random access iterators

Michele Caini před 4 roky
rodič
revize
7624a9d34c

+ 9 - 3
src/entt/entity/sparse_set.hpp

@@ -239,7 +239,9 @@ class basic_sparse_set {
     void push_back(const Entity entt) {
         ENTT_ASSERT(count != reserved, "No more space left");
         assure_page(page(entt))[offset(entt)] = entity_type{static_cast<typename traits_type::entity_type>(count)};
-        alloc_traits::construct(allocator, std::addressof(packed[count++]), entt);
+        alloc_traits::construct(allocator, std::addressof(packed[count]), entt);
+        // exception safety guarantee requires to update this after construction
+        ++count;
     }
 
     void pop(const Entity entt, void *ud) {
@@ -558,15 +560,19 @@ public:
      * @tparam It Type of input iterator.
      * @param first An iterator to the first element of the range of entities.
      * @param last An iterator past the last element of the range of entities.
+     * @return The number of elements assigned to the sparse set.
      */
     template<typename It>
-    void insert(It first, It last) {
-        maybe_resize_packed(packed_size_for(count + std::distance(first, last)));
+    size_type insert(It first, It last) {
+        const auto length = std::distance(first, last);
+        maybe_resize_packed(packed_size_for(count + length));
 
         for(; first != last; ++first) {
             ENTT_ASSERT(!contains(*first), "Set already contains entity");
             push_back(*first);
         }
+
+        return length;
     }
 
     /**

+ 15 - 15
src/entt/entity/storage.hpp

@@ -227,9 +227,9 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
     }
 
     template<typename... Args>
-    Type & construct_at(const std::size_t pos, Args &&... args) {
-        ENTT_ASSERT(pos < (bucket * page_size), "No more space left");
-        auto &ref = packed[page(pos)][offset(pos)];
+    Type & push_back(Args &&... args) {
+        ENTT_ASSERT(count < (bucket * page_size), "No more space left");
+        auto &ref = packed[page(count)][offset(count)];
 
         if constexpr(std::is_aggregate_v<value_type>) {
             alloc_traits::construct(allocator, std::addressof(ref), Type{std::forward<Args>(args)...});
@@ -237,6 +237,9 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
             alloc_traits::construct(allocator, std::addressof(ref), std::forward<Args>(args)...);
         }
 
+        // exception safety guarantee requires to update this after construction
+        ++count;
+
         return ref;
     }
 
@@ -503,7 +506,7 @@ public:
     template<typename... Args>
     value_type & emplace(const entity_type entt, Args &&... args) {
         maybe_resize_packed(count + 1u);
-        auto &value = construct_at(count++, std::forward<Args>(args)...);
+        auto &value = push_back(std::forward<Args>(args)...);
         // entity goes after component in case constructor throws
         underlying_type::emplace(entt);
         return value;
@@ -539,14 +542,13 @@ public:
      */
     template<typename It>
     void insert(It first, It last, const value_type &value = {}) {
-        if(const auto sz = count + std::distance(first, last); sz != count) {
+        if(const auto length = underlying_type::insert(first, last); length) {
+            const auto sz = count + length;
             maybe_resize_packed(sz);
 
-            for(; count < sz; ++count) {
-                construct_at(count, value);
+            while(count != sz) {
+                push_back(value);
             }
-
-            underlying_type::insert(first, last);
         }
     }
 
@@ -565,14 +567,12 @@ public:
      */
     template<typename EIt, typename CIt>
     void insert(EIt first, EIt last, CIt from, CIt to) {
-        if(const auto sz = count + std::distance(from, to); sz != count) {
-            maybe_resize_packed(sz);
+        if(const auto length = underlying_type::insert(first, last); length) {
+            maybe_resize_packed(count + length);
 
-            for(; count < sz; ++count) {
-                construct_at(count, *(from++));
+            for(; from != to; ++from) {
+                push_back(*from);
             }
-
-            underlying_type::insert(first, last);
         }
     }
 

+ 4 - 1
test/entt/entity/sparse_set.cpp

@@ -134,7 +134,10 @@ TEST(SparseSet, Insert) {
     entities[1] = entt::entity{42};
 
     set.emplace(entt::entity{12});
-    set.insert(std::begin(entities), std::end(entities));
+
+    ASSERT_EQ(set.insert(std::end(entities), std::end(entities)), 0u);
+    ASSERT_EQ(set.insert(std::begin(entities), std::end(entities)), 2u);
+
     set.emplace(entt::entity{24});
 
     ASSERT_TRUE(set.contains(entities[0]));