Kaynağa Gözat

runtime view: allocator support

Michele Caini 3 yıl önce
ebeveyn
işleme
bcfd6d1b4f

+ 0 - 1
TODO

@@ -13,7 +13,6 @@ DOC:
 * update entity doc when the storage based model is in place
 
 WIP:
-* runtime view: allocator support, storage access
 * get rid of observers, storage based views made them pointless - document alternatives
 * dense_map/set and registry: move ctor/op should be noexcept (op is conditionally noexcept though)
 * add storage getter for filters to views and groups

+ 1 - 1
src/entt/entity/fwd.hpp

@@ -31,7 +31,7 @@ class basic_registry;
 template<typename, typename, typename = void>
 class basic_view;
 
-template<typename Type>
+template<typename Type, typename = std::allocator<Type *>>
 class basic_runtime_view;
 
 template<typename, typename, typename>

+ 81 - 6
src/entt/entity/runtime_view.hpp

@@ -138,9 +138,17 @@ private:
  * In any other case, attempting to use a view results in undefined behavior.
  *
  * @tparam Type Common base type.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
  */
-template<typename Type>
-struct basic_runtime_view {
+template<typename Type, typename Allocator>
+class basic_runtime_view {
+    using alloc_traits = std::allocator_traits<Allocator>;
+    static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
+    using container_type = std::vector<Type *, Allocator>;
+
+public:
+    /*! @brief Allocator type. */
+    using allocator_type = Allocator;
     /*! @brief Underlying entity identifier. */
     using entity_type = typename Type::entity_type;
     /*! @brief Unsigned integer type. */
@@ -152,8 +160,75 @@ struct basic_runtime_view {
 
     /*! @brief Default constructor to use to create empty, invalid views. */
     basic_runtime_view() noexcept
-        : pools{},
-          filter{} {}
+        : basic_runtime_view{allocator_type{}} {}
+
+    /**
+     * @brief Constructs an empty, invalid view with a given allocator.
+     * @param allocator The allocator to use.
+     */
+    explicit basic_runtime_view(const allocator_type &allocator)
+        : pools{allocator},
+          filter{allocator} {}
+
+    /*! @brief Default copy constructor. */
+    basic_runtime_view(const basic_runtime_view &other) = default;
+
+    /**
+     * @brief Allocator-extended copy constructor.
+     * @param other The instance to copy from.
+     * @param allocator The allocator to use.
+     */
+    basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator)
+        : pools{other.pools, allocator},
+          filter{other.filter, allocator} {}
+
+    /*! @brief Default move constructor. */
+    basic_runtime_view(basic_runtime_view &&other) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
+
+    /**
+     * @brief Allocator-extended move constructor.
+     * @param other The instance to move from.
+     * @param allocator The allocator to use.
+     */
+    basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator)
+        : pools{std::move(other.pools), allocator},
+          filter{std::move(other.filter), allocator} {}
+
+    /**
+     * @brief Default copy assignment operator.
+     * @return This container.
+     */
+    basic_runtime_view &operator=(const basic_runtime_view &other) = default;
+
+    /**
+     * @brief Default move assignment operator.
+     * @return This container.
+     */
+    basic_runtime_view &operator=(basic_runtime_view &&other) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
+
+    /**
+     * @brief Exchanges the contents with those of a given view.
+     * @param other View to exchange the content with.
+     */
+    void swap(basic_runtime_view &other) {
+        using std::swap;
+        swap(pools, other.pools);
+        swap(filter, other.filter);
+    }
+
+    /**
+     * @brief Returns the associated allocator.
+     * @return The associated allocator.
+     */
+    [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+        return pools.get_allocator();
+    }
+
+    /*! @brief Clears the view. */
+    void clear() {
+        pools.clear();
+        filter.clear();
+    }
 
     /**
      * @brief Appends an opaque storage object to a runtime view.
@@ -251,8 +326,8 @@ struct basic_runtime_view {
     }
 
 private:
-    std::vector<base_type *> pools;
-    std::vector<base_type *> filter;
+    container_type pools;
+    container_type filter;
 };
 
 } // namespace entt

+ 145 - 43
test/entt/entity/runtime_view.cpp

@@ -71,13 +71,35 @@ TYPED_TEST(RuntimeView, Functionalities) {
         ASSERT_EQ(registry.get<char>(entity), '2');
     }
 
-    entt::runtime_view empty{};
+    view.clear();
 
-    ASSERT_EQ(empty.size_hint(), 0u);
-    ASSERT_EQ(empty.begin(), empty.end());
+    ASSERT_EQ(view.size_hint(), 0u);
+    ASSERT_EQ(view.begin(), view.end());
 }
 
-TYPED_TEST(RuntimeView, Iterator) {
+TYPED_TEST(RuntimeView, Constructors) {
+    using runtime_view_type = typename TestFixture::type;
+
+    entt::registry registry;
+    runtime_view_type view{};
+
+    const auto entity = registry.create();
+    registry.emplace<int>(entity);
+
+    view = runtime_view_type{std::allocator<int>{}};
+    view.iterate(registry.storage<int>());
+
+    ASSERT_TRUE(view.contains(entity));
+
+    runtime_view_type temp{view, view.get_allocator()};
+    runtime_view_type other{std::move(temp), view.get_allocator()};
+
+    ASSERT_TRUE(view.contains(entity));
+    ASSERT_FALSE(temp.contains(entity));
+    ASSERT_TRUE(other.contains(entity));
+}
+
+TYPED_TEST(RuntimeView, Copy) {
     using runtime_view_type = typename TestFixture::type;
 
     entt::registry registry;
@@ -87,8 +109,97 @@ TYPED_TEST(RuntimeView, Iterator) {
     registry.emplace<int>(entity);
     registry.emplace<char>(entity);
 
-    view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
-    using iterator = typename decltype(view)::iterator;
+    view.iterate(registry.storage<int>());
+
+    ASSERT_TRUE(view.contains(entity));
+
+    runtime_view_type other{view};
+
+    ASSERT_TRUE(view.contains(entity));
+    ASSERT_TRUE(other.contains(entity));
+
+    other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
+
+    ASSERT_TRUE(view.contains(entity));
+    ASSERT_FALSE(other.contains(entity));
+
+    other = view;
+
+    ASSERT_TRUE(view.contains(entity));
+    ASSERT_TRUE(other.contains(entity));
+}
+
+TYPED_TEST(RuntimeView, Move) {
+    using runtime_view_type = typename TestFixture::type;
+
+    entt::registry registry;
+    runtime_view_type view{};
+
+    const auto entity = registry.create();
+    registry.emplace<int>(entity);
+    registry.emplace<char>(entity);
+
+    view.iterate(registry.storage<int>());
+
+    ASSERT_TRUE(view.contains(entity));
+
+    runtime_view_type other{std::move(view)};
+
+    ASSERT_FALSE(view.contains(entity));
+    ASSERT_TRUE(other.contains(entity));
+
+    view = other;
+    other.iterate(registry.storage<int>()).exclude(registry.storage<char>());
+
+    ASSERT_TRUE(view.contains(entity));
+    ASSERT_FALSE(other.contains(entity));
+
+    other = std::move(view);
+
+    ASSERT_FALSE(view.contains(entity));
+    ASSERT_TRUE(other.contains(entity));
+}
+
+TYPED_TEST(RuntimeView, Swap) {
+    using runtime_view_type = typename TestFixture::type;
+
+    entt::registry registry;
+    runtime_view_type view{};
+    runtime_view_type other{};
+
+    const auto entity = registry.create();
+
+    registry.emplace<int>(entity);
+    view.iterate(registry.storage<int>());
+
+    ASSERT_EQ(view.size_hint(), 1u);
+    ASSERT_EQ(other.size_hint(), 0u);
+    ASSERT_TRUE(view.contains(entity));
+    ASSERT_FALSE(other.contains(entity));
+    ASSERT_NE(view.begin(), view.end());
+    ASSERT_EQ(other.begin(), other.end());
+
+    view.swap(other);
+
+    ASSERT_EQ(view.size_hint(), 0u);
+    ASSERT_EQ(other.size_hint(), 1u);
+    ASSERT_FALSE(view.contains(entity));
+    ASSERT_TRUE(other.contains(entity));
+    ASSERT_EQ(view.begin(), view.end());
+    ASSERT_NE(other.begin(), other.end());
+}
+
+TYPED_TEST(RuntimeView, Iterator) {
+    using runtime_view_type = typename TestFixture::type;
+    using iterator = typename runtime_view_type::iterator;
+
+    entt::registry registry;
+    runtime_view_type view{};
+
+    const auto entity = registry.create();
+
+    registry.emplace<int>(entity);
+    view.iterate(registry.storage<int>());
 
     iterator end{view.begin()};
     iterator begin{};
@@ -115,20 +226,18 @@ TYPED_TEST(RuntimeView, Contains) {
     entt::registry registry;
     runtime_view_type view{};
 
-    const auto e0 = registry.create();
-    registry.emplace<int>(e0);
-    registry.emplace<char>(e0);
+    const auto entity = registry.create();
+    const auto other = registry.create();
 
-    const auto e1 = registry.create();
-    registry.emplace<int>(e1);
-    registry.emplace<char>(e1);
+    registry.emplace<int>(entity);
+    registry.emplace<int>(other);
 
-    registry.destroy(e0);
+    registry.destroy(entity);
 
-    view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
+    view.iterate(registry.storage<int>());
 
-    ASSERT_FALSE(view.contains(e0));
-    ASSERT_TRUE(view.contains(e1));
+    ASSERT_FALSE(view.contains(entity));
+    ASSERT_TRUE(view.contains(other));
 }
 
 TYPED_TEST(RuntimeView, Empty) {
@@ -137,24 +246,19 @@ TYPED_TEST(RuntimeView, Empty) {
     entt::registry registry;
     runtime_view_type view{};
 
-    const auto e0 = registry.create();
-    registry.emplace<double>(e0);
-    registry.emplace<int>(e0);
-    registry.emplace<float>(e0);
+    const auto entity = registry.create();
+    const auto other = registry.create();
 
-    const auto e1 = registry.create();
-    registry.emplace<char>(e1);
-    registry.emplace<float>(e1);
+    registry.emplace<double>(entity);
+    registry.emplace<float>(other);
 
-    view.iterate(registry.storage<int>())
-        .iterate(registry.storage<char>())
-        .iterate(registry.storage<float>());
+    view.iterate(registry.storage<int>());
 
-    ASSERT_FALSE(view.contains(e0));
-    ASSERT_FALSE(view.contains(e1));
+    ASSERT_FALSE(view.contains(entity));
+    ASSERT_FALSE(view.contains(other));
     ASSERT_EQ(view.begin(), view.end());
-    ASSERT_EQ((std::find(view.begin(), view.end(), e0)), view.end());
-    ASSERT_EQ((std::find(view.begin(), view.end(), e1)), view.end());
+    ASSERT_EQ((std::find(view.begin(), view.end(), entity)), view.end());
+    ASSERT_EQ((std::find(view.begin(), view.end(), other)), view.end());
 }
 
 TYPED_TEST(RuntimeView, Each) {
@@ -163,17 +267,16 @@ TYPED_TEST(RuntimeView, Each) {
     entt::registry registry;
     runtime_view_type view{};
 
-    const auto e0 = registry.create();
-    registry.emplace<int>(e0);
-    registry.emplace<char>(e0);
+    const auto entity = registry.create();
+    const auto other = registry.create();
 
-    const auto e1 = registry.create();
-    registry.emplace<char>(e1);
+    registry.emplace<int>(entity);
+    registry.emplace<char>(other);
 
     view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
 
-    view.each([e0](const auto entt) {
-        ASSERT_EQ(entt, e0);
+    view.each([entity](const auto entt) {
+        ASSERT_EQ(entt, entity);
     });
 }
 
@@ -195,8 +298,8 @@ TYPED_TEST(RuntimeView, EachWithHoles) {
 
     view.iterate(registry.storage<int>()).iterate(registry.storage<char>());
 
-    view.each([e0](auto entity) {
-        ASSERT_EQ(e0, entity);
+    view.each([e0](auto entt) {
+        ASSERT_EQ(e0, entt);
     });
 }
 
@@ -214,14 +317,13 @@ TYPED_TEST(RuntimeView, ExcludedComponents) {
     registry.emplace<char>(e1);
 
     view.iterate(registry.storage<int>())
-        .exclude(registry.storage<char>())
-        .exclude(registry.storage<double>());
+        .exclude(registry.storage<char>());
 
     ASSERT_TRUE(view.contains(e0));
     ASSERT_FALSE(view.contains(e1));
 
-    view.each([e0](auto entity) {
-        ASSERT_EQ(e0, entity);
+    view.each([e0](auto entt) {
+        ASSERT_EQ(e0, entt);
     });
 }