Michele Caini 5 лет назад
Родитель
Сommit
c55c1c0615
6 измененных файлов с 491 добавлено и 17 удалено
  1. 3 1
      TODO
  2. 0 16
      src/entt/entity/view.hpp
  3. 332 0
      src/entt/entity/view_pack.hpp
  4. 1 0
      src/entt/entt.hpp
  5. 1 0
      test/CMakeLists.txt
  6. 154 0
      test/entt/entity/view_pack.cpp

+ 3 - 1
TODO

@@ -10,7 +10,9 @@
 
 
 WIP:
 WIP:
 * HP: write documentation for custom storages and views!!
 * HP: write documentation for custom storages and views!!
-* use extended get to remove is_eto_eligible checks from views and groups.
+* view pack: doc and plain function as an alias for operator|
+* reverse iterators, rbegin and rend for the view pack
+* use extended get to remove is_eto_eligible checks from views and groups
 * factory invoke: add support for external member functions as static meta functions
 * factory invoke: add support for external member functions as static meta functions
 * pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
 * pagination doesn't work nicely across boundaries probably, give it a look. RO operations are fine, adding components maybe not.
 * make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use
 * make it easier to hook into the type system and describe how to do that to eg auto-generate meta types on first use

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

@@ -349,10 +349,6 @@ public:
      * The returned iterator points to the first entity of the view. If the view
      * The returned iterator points to the first entity of the view. If the view
      * is empty, the returned iterator will be equal to `end()`.
      * is empty, the returned iterator will be equal to `end()`.
      *
      *
-     * @note
-     * Iterators stay true to the order imposed to the underlying data
-     * structures.
-     *
      * @return An iterator to the first entity of the view.
      * @return An iterator to the first entity of the view.
      */
      */
     [[nodiscard]] iterator begin() const {
     [[nodiscard]] iterator begin() const {
@@ -366,10 +362,6 @@ public:
      * the view. Attempting to dereference the returned iterator results in
      * the view. Attempting to dereference the returned iterator results in
      * undefined behavior.
      * undefined behavior.
      *
      *
-     * @note
-     * Iterators stay true to the order imposed to the underlying data
-     * structures.
-     *
      * @return An iterator to the entity following the last entity of the view.
      * @return An iterator to the entity following the last entity of the view.
      */
      */
     [[nodiscard]] iterator end() const {
     [[nodiscard]] iterator end() const {
@@ -382,10 +374,6 @@ public:
      * The returned iterator points to the first entity of the reversed view. If
      * The returned iterator points to the first entity of the reversed view. If
      * the view is empty, the returned iterator will be equal to `rend()`.
      * the view is empty, the returned iterator will be equal to `rend()`.
      *
      *
-     * @note
-     * Iterators stay true to the order imposed to the underlying data
-     * structures.
-     *
      * @return An iterator to the first entity of the reversed view.
      * @return An iterator to the first entity of the reversed view.
      */
      */
     [[nodiscard]] reverse_iterator rbegin() const {
     [[nodiscard]] reverse_iterator rbegin() const {
@@ -400,10 +388,6 @@ public:
      * the reversed view. Attempting to dereference the returned iterator
      * the reversed view. Attempting to dereference the returned iterator
      * results in undefined behavior.
      * results in undefined behavior.
      *
      *
-     * @note
-     * Iterators stay true to the order imposed to the underlying data
-     * structures.
-     *
      * @return An iterator to the entity following the last entity of the
      * @return An iterator to the entity following the last entity of the
      * reversed view.
      * reversed view.
      */
      */

+ 332 - 0
src/entt/entity/view_pack.hpp

@@ -0,0 +1,332 @@
+#ifndef ENTT_ENTITY_VIEW_PACK_HPP
+#define ENTT_ENTITY_VIEW_PACK_HPP
+
+
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include "fwd.hpp"
+#include "utility.hpp"
+
+
+namespace entt {
+
+
+/**
+ * @brief View pack.
+ *
+ * The view pack allows users to combine multiple views into a single iterable
+ * object, while also giving them full control over which view should lead the
+ * iteration.<br/>
+ * Its intended primary use is for custom storage and views, but it can also be
+ * very convenient in everyday use.
+ *
+ * @tparam View Type of the leading view of the pack.
+ * @tparam Other Types of all other views of the pack.
+ */
+template<typename View, typename... Other>
+class view_pack {
+    using common_entity_type = std::common_type_t<typename View::entity_type, typename Other::entity_type...>;
+
+    class view_pack_iterator {
+        friend class view_pack<View, Other...>;
+
+        using underlying_iterator = typename View::iterator;
+
+        view_pack_iterator(underlying_iterator from, underlying_iterator to, const std::tuple<View, Other...> &ref) ENTT_NOEXCEPT
+            : it{from},
+              last{to},
+              pack{std::get<Other>(ref)...}
+        {
+            if(it != last && !valid()) {
+                ++(*this);
+            }
+        }
+
+        [[nodiscard]] bool valid() const {
+            const auto entity = *it;
+            return (std::get<Other>(pack).contains(entity) && ...);
+        }
+
+    public:
+        using difference_type = typename std::iterator_traits<underlying_iterator>::difference_type;
+        using value_type = decltype(*std::declval<underlying_iterator>());
+        using pointer = void;
+        using reference = value_type;
+        using iterator_category = std::input_iterator_tag;
+
+        view_pack_iterator & operator++() ENTT_NOEXCEPT {
+            while(++it != last && !valid());
+            return *this;
+        }
+
+        view_pack_iterator operator++(int) ENTT_NOEXCEPT {
+            view_pack_iterator orig = *this;
+            return ++(*this), orig;
+        }
+
+        [[nodiscard]] reference operator*() const {
+            return *it;
+        }
+
+        [[nodiscard]] bool operator==(const view_pack_iterator &other) const ENTT_NOEXCEPT {
+            return other.it == it;
+        }
+
+        [[nodiscard]] bool operator!=(const view_pack_iterator &other) const ENTT_NOEXCEPT {
+            return !(*this == other);
+        }
+
+    private:
+        underlying_iterator it;
+        const underlying_iterator last;
+        const std::tuple<Other...> pack;
+    };
+
+    class iterable_view_pack {
+        friend class view_pack<View, Other...>;
+
+        using iterable_view = decltype(std::declval<View>().each());
+
+        class iterable_view_pack_iterator {
+            friend class iterable_view_pack;
+
+            using underlying_iterator = typename iterable_view::iterator;
+
+            iterable_view_pack_iterator(underlying_iterator from, underlying_iterator to, const std::tuple<Other...> &ref) ENTT_NOEXCEPT
+                : it{from},
+                  last{to},
+                  pack{ref}
+            {
+                if(it != last && !valid()) {
+                    ++(*this);
+                }
+            }
+
+            [[nodiscard]] bool valid() const {
+                const auto entity = std::get<0>(*it);
+                return (std::get<Other>(pack).contains(entity) && ...);
+            }
+
+        public:
+            using difference_type = typename std::iterator_traits<underlying_iterator>::difference_type;
+            using value_type = decltype(std::tuple_cat(*std::declval<underlying_iterator>(), std::declval<Other>().get({})...));
+            using pointer = void;
+            using reference = value_type;
+            using iterator_category = std::input_iterator_tag;
+
+            iterable_view_pack_iterator & operator++() ENTT_NOEXCEPT {
+                while(++it != last && !valid());
+                return *this;
+            }
+
+            iterable_view_pack_iterator operator++(int) ENTT_NOEXCEPT {
+                iterable_view_pack_iterator orig = *this;
+                return ++(*this), orig;
+            }
+
+            [[nodiscard]] reference operator*() const {
+                const auto curr = *it;
+                return std::tuple_cat(curr, std::get<Other>(pack).get(std::get<0>(curr))...);
+            }
+
+            [[nodiscard]] bool operator==(const iterable_view_pack_iterator &other) const ENTT_NOEXCEPT {
+                return other.it == it;
+            }
+
+            [[nodiscard]] bool operator!=(const iterable_view_pack_iterator &other) const ENTT_NOEXCEPT {
+                return !(*this == other);
+            }
+
+        private:
+            underlying_iterator it;
+            const underlying_iterator last;
+            const std::tuple<Other...> pack;
+        };
+
+        iterable_view_pack(const std::tuple<View, Other...> &ref)
+            : iterable{std::get<View>(ref).each()},
+              pack{std::get<Other>(ref)...}
+        {}
+
+    public:
+        using iterator = iterable_view_pack_iterator;
+
+        [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+            return { iterable.begin(), iterable.end(), pack };
+        }
+
+        [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+            return { iterable.end(), iterable.end(), pack };
+        }
+
+    private:
+        iterable_view iterable;
+        std::tuple<Other...> pack;
+    };
+
+public:
+    /*! @brief Underlying entity identifier. */
+    using entity_type = common_entity_type;
+    /*! @brief Input iterator type. */
+    using iterator = view_pack_iterator;
+
+    /**
+     * @brief Constructs a pack from a bunch of views.
+     * @param view A reference to the leading view for the pack.
+     * @param other References to the other views to use to construct the pack.
+     */
+    view_pack(const View &view, const Other &... other)
+        : pack{view, other...}
+    {}
+
+    /**
+     * @brief Returns an iterator to the first entity of the pack.
+     *
+     * The returned iterator points to the first entity of the pack. If the pack
+     * is empty, the returned iterator will be equal to `end()`.
+     *
+     * @note
+     * Iterators stay true to the order imposed by the first view of the pack.
+     *
+     * @return An iterator to the first entity of the pack.
+     */
+    [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
+        return { std::get<View>(pack).begin(), std::get<View>(pack).end(), pack };
+    }
+
+    /**
+     * @brief Returns an iterator that is past the last entity of the pack.
+     *
+     * The returned iterator points to the entity following the last entity of
+     * the pack. Attempting to dereference the returned iterator results in
+     * undefined behavior.
+     *
+     * @note
+     * Iterators stay true to the order imposed by the first view of the pack.
+     *
+     * @return An iterator to the entity following the last entity of the pack.
+     */
+    [[nodiscard]] iterator end() const ENTT_NOEXCEPT {
+        return { std::get<View>(pack).end(), std::get<View>(pack).end(), pack };
+    }
+
+    /**
+     * @brief Iterates entities and components and applies the given function
+     * object to them.
+     *
+     * The function object is invoked for each entity. It is provided with the
+     * entity itself and a set of references to non-empty components. The
+     * _constness_ of the components is as requested.<br/>
+     * The signature of the function must be equivalent to one of the following
+     * forms:
+     *
+     * @code{.cpp}
+     * void(const entity_type, Type &...);
+     * void(Type &...);
+     * @endcode
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @tparam Func Type of the function object to invoke.
+     * @param func A valid function object.
+     */
+    template<typename Func>
+    void each(Func func) const {
+        for(auto value: std::get<View>(pack).each()) {
+            const auto entity = std::get<0>(value);
+
+            if((std::get<Other>(pack).contains(entity) && ...)) {
+                std::apply(func, std::tuple_cat(value, std::get<Other>(pack).get(entity)...));
+            }
+        }
+    }
+
+    /**
+     * @brief Returns an iterable object to use to _visit_ the pack.
+     *
+     * The iterable object returns tuples that contain the current entity and a
+     * set of references to its non-empty components. The _constness_ of the
+     * components is as requested.
+     *
+     * @note
+     * Empty types aren't explicitly instantiated and therefore they are never
+     * returned during iterations.
+     *
+     * @return An iterable object to use to _visit_ the pack.
+     */
+    [[nodiscard]] iterable_view_pack each() const ENTT_NOEXCEPT {
+        return pack;
+    }
+
+    /**
+     * @brief Returns a copy of the requested view from a pack.
+     * @tparam Type Type of the view to return.
+     * @return A copy of the requested view from the pack.
+     */
+    template<typename Type>
+    [[nodiscard]] operator Type() const ENTT_NOEXCEPT {
+        return std::get<Type>(pack);
+    }
+
+    /**
+     * @brief Appends a view to a pack.
+     * @tparam Args View template arguments.
+     * @param view A reference to a view to append to the pack.
+     * @return The extended pack.
+     */
+    template<typename... Args>
+    [[nodiscard]] auto operator|(const basic_view<Args...> &view) const {
+        return std::make_from_tuple<view_pack<View, Other..., basic_view<Args...>>>(std::tuple_cat(pack, std::make_tuple(view)));
+    }
+
+    /**
+     * @brief Appends a pack and therefore all its views to another pack.
+     * @tparam Pack Types of views of the pack to append.
+     * @param other A reference to the pack to append.
+     * @return The extended pack.
+     */
+    template<typename... Pack>
+    [[nodiscard]] auto operator|(const view_pack<Pack...> &other) const {
+        return std::make_from_tuple<view_pack<View, Other..., Pack...>>(std::tuple_cat(pack, std::make_tuple(static_cast<Pack>(other)...)));
+    }
+
+private:
+    std::tuple<View, Other...> pack;
+};
+
+
+/**
+ * @brief Combines two views in a pack.
+ * @tparam Args Template arguments of the first view.
+ * @tparam Other Template arguments of the second view.
+ * @param lhs A reference to the first view with which to create the pack.
+ * @param rhs A reference to the second view with which to create the pack.
+ * @return A pack that combines the two views in a single iterable object.
+ */
+template<typename... Args, typename... Other>
+[[nodiscard]] auto operator|(const basic_view<Args...> &lhs, const basic_view<Other...> &rhs) {
+    return view_pack{lhs, rhs};
+}
+
+
+/**
+ * @brief Combines a view with a pack.
+ * @tparam Args View template arguments.
+ * @tparam Pack Types of views of the pack.
+ * @param view A reference to the view to combine with the pack.
+ * @param pack A reference to the pack to combine with the view.
+ * @return The extended pack.
+ */
+template<typename... Args, typename... Pack>
+[[nodiscard]] auto operator|(const basic_view<Args...> &view, const view_pack<Pack...> &pack) {
+    return view_pack{view} | pack;
+}
+
+
+}
+
+
+#endif

+ 1 - 0
src/entt/entt.hpp

@@ -21,6 +21,7 @@
 #include "entity/storage.hpp"
 #include "entity/storage.hpp"
 #include "entity/utility.hpp"
 #include "entity/utility.hpp"
 #include "entity/view.hpp"
 #include "entity/view.hpp"
+#include "entity/view_pack.hpp"
 #include "locator/locator.hpp"
 #include "locator/locator.hpp"
 #include "meta/container.hpp"
 #include "meta/container.hpp"
 #include "meta/ctx.hpp"
 #include "meta/ctx.hpp"

+ 1 - 0
test/CMakeLists.txt

@@ -174,6 +174,7 @@ SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
 SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
 SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
 SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
 SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
 SETUP_BASIC_TEST(view entt/entity/view.cpp)
 SETUP_BASIC_TEST(view entt/entity/view.cpp)
+SETUP_BASIC_TEST(view_pack entt/entity/view_pack.cpp)
 
 
 # Test locator
 # Test locator
 
 

+ 154 - 0
test/entt/entity/view_pack.cpp

@@ -0,0 +1,154 @@
+#include <cstdint>
+#include <iterator>
+#include <type_traits>
+#include <gtest/gtest.h>
+#include <entt/entity/registry.hpp>
+#include <entt/entity/view.hpp>
+#include <entt/entity/view_pack.hpp>
+
+struct empty_type {};
+
+TEST(ViewPack, Construction) {
+    entt::registry registry;
+
+    const auto view1 = registry.view<int, const char>();
+    const auto view2 = registry.view<empty_type>();
+    const auto view3 = registry.view<double>();
+
+    static_assert(std::is_same_v<decltype(entt::view_pack{view1}), entt::view_pack<entt::view<entt::exclude_t<>, int, const char>>>);
+    static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(view1 | view2 | view3)>);
+
+    static_assert(std::is_same_v<
+        decltype(entt::view_pack{view1, view2, view3}),
+        entt::view_pack<
+            entt::view<entt::exclude_t<>, int, const char>,
+            entt::view<entt::exclude_t<>, empty_type>,
+            entt::view<entt::exclude_t<>, double>
+        >
+    >);
+
+    static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(entt::view_pack{view1} | view2 | view3)>);
+    static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(view1 | entt::view_pack{view2} | view3)>);
+    static_assert(std::is_same_v<decltype(entt::view_pack{view1, view2, view3}), decltype(view1 | view2 | entt::view_pack{view3})>);
+}
+
+TEST(ViewPack, ShortestPool) {
+    entt::registry registry;
+    entt::entity entities[3];
+
+    registry.create(std::begin(entities), std::end(entities));
+
+    registry.insert<int>(std::begin(entities), std::end(entities));
+    registry.insert<empty_type>(std::begin(entities), std::end(entities));
+    registry.insert<char>(std::rbegin(entities), std::rend(entities) - 1u);
+
+    const auto tmp = registry.view<char>() | registry.view<empty_type>();
+    const auto pack = tmp | registry.view<const int>();
+
+    {
+        std::size_t next{};
+
+        for(const auto entt: pack) {
+            ASSERT_EQ(entt::to_integral(entt), ++next);
+            ASSERT_TRUE((registry.has<int, char>(entt)));
+        }
+    }
+
+    auto it = pack.begin();
+
+    ASSERT_EQ(*it++, entities[1u]);
+    ASSERT_EQ(++it, pack.end());
+    ASSERT_TRUE(it == pack.end());
+    ASSERT_FALSE(it != pack.end());
+
+    pack.each([&registry, next = 0u](const auto entt, auto &&cv, auto &&iv) mutable {
+        static_assert(std::is_same_v<decltype(entt), const entt::entity>);
+        static_assert(std::is_same_v<decltype(cv), char &>);
+        static_assert(std::is_same_v<decltype(iv), const int &>);
+        ASSERT_EQ(entt::to_integral(entt), ++next);
+        ASSERT_EQ(&cv, registry.try_get<char>(entt));
+        ASSERT_EQ(&iv, registry.try_get<int>(entt));
+    });
+
+    auto eit = pack.each().begin();
+
+    ASSERT_EQ(std::get<0>(*eit++), entities[1u]);
+    static_assert(std::is_same_v<decltype(std::get<1>(*eit)), char &>);
+    static_assert(std::is_same_v<decltype(std::get<2>(*eit)), const int &>);
+    ASSERT_EQ(++eit, pack.each().end());
+    ASSERT_TRUE(eit == pack.each().end());
+    ASSERT_FALSE(eit != pack.each().end());
+
+    {
+	    std::size_t next{};
+	    
+	    for(const auto [entt, cv, iv]: pack.each()) {
+	        static_assert(std::is_same_v<decltype(entt), const entt::entity>);
+	        static_assert(std::is_same_v<decltype(cv), char &>);
+	        static_assert(std::is_same_v<decltype(iv), const int &>);
+	        ASSERT_EQ(entt::to_integral(entt), ++next);
+	        ASSERT_EQ(&cv, registry.try_get<char>(entt));
+	        ASSERT_EQ(&iv, registry.try_get<int>(entt));
+	    }
+    }
+}
+
+TEST(ViewPack, LongestPool) {
+    entt::registry registry;
+    entt::entity entities[3];
+
+    registry.create(std::begin(entities), std::end(entities));
+
+    registry.insert<int>(std::begin(entities), std::end(entities));
+    registry.insert<empty_type>(std::begin(entities), std::end(entities));
+    registry.insert<char>(std::rbegin(entities), std::rend(entities) - 1u);
+
+    const auto pack = registry.view<int>() | registry.view<empty_type>() | registry.view<const char>();
+
+    {
+        std::size_t next{2u};
+        
+        for(const auto entt: pack) {
+            ASSERT_EQ(entt::to_integral(entt), next--);
+            ASSERT_TRUE((registry.has<int, char>(entt)));
+        }
+    }
+
+    auto it = pack.begin();
+
+    ASSERT_EQ(*it++, entities[2u]);
+    ASSERT_EQ(++it, pack.end());
+    ASSERT_TRUE(it == pack.end());
+    ASSERT_FALSE(it != pack.end());
+
+    pack.each([&registry, next = 2u](const auto entt, auto &&iv, auto &&cv) mutable {
+        static_assert(std::is_same_v<decltype(entt), const entt::entity>);
+        static_assert(std::is_same_v<decltype(iv), int &>);
+        static_assert(std::is_same_v<decltype(cv), const char &>);
+        ASSERT_EQ(entt::to_integral(entt), next--);
+        ASSERT_EQ(&iv, registry.try_get<int>(entt));
+        ASSERT_EQ(&cv, registry.try_get<char>(entt));
+    });
+
+    auto eit = pack.each().begin();
+
+    ASSERT_EQ(std::get<0>(*eit++), entities[2u]);
+    static_assert(std::is_same_v<decltype(std::get<1>(*eit)), int &>);
+    static_assert(std::is_same_v<decltype(std::get<2>(*eit)), const char &>);
+    ASSERT_EQ(++eit, pack.each().end());
+    ASSERT_TRUE(eit == pack.each().end());
+    ASSERT_FALSE(eit != pack.each().end());
+
+    {
+        std::size_t next{2u};
+        
+        for(const auto [entt, iv, cv]: pack.each()) {
+            static_assert(std::is_same_v<decltype(entt), const entt::entity>);
+            static_assert(std::is_same_v<decltype(iv), int &>);
+            static_assert(std::is_same_v<decltype(cv), const char &>);
+            ASSERT_EQ(entt::to_integral(entt), next--);
+            ASSERT_EQ(&iv, registry.try_get<int>(entt));
+            ASSERT_EQ(&cv, registry.try_get<char>(entt));
+        }
+    }
+}