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

storage/view/group: reintroduce (opaque) ::raw functions as it ought to be

Michele Caini 4 лет назад
Родитель
Сommit
b062bbf58d

+ 26 - 10
docs/md/entity.md

@@ -10,6 +10,7 @@
   * [Type-less and bitset-free](#type-less-and-bitset-free)
   * [Build your own](#build-your-own)
   * [Pay per use](#pay-per-use)
+  * [All or nothing](#all-or-nothing)
 * [Vademecum](#vademecum)
 * [Pools](#pools)
 * [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
@@ -118,6 +119,19 @@ performance where needed.
 So far, this choice has proven to be a good one and I really hope it can be for
 many others besides me.
 
+## All or nothing
+
+`EnTT` is such that a `T**` pointer (or whatever a custom pool returns) is
+always available to directly access all the instances of a given component type
+`T`.<br/>
+I cannot say whether it will be useful or not to the reader, but it's worth to
+mention it since it's one of the corner stones of this library.
+
+Many of the tools described below give the possibility to get this information
+and have been designed around this need.<br/>
+The rest is experimentation and the desire to invent something new, hoping to
+have succeeded.
+
 # Vademecum
 
 The registry to store, the views and the groups to iterate. That's all.
@@ -1237,11 +1251,12 @@ different in the two cases.
 Single component views are specialized in order to give a boost in terms of
 performance in all the situations. This kind of views can access the underlying
 data structures directly and avoid superfluous checks. There is nothing as fast
-as a single component view. In fact, they walk through a packed array of
-components and return them one at a time.<br/>
-Single component views offer a bunch of functionalities to get the number of
-entities they are going to return and a raw access to the entity list. It's also
-possible to ask a view if it contains a given entity.<br/>
+as a single component view. In fact, they walk through a packed (actually paged)
+array of components and return them one at a time.<br/>
+Single component views also offer a bunch of functionalities to get the number
+of entities they are going to return and a raw access to the entity list as well
+as to the component list. It's also possible to ask a view if it contains a
+given entity.<br/>
 Refer to the inline documentation for all the details.
 
 Multi component views iterate entities that have at least all the given
@@ -1435,8 +1450,9 @@ However, it's unlikely that users will be able to appreciate the impact of
 groups on the other functionalities of a registry.
 
 Groups offer a bunch of functionalities to get the number of entities they are
-going to return and a raw access to the entity list. It's also possible to ask a
-group if it contains a given entity.<br/>
+going to return and a raw access to the entity list as well as to the component
+list for owned components. It's also possible to ask a group if it contains a
+given entity.<br/>
 Refer to the inline documentation for all the details.
 
 There is no need to store groups aside for they are extremely cheap to
@@ -1846,9 +1862,9 @@ When an empty type is detected, it's not instantiated in any case. Therefore,
 only the entities to which it's assigned are made available.<br/>
 There doesn't exist a way to _get_ empty types from a registry, views and groups
 will never return instances for them (for example, during a call to `each`) and
-some functions such as `try_get` aren't available for empty types. Finally, the
-`sort` functionality will only accepts callbacks that require to return entities
-rather than components:
+some functions such as `try_get` or the raw access to the list of components
+aren't available for empty types. Finally, the `sort` functionality will only
+accepts callbacks that require to return entities rather than components:
 
 ```cpp
 registry.sort<empty_type>([](const entt::entity lhs, const entt::entity rhs) {

+ 16 - 0
src/entt/entity/group.hpp

@@ -680,6 +680,22 @@ public:
         return !*this || !*length;
     }
 
+    /**
+     * @brief Direct access to the raw representation offered by the storage.
+     *
+     * @warning
+     * This function is only available for owned types.
+     *
+     * @tparam Component Type of component in which one is interested.
+     * @return A pointer to the array of components.
+     */
+    template<typename Component>
+    [[nodiscard]] auto raw() const ENTT_NOEXCEPT {
+        static_assert((std::is_same_v<Component, Owned> || ...), "Non-owned type");
+        auto *cpool = std::get<storage_type<Component> *>(pools);
+        return cpool ? cpool->raw() : decltype(cpool->raw()){};
+    }
+
     /**
      * @brief Direct access to the list of entities.
      *

+ 21 - 3
src/entt/entity/storage.hpp

@@ -59,11 +59,12 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
     using alloc_type = typename std::allocator_traits<Allocator>::template rebind_alloc<Type>;
     using alloc_traits = std::allocator_traits<alloc_type>;
     using alloc_pointer = typename alloc_traits::pointer;
+    using alloc_const_pointer = typename alloc_traits::const_pointer;
 
     using bucket_alloc_type = typename std::allocator_traits<Allocator>::template rebind_alloc<alloc_pointer>;
     using bucket_alloc_traits = std::allocator_traits<bucket_alloc_type>;
     using bucket_alloc_pointer = typename bucket_alloc_traits::pointer;
-    using bucket_alloc_const_pointer = typename bucket_alloc_traits::const_pointer;
+    using bucket_alloc_const_pointer = typename std::allocator_traits<Allocator>::template rebind_alloc<alloc_const_pointer>::const_pointer;
 
     using entity_alloc_type = typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>;
 
@@ -74,7 +75,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
     class storage_iterator final {
         friend class basic_storage<Entity, Type>;
 
-        storage_iterator(const bucket_alloc_const_pointer *ref, const typename traits_type::difference_type idx) ENTT_NOEXCEPT
+        storage_iterator(bucket_alloc_pointer const *ref, const typename traits_type::difference_type idx) ENTT_NOEXCEPT
             : packed{ref}, index{idx}
         {}
 
@@ -166,7 +167,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
         }
 
     private:
-        const bucket_alloc_const_pointer *packed;
+        bucket_alloc_pointer const *packed;
         difference_type index;
     };
 
@@ -270,6 +271,10 @@ public:
     using entity_type = Entity;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
+    /*! @brief Pointer type to contained elements. */
+    using pointer = bucket_alloc_pointer;
+    /*! @brief Constant pointer type to contained elements. */
+    using const_pointer = bucket_alloc_const_pointer;
     /*! @brief Random access iterator type. */
     using iterator = storage_iterator<Type>;
     /*! @brief Constant random access iterator type. */
@@ -354,6 +359,19 @@ public:
         count ? maybe_resize_packed(count) : reset_to_empty();
     }
 
+    /**
+     * @brief Direct access to the array of objects.
+     * @return A pointer to the array of objects.
+     */
+    [[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT {
+        return packed;
+    }
+
+    /*! @copydoc raw */
+    [[nodiscard]] pointer raw() ENTT_NOEXCEPT {
+        return packed;
+    }
+
     /**
      * @brief Returns an iterator to the beginning.
      *

+ 8 - 0
src/entt/entity/view.hpp

@@ -708,6 +708,14 @@ public:
         return std::get<0>(pools)->empty();
     }
 
+    /**
+     * @brief Direct access to the raw representation offered by the storage.
+     * @return A pointer to the array of components.
+     */
+    [[nodiscard]] auto raw() const ENTT_NOEXCEPT {
+        return std::get<0>(pools)->raw();
+    }
+
     /**
      * @brief Direct access to the list of entities.
      *

+ 80 - 65
test/entt/entity/group.cpp

@@ -53,7 +53,7 @@ TEST(NonOwningGroup, Functionalities) {
         ASSERT_EQ(cgroup.get<const char>(entity), '2');
     }
 
-    ASSERT_EQ(*(group.data() + 0), e1);
+    ASSERT_EQ(group.data()[0u], e1);
 
     registry.erase<char>(e0);
     registry.erase<char>(e1);
@@ -221,17 +221,17 @@ TEST(NonOwningGroup, Sort) {
     registry.emplace<int>(e1, 1);
     registry.emplace<int>(e2, 2);
 
-    ASSERT_EQ(*(group.data() + 0u), e0);
-    ASSERT_EQ(*(group.data() + 1u), e1);
-    ASSERT_EQ(*(group.data() + 2u), e2);
+    ASSERT_EQ(group.data()[0u], e0);
+    ASSERT_EQ(group.data()[1u], e1);
+    ASSERT_EQ(group.data()[2u], e2);
 
     group.sort([](const entt::entity lhs, const entt::entity rhs) {
         return entt::to_integral(lhs) < entt::to_integral(rhs);
     });
 
-    ASSERT_EQ(*(group.data() + 0u), e2);
-    ASSERT_EQ(*(group.data() + 1u), e1);
-    ASSERT_EQ(*(group.data() + 2u), e0);
+    ASSERT_EQ(group.data()[0u], e2);
+    ASSERT_EQ(group.data()[1u], e1);
+    ASSERT_EQ(group.data()[2u], e0);
 
     ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
     ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u)));
@@ -243,9 +243,9 @@ TEST(NonOwningGroup, Sort) {
         return lhs > rhs;
     });
 
-    ASSERT_EQ(*(group.data() + 0u), e0);
-    ASSERT_EQ(*(group.data() + 1u), e1);
-    ASSERT_EQ(*(group.data() + 2u), e2);
+    ASSERT_EQ(group.data()[0u], e0);
+    ASSERT_EQ(group.data()[1u], e1);
+    ASSERT_EQ(group.data()[2u], e2);
 
     ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
     ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u)));
@@ -259,9 +259,9 @@ TEST(NonOwningGroup, Sort) {
         return std::get<0>(lhs) < std::get<0>(rhs);
     });
 
-    ASSERT_EQ(*(group.data() + 0u), e2);
-    ASSERT_EQ(*(group.data() + 1u), e1);
-    ASSERT_EQ(*(group.data() + 2u), e0);
+    ASSERT_EQ(group.data()[0u], e2);
+    ASSERT_EQ(group.data()[1u], e1);
+    ASSERT_EQ(group.data()[2u], e0);
 
     ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u)));
     ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u)));
@@ -665,8 +665,8 @@ TEST(OwningGroup, Functionalities) {
 
     ASSERT_EQ(group.size(), 1u);
 
-    ASSERT_EQ(*(cgroup.rbegin() + 0), e1);
-    ASSERT_EQ(*(group.rbegin() + 0), e1);
+    ASSERT_EQ(cgroup.raw<const int>()[0u][0u], 42);
+    ASSERT_EQ(group.raw<int>()[0u][0u], 42);
 
     for(auto entity: group) {
         ASSERT_EQ(std::get<0>(cgroup.get<const int, const char>(entity)), 42);
@@ -674,7 +674,8 @@ TEST(OwningGroup, Functionalities) {
         ASSERT_EQ(cgroup.get<const char>(entity), '2');
     }
 
-    ASSERT_EQ(*(group.data() + 0), e1);
+    ASSERT_EQ(group.data()[0u], e1);
+    ASSERT_EQ(group.raw<int>()[0u][0u], 42);
 
     registry.erase<char>(e0);
     registry.erase<char>(e1);
@@ -705,6 +706,7 @@ TEST(OwningGroup, Invalid) {
     ASSERT_TRUE(group.empty());
     ASSERT_EQ(group.size(), 0u);
 
+    ASSERT_EQ(group.raw<const int>(), nullptr);
     ASSERT_EQ(group.data(), nullptr);
 
     ASSERT_EQ(group.begin(), group.end());
@@ -839,17 +841,21 @@ TEST(OwningGroup, SortOrdered) {
         return group.get<boxed_int>(lhs).value < group.get<boxed_int>(rhs).value;
     });
 
-    ASSERT_EQ(*(group.data() + 0u), entities[0]);
-    ASSERT_EQ(*(group.data() + 1u), entities[1]);
-    ASSERT_EQ(*(group.data() + 2u), entities[2]);
+    ASSERT_EQ(group.data()[0u], entities[0]);
+    ASSERT_EQ(group.data()[1u], entities[1]);
+    ASSERT_EQ(group.data()[2u], entities[2]);
+    ASSERT_EQ(group.data()[3u], entities[3]);
+    ASSERT_EQ(group.data()[4u], entities[4]);
 
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 0u)).value, 12);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 1u)).value, 9);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 2u)).value, 6);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][0u].value, 12);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][1u].value, 9);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][2u].value, 6);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][3u].value, 1);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][4u].value, 2);
 
-    ASSERT_EQ(group.get<char>(*(group.data() + 0u)), 'a');
-    ASSERT_EQ(group.get<char>(*(group.data() + 1u)), 'b');
-    ASSERT_EQ(group.get<char>(*(group.data() + 2u)), 'c');
+    ASSERT_EQ(group.raw<char>()[0u][0u], 'a');
+    ASSERT_EQ(group.raw<char>()[0u][1u], 'b');
+    ASSERT_EQ(group.raw<char>()[0u][2u], 'c');
 
     ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{12}, 'a')));
     ASSERT_EQ((group.get<boxed_int, char>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
@@ -882,17 +888,21 @@ TEST(OwningGroup, SortReverse) {
         return lhs.value < rhs.value;
     });
 
-    ASSERT_EQ(*(group.data() + 0u), entities[2]);
-    ASSERT_EQ(*(group.data() + 1u), entities[1]);
-    ASSERT_EQ(*(group.data() + 2u), entities[0]);
+    ASSERT_EQ(group.data()[0u], entities[2]);
+    ASSERT_EQ(group.data()[1u], entities[1]);
+    ASSERT_EQ(group.data()[2u], entities[0]);
+    ASSERT_EQ(group.data()[3u], entities[3]);
+    ASSERT_EQ(group.data()[4u], entities[4]);
 
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 0u)).value, 12);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 1u)).value, 9);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 2u)).value, 6);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][0u].value, 12);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][1u].value, 9);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][2u].value, 6);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][3u].value, 1);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][4u].value, 2);
 
-    ASSERT_EQ(group.get<char>(*(group.data() + 0u)), 'c');
-    ASSERT_EQ(group.get<char>(*(group.data() + 1u)), 'b');
-    ASSERT_EQ(group.get<char>(*(group.data() + 2u)), 'a');
+    ASSERT_EQ(group.raw<char>()[0u][0u], 'c');
+    ASSERT_EQ(group.raw<char>()[0u][1u], 'b');
+    ASSERT_EQ(group.raw<char>()[0u][2u], 'a');
 
     ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{6}, 'a')));
     ASSERT_EQ((group.get<boxed_int, char>(entities[1])), (std::make_tuple(boxed_int{9}, 'b')));
@@ -933,29 +943,27 @@ TEST(OwningGroup, SortUnordered) {
         return std::get<1>(lhs) < std::get<1>(rhs);
     });
 
-    ASSERT_EQ(*(group.data() + 0u), entities[4]);
-    ASSERT_EQ(*(group.data() + 1u), entities[3]);
-    ASSERT_EQ(*(group.data() + 2u), entities[0]);
-    ASSERT_EQ(*(group.data() + 3u), entities[1]);
-    ASSERT_EQ(*(group.data() + 4u), entities[2]);
-
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 0u)).value, 12);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 1u)).value, 9);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 2u)).value, 6);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 3u)).value, 3);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 4u)).value, 1);
-
-    ASSERT_EQ(group.get<char>(*(group.data() + 0u)), 'e');
-    ASSERT_EQ(group.get<char>(*(group.data() + 1u)), 'd');
-    ASSERT_EQ(group.get<char>(*(group.data() + 2u)), 'c');
-    ASSERT_EQ(group.get<char>(*(group.data() + 3u)), 'b');
-    ASSERT_EQ(group.get<char>(*(group.data() + 4u)), 'a');
-
-    ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{6}, 'c')));
-    ASSERT_EQ((group.get<boxed_int, char>(entities[1])), (std::make_tuple(boxed_int{3}, 'b')));
-    ASSERT_EQ((group.get<boxed_int, char>(entities[2])), (std::make_tuple(boxed_int{1}, 'a')));
-    ASSERT_EQ((group.get<boxed_int, char>(entities[3])), (std::make_tuple(boxed_int{9}, 'd')));
-    ASSERT_EQ((group.get<boxed_int, char>(entities[4])), (std::make_tuple(boxed_int{12}, 'e')));
+    ASSERT_EQ(group.data()[0u], entities[4]);
+    ASSERT_EQ(group.data()[1u], entities[3]);
+    ASSERT_EQ(group.data()[2u], entities[0]);
+    ASSERT_EQ(group.data()[3u], entities[1]);
+    ASSERT_EQ(group.data()[4u], entities[2]);
+    ASSERT_EQ(group.data()[5u], entities[5]);
+    ASSERT_EQ(group.data()[6u], entities[6]);
+
+    ASSERT_EQ(group.raw<boxed_int>()[0u][0u].value, 12);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][1u].value, 9);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][2u].value, 6);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][3u].value, 3);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][4u].value, 1);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][5u].value, 4);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][6u].value, 5);
+
+    ASSERT_EQ(group.get<char>(group.data()[0u]), 'e');
+    ASSERT_EQ(group.get<char>(group.data()[1u]), 'd');
+    ASSERT_EQ(group.get<char>(group.data()[2u]), 'c');
+    ASSERT_EQ(group.get<char>(group.data()[3u]), 'b');
+    ASSERT_EQ(group.get<char>(group.data()[4u]), 'a');
 
     ASSERT_FALSE(group.contains(entities[5]));
     ASSERT_FALSE(group.contains(entities[6]));
@@ -980,15 +988,20 @@ TEST(OwningGroup, SortWithExclusionList) {
         return lhs < rhs;
     });
 
-    ASSERT_EQ(*(group.data() + 0u), entities[4]);
-    ASSERT_EQ(*(group.data() + 1u), entities[3]);
-    ASSERT_EQ(*(group.data() + 2u), entities[1]);
-    ASSERT_EQ(*(group.data() + 3u), entities[0]);
+    ASSERT_EQ(group.data()[0u], entities[4]);
+    ASSERT_EQ(group.data()[1u], entities[3]);
+    ASSERT_EQ(group.data()[2u], entities[1]);
+    ASSERT_EQ(group.data()[3u], entities[0]);
 
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 0u)).value, 4);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 1u)).value, 3);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 2u)).value, 1);
-    ASSERT_EQ(group.get<boxed_int>(*(group.data() + 3u)).value, 0);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][0u].value, 4);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][1u].value, 3);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][2u].value, 1);
+    ASSERT_EQ(group.raw<boxed_int>()[0u][3u].value, 0);
+
+    ASSERT_EQ(group.get<boxed_int>(entities[0]).value, 0);
+    ASSERT_EQ(group.get<boxed_int>(entities[1]).value, 1);
+    ASSERT_EQ(group.get<boxed_int>(entities[3]).value, 3);
+    ASSERT_EQ(group.get<boxed_int>(entities[4]).value, 4);
 
     ASSERT_FALSE(group.contains(entities[2]));
 }
@@ -1049,6 +1062,8 @@ TEST(OwningGroup, ConstNonConstAndAllInBetween) {
     static_assert(std::is_same_v<decltype(group.get<int, const char, double, const float>({})), std::tuple<int &, const char &, double &, const float &>>);
     static_assert(std::is_same_v<decltype(group.get({})), std::tuple<int &, const char &, double &, const float &>>);
     static_assert(std::is_same_v<decltype(group.data()), const entt::entity *>);
+    static_assert(std::is_same_v<decltype(group.raw<const char>()), const char * const *>);
+    static_assert(std::is_same_v<decltype(group.raw<int>()), int **>);
 
     group.each([](auto &&i, auto &&c, auto &&d, auto &&f) {
         static_assert(std::is_same_v<decltype(i), int &>);

+ 1 - 0
test/entt/entity/registry_no_eto.cpp

@@ -14,6 +14,7 @@ TEST(Registry, NoEto) {
     registry.emplace<empty_type>(entity);
     registry.emplace<int>(entity, 42);
 
+    ASSERT_NE(registry.view<empty_type>().raw(), nullptr);
     ASSERT_NE(registry.try_get<empty_type>(entity), nullptr);
     ASSERT_EQ(registry.view<empty_type>().get(entity), std::as_const(registry).view<const empty_type>().get(entity));
 

+ 19 - 3
test/entt/entity/storage.cpp

@@ -418,6 +418,22 @@ TEST(Storage, ConstReverseIterator) {
     ASSERT_GE(cend, pool.crend());
 }
 
+TEST(Storage, Raw) {
+    entt::storage<int> pool;
+
+    pool.emplace(entt::entity{3}, 3);
+    pool.emplace(entt::entity{12}, 6);
+    pool.emplace(entt::entity{42}, 9);
+
+    ASSERT_EQ(pool.get(entt::entity{3}), 3);
+    ASSERT_EQ(std::as_const(pool).get(entt::entity{12}), 6);
+    ASSERT_EQ(pool.get(entt::entity{42}), 9);
+
+    ASSERT_EQ(pool.raw()[0u][0u], 3);
+    ASSERT_EQ(std::as_const(pool).raw()[0u][1u], 6);
+    ASSERT_EQ(pool.raw()[0u][2u], 9);
+}
+
 TEST(Storage, SortOrdered) {
     entt::storage<boxed_int> pool;
     entt::entity entities[5u]{entt::entity{12}, entt::entity{42}, entt::entity{7}, entt::entity{3}, entt::entity{9}};
@@ -480,9 +496,9 @@ TEST(Storage, SortRange) {
 
     pool.sort_n(2u, [](auto lhs, auto rhs) { return lhs.value < rhs.value; });
 
-    ASSERT_EQ(pool.rbegin()[0u], boxed_int{6});
-    ASSERT_EQ(pool.rbegin()[1u], boxed_int{3});
-    ASSERT_EQ(pool.rbegin()[2u], boxed_int{1});
+    ASSERT_EQ(pool.raw()[0u][0u], boxed_int{6});
+    ASSERT_EQ(pool.raw()[0u][1u], boxed_int{3});
+    ASSERT_EQ(pool.raw()[0u][2u], boxed_int{1});
 
     ASSERT_EQ(pool.data()[0u], entt::entity{42});
     ASSERT_EQ(pool.data()[1u], entt::entity{12});

+ 18 - 5
test/entt/entity/view.cpp

@@ -44,8 +44,11 @@ TEST(SingleComponentView, Functionalities) {
         ASSERT_TRUE(cview.get<const char>(entity) == '1' || std::get<const char &>(cview.get(entity)) == '2');
     }
 
-    ASSERT_EQ(*(view.data() + 0), e1);
-    ASSERT_EQ(*(view.data() + 1), e0);
+    ASSERT_EQ(view.data()[0u], e1);
+    ASSERT_EQ(view.data()[1u], e0);
+
+    ASSERT_EQ(view.raw()[0u][0u], '2');
+    ASSERT_EQ(cview.raw()[0u][1u], '1');
 
     registry.erase<char>(e0);
     registry.erase<char>(e1);
@@ -61,7 +64,7 @@ TEST(SingleComponentView, Functionalities) {
     ASSERT_FALSE(invalid);
 }
 
-TEST(SingleComponentView, Data) {
+TEST(SingleComponentView, RawData) {
     entt::registry registry;
     auto view = registry.view<int>();
     auto cview = std::as_const(registry).view<const int>();
@@ -70,6 +73,8 @@ TEST(SingleComponentView, Data) {
 
     ASSERT_EQ(view.size(), 0u);
     ASSERT_EQ(cview.size(), 0u);
+    ASSERT_EQ(view.raw(), nullptr);
+    ASSERT_EQ(cview.raw(), nullptr);
     ASSERT_EQ(view.data(), nullptr);
     ASSERT_EQ(cview.data(), nullptr);
 
@@ -77,8 +82,10 @@ TEST(SingleComponentView, Data) {
 
     ASSERT_NE(view.size(), 0u);
     ASSERT_NE(cview.size(), 0u);
-    ASSERT_EQ(*view.data(), entity);
-    ASSERT_EQ(*cview.data(), entity);
+    ASSERT_EQ(view.raw()[0u][0u], 42);
+    ASSERT_EQ(cview.raw()[0u][0u], 42);
+    ASSERT_EQ(view.data()[0u], entity);
+    ASSERT_EQ(cview.data()[0u], entity);
 
     registry.destroy(entity);
 
@@ -98,6 +105,7 @@ TEST(SingleComponentView, LazyTypeFromConstRegistry) {
     ASSERT_TRUE(cview);
     ASSERT_TRUE(eview);
 
+    ASSERT_NE(cview.raw(), nullptr);
     ASSERT_NE(eview.data(), nullptr);
 
     ASSERT_FALSE(cview.empty());
@@ -218,10 +226,15 @@ TEST(SingleComponentView, ConstNonConstAndAllInBetween) {
     ASSERT_EQ(view.size(), 1u);
     ASSERT_EQ(cview.size(), 1u);
 
+    static_assert(std::is_same_v<decltype(view.raw()), int **>);
+    static_assert(std::is_same_v<decltype(cview.raw()), const int * const *>);
+
     static_assert(std::is_same_v<decltype(view.get<int>({})), int &>);
     static_assert(std::is_same_v<decltype(view.get({})), std::tuple<int &>>);
+    static_assert(std::is_same_v<decltype(view.raw()), int **>);
     static_assert(std::is_same_v<decltype(cview.get<const int>({})), const int &>);
     static_assert(std::is_same_v<decltype(cview.get({})), std::tuple<const int &>>);
+    static_assert(std::is_same_v<decltype(cview.raw()), const int * const *>);
 
     view.each([](auto &&i) {
         static_assert(std::is_same_v<decltype(i), int &>);