Browse Source

entity/*: remove the requirement for get_as_tuple from storage classes

Michele Caini 5 years ago
parent
commit
54aa98a752
4 changed files with 78 additions and 106 deletions
  1. 13 13
      src/entt/entity/group.hpp
  2. 47 73
      src/entt/entity/storage.hpp
  3. 18 15
      src/entt/entity/view.hpp
  4. 0 5
      test/entt/entity/storage.cpp

+ 13 - 13
src/entt/entity/group.hpp

@@ -102,7 +102,7 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>> final {
 
             [[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
                 const auto entt = *it;
-                return std::tuple_cat(std::make_tuple(entt), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+                return std::tuple_cat(std::make_tuple(entt), get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
             }
 
             [[nodiscard]] bool operator==(const iterable_group_iterator &other) const ENTT_NOEXCEPT {
@@ -385,11 +385,11 @@ public:
         ENTT_ASSERT(contains(entt));
 
         if constexpr(sizeof...(Component) == 0) {
-            return std::tuple_cat(std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+            return std::tuple_cat(get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
         } else if constexpr(sizeof...(Component) == 1) {
             return (std::get<storage_type<Component> *>(pools)->get(entt), ...);
         } else {
-            return std::tuple_cat(std::get<storage_type<Component> *>(pools)->get_as_tuple(entt)...);
+            return std::tuple_cat(get_as_tuple(*std::get<storage_type<Component> *>(pools), entt)...);
         }
     }
 
@@ -613,7 +613,7 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final
                 return std::tuple_cat(
                     std::make_tuple(*it),
                     std::forward_as_tuple(*std::get<OIt>(owned)...),
-                    std::get<storage_type<Get> *>(get)->get_as_tuple(*it)...
+                    get_as_tuple(*std::get<storage_type<Get> *>(get), *it)...
                 );
             }
 
@@ -639,11 +639,11 @@ class basic_group<Entity, exclude_t<Exclude...>, get_t<Get...>, Owned...> final
     public:
         using iterator = iterable_group_iterator<
             typename basic_sparse_set<Entity>::iterator,
-            type_list_cat_t<std::conditional_t<std::tuple_size_v<decltype(std::declval<storage_type<Owned>>().get_as_tuple({}))> == 0, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>
+            type_list_cat_t<std::conditional_t<std::is_same_v<typename storage_type<Owned>::storage_category, empty_storage_tag>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().end())>>...>
         >;
         using reverse_iterator = iterable_group_iterator<
             typename basic_sparse_set<Entity>::reverse_iterator,
-            type_list_cat_t<std::conditional_t<std::tuple_size_v<decltype(std::declval<storage_type<Owned>>().get_as_tuple({}))> == 0, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().rbegin())>>...>
+            type_list_cat_t<std::conditional_t<std::is_same_v<typename storage_type<Owned>::storage_category, empty_storage_tag>, type_list<>, type_list<decltype(std::declval<storage_type<Owned>>().rbegin())>>...>
         >;
 
         [[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
@@ -912,15 +912,15 @@ public:
         ENTT_ASSERT(contains(entt));
 
         if constexpr(sizeof...(Component) == 0) {
-            auto filter = [entt, index = std::get<0>(pools)->index(entt)](auto *cpool) {
-                if constexpr(std::tuple_size_v<decltype(cpool->get_as_tuple({}))> != 0 && (std::is_same_v<typename std::remove_reference_t<decltype(*cpool)>::value_type, std::remove_const_t<Owned>> || ...)) {
-                    return std::forward_as_tuple(cpool->raw()[index]);
+            auto filter = [entt, index = std::get<0>(pools)->index(entt)]([[maybe_unused]] auto *cpool) {
+                if constexpr(std::is_same_v<typename std::remove_reference_t<decltype(*cpool)>::storage_category, empty_storage_tag>) {
+                    return std::make_tuple();
                 } else {
-                    return cpool->get_as_tuple(entt);
+                    return std::forward_as_tuple(cpool->raw()[index]);
                 }
             };
 
-            return std::tuple_cat(filter(std::get<storage_type<Owned> *>(pools))..., filter(std::get<storage_type<Get> *>(pools))...);
+            return std::tuple_cat(filter(std::get<storage_type<Owned> *>(pools))..., get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
         } else if constexpr(sizeof...(Component) == 1) {
             return (std::get<storage_type<Component> *>(pools)->get(entt), ...);
         } else {
@@ -953,7 +953,7 @@ public:
     template<typename Func>
     void each(Func func) const {
         auto owned = std::tuple_cat([length = *length](auto *cpool) {
-            if constexpr(std::tuple_size_v<decltype(cpool->get_as_tuple({}))> == 0) {
+            if constexpr(std::is_same_v<typename std::remove_reference_t<decltype(*cpool)>::storage_category, empty_storage_tag>) {
                 return std::make_tuple();
             } else {
                 return std::make_tuple(cpool->end() - length);
@@ -961,7 +961,7 @@ public:
         }(std::get<storage_type<Owned> *>(pools))...);
 
         for(const auto entt: *this) {
-            auto args = std::tuple_cat(std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr++...); }, owned), std::get<storage_type<Get> *>(pools)->get_as_tuple(entt)...);
+            auto args = std::tuple_cat(std::apply([](auto &&... curr) { return std::forward_as_tuple(*curr++...); }, owned), get_as_tuple(*std::get<storage_type<Get> *>(pools), entt)...);
 
             if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::make_tuple(entt), get({})))>) {
                 std::apply(func, std::tuple_cat(std::make_tuple(entt), args));

+ 47 - 73
src/entt/entity/storage.hpp

@@ -21,6 +21,12 @@
 namespace entt {
 
 
+/*! @brief Empty storage category tag. */
+struct empty_storage_tag {};
+/*! @brief Dense storage category tag. */
+struct dense_storage_tag: empty_storage_tag {};
+
+
 /**
  * @brief Basic storage implementation.
  *
@@ -186,6 +192,8 @@ public:
     using reverse_iterator = Type *;
     /*! @brief Constant reverse iterator type. */
     using const_reverse_iterator = const Type *;
+    /*! @brief Storage category. */
+    using storage_category = dense_storage_tag;
 
     /**
      * @brief Increases the capacity of a storage.
@@ -341,26 +349,6 @@ public:
         return const_cast<value_type &>(std::as_const(*this).get(entt));
     }
 
-    /**
-    * @brief Returns the object associated with an entity as a tuple suitable
-    * for merging in a multi-type get.
-    *
-    * @warning
-    * Attempting to use an entity that doesn't belong to the storage results in
-    * undefined behavior.
-    *
-    * @param entt A valid entity identifier.
-    * @return The object associated with the entity as a tuple.
-    */
-    [[nodiscard]] std::tuple<const value_type &> get_as_tuple(const entity_type entt) const {
-        return { instances[underlying_type::index(entt)] };
-    }
-
-    /*! @copydoc get_as_tuple */
-    [[nodiscard]] std::tuple<value_type &> get_as_tuple(const entity_type entt) {
-        return { instances[underlying_type::index(entt)] };
-    }
-
     /**
      * @brief Assigns an entity to a storage and constructs its object.
      *
@@ -510,27 +498,8 @@ public:
     using entity_type = Entity;
     /*! @brief Unsigned integer type. */
     using size_type = std::size_t;
-
-    /**
-    * @brief Returns the object associated with an entity as a tuple suitable
-    * for merging in a multi-type get.
-    *
-    * @warning
-    * Attempting to use an entity that doesn't belong to the storage results in
-    * undefined behavior.
-    *
-    * @param entt A valid entity identifier.
-    * @return The object associated with the entity as a tuple.
-    */
-    [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const {
-        ENTT_ASSERT(contains(entt));
-        return {};
-    }
-
-    /*! @copydoc get_as_tuple */
-    [[nodiscard]] std::tuple<> get_as_tuple(const entity_type entt) {
-        return std::as_const(*this).get_as_tuple(entt);
-    }
+    /*! @brief Storage category. */
+    using storage_category = empty_storage_tag;
 
     /**
      * @brief Assigns an entity to a storage and constructs its object.
@@ -579,6 +548,8 @@ struct storage_adapter_mixin: Type {
     using value_type = typename Type::value_type;
     /*! @brief Underlying entity identifier. */
     using entity_type = typename Type::entity_type;
+    /*! @brief Storage category. */
+    using storage_category = typename Type::storage_category;
 
     /**
      * @brief Assigns entities to a storage.
@@ -647,39 +618,13 @@ struct storage_adapter_mixin: Type {
  * @tparam Type The type of the underlying storage.
  */
 template<typename Type>
-class sigh_storage_mixin: public Type {
-    template<typename... Args>
-    auto dispatch_emplace(int, basic_registry<typename Type::entity_type> &owner, const typename Type::entity_type entity, Args &&... args)
-    -> std::enable_if_t<std::is_void_v<decltype(std::declval<Type>().emplace(owner, entity, std::forward<Args>(args)...))>> {
-        Type::emplace(owner, entity, std::forward<Args>(args)...);
-        construction.publish(owner, entity);
-    }
-
-    template<typename... Args>
-    decltype(auto) dispatch_emplace(double, basic_registry<typename Type::entity_type> &owner, const typename Type::entity_type entity, Args &&... args) {
-        Type::emplace(owner, entity, std::forward<Args>(args)...);
-        construction.publish(owner, entity);
-        return this->get(entity);
-    }
-
-    template<typename... Func>
-    auto dispatch_patch(int, basic_registry<typename Type::entity_type> &owner, const typename Type::entity_type entity, Func &&... func)
-    -> decltype(std::declval<Type>().patch(owner, entity, std::forward<Func>(func)...)) {
-        Type::patch(owner, entity, std::forward<Func>(func)...);
-        update.publish(owner, entity);
-        return this->get(entity);
-    }
-
-    template<typename... Func>
-    void dispatch_patch(double, basic_registry<typename Type::entity_type> &owner, const typename Type::entity_type entity, Func &&...) {
-        update.publish(owner, entity);
-    }
-
-public:
+struct sigh_storage_mixin: Type {
     /*! @brief Underlying value type. */
     using value_type = typename Type::value_type;
     /*! @brief Underlying entity identifier. */
     using entity_type = typename Type::entity_type;
+    /*! @brief Storage category. */
+    using storage_category = typename Type::storage_category;
 
     /**
      * @brief Returns a sink object.
@@ -756,7 +701,12 @@ public:
      */
     template<typename... Args>
     decltype(auto) emplace(basic_registry<entity_type> &owner, const entity_type entity, Args &&... args) {
-        return dispatch_emplace(0, owner, entity, std::forward<Args>(args)...);
+        Type::emplace(owner, entity, std::forward<Args>(args)...);
+        construction.publish(owner, entity);
+
+        if constexpr(!std::is_same_v<storage_category, empty_storage_tag>) {
+            return this->get(entity);
+        }
     }
 
     /**
@@ -818,8 +768,14 @@ public:
      * @return A reference to the patched instance.
      */
     template<typename... Func>
-    decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entity, Func &&... func) {
-        return dispatch_patch(0, owner, entity, std::forward<Func>(func)...);
+    decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entity, [[maybe_unused]] Func &&... func) {
+        if constexpr(std::is_same_v<storage_category, empty_storage_tag>) {
+            update.publish(owner, entity);
+        } else {
+            Type::patch(owner, entity, std::forward<Func>(func)...);
+            update.publish(owner, entity);
+            return this->get(entity);
+        }
     }
 
 private:
@@ -858,6 +814,24 @@ struct storage_traits<Entity, const Type> {
 };
 
 
+/**
+ * @brief Gets the element associated with an entity from a storage, if any.
+ * @tparam Type Storage type.
+ * @param container A valid instance of a storage class.
+ * @param entity A valid entity identifier.
+ * @return A possibly empty tuple containing the requested element.
+ */
+template<typename Type>
+[[nodiscard]] auto get_as_tuple([[maybe_unused]] Type &container, [[maybe_unused]] const typename Type::entity_type entity) {
+    if constexpr(std::is_same_v<typename Type::storage_category, empty_storage_tag>) {
+        return std::make_tuple();
+    } else {
+        static_assert(std::is_same_v<typename Type::storage_category, dense_storage_tag>, "Unknown storage category");
+        return std::forward_as_tuple(container.get(entity));
+    }
+}
+
+
 }
 
 

+ 18 - 15
src/entt/entity/view.hpp

@@ -241,18 +241,18 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
         return other;
     }
 
-    template<typename It, typename Pool>
-    [[nodiscard]] auto get_as_tuple([[maybe_unused]] It &it, [[maybe_unused]] Pool *cpool, [[maybe_unused]] const Entity entt) const {
-        if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, typename Pool::value_type>) {
+    template<typename Comp, typename It>
+    [[nodiscard]] auto dispatch_get([[maybe_unused]] It &it, [[maybe_unused]] const Entity entt) const {
+        if constexpr(std::is_same_v<typename std::iterator_traits<It>::value_type, typename storage_type<Comp>::value_type>) {
             return std::forward_as_tuple(*it);
         } else {
-            return cpool->get_as_tuple(entt);
+            return get_as_tuple(*std::get<storage_type<Comp> *>(pools), entt);
         }
     }
 
     template<typename Comp, typename Func>
     void traverse(Func func) const {
-        if constexpr(std::tuple_size_v<decltype(std::declval<storage_type<Comp>>().get_as_tuple({}))> == 0) {
+        if constexpr(std::is_same_v<typename storage_type<Comp>::storage_category, empty_storage_tag>) {
             for(const auto entt: static_cast<const basic_sparse_set<entity_type> &>(*std::get<storage_type<Comp> *>(pools))) {
                 if(((std::is_same_v<Comp, Component> || std::get<storage_type<Component> *>(pools)->contains(entt)) && ...)
                     && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...))
@@ -272,9 +272,9 @@ class basic_view<Entity, exclude_t<Exclude...>, Component...> final {
                     && !(std::get<const storage_type<Exclude> *>(filter)->contains(entt) || ...))
                 {
                     if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::make_tuple(entt), get({})))>) {
-                        std::apply(func, std::tuple_cat(std::make_tuple(entt), get_as_tuple(it, std::get<storage_type<Component> *>(pools), entt)...));
+                        std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Component>(it, entt)...));
                     } else {
-                        std::apply(func, std::tuple_cat(get_as_tuple(it, std::get<storage_type<Component> *>(pools), entt)...));
+                        std::apply(func, std::tuple_cat(dispatch_get<Component>(it, entt)...));
                     }
                 }
 
@@ -422,12 +422,11 @@ public:
         ENTT_ASSERT(contains(entt));
 
         if constexpr(sizeof...(Comp) == 0) {
-            return std::tuple_cat(std::get<storage_type<Component> *>(pools)->get_as_tuple(entt)...);
+            return std::tuple_cat(get_as_tuple(*std::get<storage_type<Component> *>(pools), entt)...);
         } else if constexpr(sizeof...(Comp) == 1) {
             return (std::get<storage_type<Comp> *>(pools)->get(entt), ...);
         } else {
-            return std::tuple_cat(std::get<storage_type<Comp> *>(pools)->get_as_tuple(entt)...);
-        }
+            return std::tuple_cat(get_as_tuple(*std::get<storage_type<Comp> *>(pools), entt)...);        }
     }
 
     /**
@@ -611,12 +610,12 @@ class basic_view<Entity, exclude_t<>, Component> final {
 
     public:
         using iterator = std::conditional_t<
-            std::tuple_size_v<decltype(std::declval<storage_type>().get_as_tuple({}))> == 0,
+            std::is_same_v<typename storage_type::storage_category, empty_storage_tag>,
             iterable_view_iterator<typename basic_sparse_set<Entity>::iterator>,
             iterable_view_iterator<typename basic_sparse_set<Entity>::iterator, decltype(std::declval<storage_type>().begin())>
         >;
         using reverse_iterator = std::conditional_t<
-            std::tuple_size_v<decltype(std::declval<storage_type>().get_as_tuple({}))> == 0,
+            std::is_same_v<typename storage_type::storage_category, empty_storage_tag>,
             iterable_view_iterator<typename basic_sparse_set<Entity>::reverse_iterator>,
             iterable_view_iterator<typename basic_sparse_set<Entity>::reverse_iterator, decltype(std::declval<storage_type>().rbegin())>
         >;
@@ -824,7 +823,11 @@ public:
     template<typename... Comp>
     [[nodiscard]] decltype(auto) get(const entity_type entt) const {
         if constexpr(sizeof...(Comp) == 0) {
-            return pool->get_as_tuple(entt);
+            if constexpr(std::is_same_v<typename storage_type::storage_category, empty_storage_tag>) {
+                return std::make_tuple();
+            } else {
+                return std::forward_as_tuple(pool->get(entt));
+            }
         } else {
             static_assert(std::is_same_v<Comp..., Component>, "Invalid component type");
             return pool->get(entt);
@@ -855,7 +858,7 @@ public:
      */
     template<typename Func>
     void each(Func func) const {
-        if constexpr(std::tuple_size_v<decltype(pool->get_as_tuple({}))> == 0) {
+        if constexpr(std::is_same_v<typename storage_type::storage_category, empty_storage_tag>) {
             if constexpr(std::is_invocable_v<Func>) {
                 for(auto pos = pool->size(); pos; --pos) {
                     func();
@@ -865,7 +868,7 @@ public:
                     func(component);
                 }
             }
-        } else if constexpr(std::is_invocable_v<Func, entity_type, std::add_lvalue_reference_t<Component>>) {
+        } else if constexpr(is_applicable_v<Func, decltype(*each().begin())>) {
             auto raw = pool->begin();
 
             for(const auto entt: *this) {

+ 0 - 5
test/entt/entity/storage.cpp

@@ -46,8 +46,6 @@ TEST(Storage, Functionalities) {
     ASSERT_FALSE(pool.contains(entt::entity{0}));
     ASSERT_TRUE(pool.contains(entt::entity{41}));
     ASSERT_EQ(pool.get(entt::entity{41}), 3);
-    ASSERT_EQ(pool.get_as_tuple(entt::entity{41}), std::make_tuple(3));
-    ASSERT_EQ(std::as_const(pool).get_as_tuple(entt::entity{41}), std::make_tuple(3));
 
     pool.remove(entt::entity{41});
 
@@ -61,8 +59,6 @@ TEST(Storage, Functionalities) {
     pool.emplace(entt::entity{41}, 12);
 
     ASSERT_EQ(pool.get(entt::entity{41}), 12);
-    ASSERT_EQ(pool.get_as_tuple(entt::entity{41}), std::make_tuple(12));
-    ASSERT_EQ(std::as_const(pool).get_as_tuple(entt::entity{41}), std::make_tuple(12));
 
     pool.clear();
 
@@ -89,7 +85,6 @@ TEST(Storage, EmptyType) {
     pool.emplace(entt::entity{99});
 
     ASSERT_TRUE(pool.contains(entt::entity{99}));
-    ASSERT_EQ(pool.get_as_tuple(entt::entity{99}), std::make_tuple());
 }
 
 TEST(Storage, Insert) {