Просмотр исходного кода

storage: insert for non-empty types always appends elements

Michele Caini 4 лет назад
Родитель
Сommit
1b22fe6de2
2 измененных файлов с 42 добавлено и 19 удалено
  1. 35 11
      src/entt/entity/storage.hpp
  2. 7 8
      test/entt/entity/storage.cpp

+ 35 - 11
src/entt/entity/storage.hpp

@@ -293,16 +293,36 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
         container.resize(from);
     }
 
-    template<typename It, typename Generator>
-    void consume_range(It first, It last, Generator generator) {
-        for(const auto sz = base_type::size(); first != last && base_type::slot() != sz; ++first) {
-            emplace(*first, generator());
-        }
+    template<typename It, typename Builder>
+    void consume_range(It first, It last, Builder builder) {
+        if constexpr(std::is_invocable_v<decltype(&basic_storage::try_insert), basic_storage &, It, It>) {
+            base_type::try_insert(first, last);
 
-        reserve(base_type::size() + std::distance(first, last));
+            ENTT_TRY {
+                for(; first != last; ++first) {
+                    builder(to_address(assure_at_least(base_type::index(*first))));
+                }
+            }
+            ENTT_CATCH {
+                for(; first != last; ++first) {
+                    base_type::try_erase(*first);
+                }
 
-        for(; first != last; ++first) {
-            emplace(*first, generator());
+                ENTT_THROW;
+            }
+        } else {
+            for(; first != last; ++first) {
+                entity_type curr[1u]{*first};
+                base_type::try_insert(curr, curr + 1u);
+
+                ENTT_TRY {
+                    builder(to_address(assure_at_least(base_type::index(*first))));
+                }
+                ENTT_CATCH {
+                    base_type::try_erase(*first);
+                    ENTT_THROW;
+                }
+            }
         }
     }
 
@@ -687,7 +707,9 @@ public:
      */
     template<typename It>
     void insert(It first, It last, const value_type &value = {}) {
-        consume_range(std::move(first), std::move(last), [&value]() -> decltype(auto) { return value; });
+        consume_range(std::move(first), std::move(last), [&value, this](auto *elem) {
+            alloc_traits::construct(packed.second(), elem, value);
+        });
     }
 
     /**
@@ -704,7 +726,9 @@ public:
      */
     template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<std::decay_t<typename std::iterator_traits<CIt>::value_type>, value_type>>>
     void insert(EIt first, EIt last, CIt from) {
-        consume_range(std::move(first), std::move(last), [&from]() -> decltype(auto) { return *(from++); });
+        consume_range(std::move(first), std::move(last), [&from, this](auto *elem) {
+            alloc_traits::construct(packed.second(), elem, *(from++));
+        });
     }
 
     /**
@@ -856,7 +880,7 @@ public:
      */
     template<typename It, typename... Args>
     void insert(It first, It last, Args &&...) {
-        if constexpr(std::is_invocable_v<decltype(&base_type::try_insert), base_type &, It, It>) {
+        if constexpr(std::is_invocable_v<decltype(&basic_storage::try_insert), basic_storage &, It, It>) {
             base_type::try_insert(first, last);
         } else {
             for(; first != last; ++first) {

+ 7 - 8
test/entt/entity/storage.cpp

@@ -285,11 +285,11 @@ TEST(Storage, Insert) {
     const stable_type values[2u] = {stable_type{42}, stable_type{3}};
     pool.insert(std::rbegin(entities), std::rend(entities), std::begin(values));
 
-    ASSERT_EQ(pool.size(), 2u);
-    ASSERT_EQ(pool.at(0u), entities[0u]);
-    ASSERT_EQ(pool.at(1u), entities[1u]);
-    ASSERT_EQ(pool.index(entities[0u]), 0u);
-    ASSERT_EQ(pool.index(entities[1u]), 1u);
+    ASSERT_EQ(pool.size(), 4u);
+    ASSERT_EQ(pool.at(2u), entities[1u]);
+    ASSERT_EQ(pool.at(3u), entities[0u]);
+    ASSERT_EQ(pool.index(entities[0u]), 3u);
+    ASSERT_EQ(pool.index(entities[1u]), 2u);
     ASSERT_EQ(pool.get(entities[0u]).value, 3);
     ASSERT_EQ(pool.get(entities[1u]).value, 42);
 }
@@ -1656,15 +1656,14 @@ TEST(Storage, ThrowingAllocator) {
     test::throwing_allocator<entt::entity>::trigger_after_allocate = true;
 
     ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), 0), test::throwing_allocator<entt::entity>::exception_type);
-    ASSERT_TRUE(pool.contains(entt::entity{1}));
+    ASSERT_FALSE(pool.contains(entt::entity{1}));
     ASSERT_FALSE(pool.contains(entt::entity{ENTT_SPARSE_PAGE}));
 
-    pool.erase(entt::entity{1});
     const int components[2u]{1, ENTT_SPARSE_PAGE};
     test::throwing_allocator<entt::entity>::trigger_on_allocate = true;
 
     ASSERT_THROW(pool.insert(std::begin(entities), std::end(entities), std::begin(components)), test::throwing_allocator<entt::entity>::exception_type);
-    ASSERT_TRUE(pool.contains(entt::entity{1}));
+    ASSERT_FALSE(pool.contains(entt::entity{1}));
     ASSERT_FALSE(pool.contains(entt::entity{ENTT_SPARSE_PAGE}));
 }