Browse Source

entity: added handle_view (close #583)

Michele Caini 5 years ago
parent
commit
22596e0f5b
3 changed files with 83 additions and 22 deletions
  1. 17 1
      src/entt/entity/fwd.hpp
  2. 39 11
      src/entt/entity/handle.hpp
  3. 27 10
      test/entt/entity/handle.cpp

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

@@ -44,7 +44,7 @@ template <typename>
 struct basic_actor;
 
 
-template<typename>
+template<typename, typename...>
 struct basic_handle;
 
 
@@ -96,6 +96,22 @@ using handle = basic_handle<entity>;
 using const_handle = basic_handle<const entity>;
 
 
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Args Other template parameters.
+ */
+template<typename... Args>
+using handle_view = basic_handle<entity, Args...>;
+
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Args Other template parameters.
+ */
+template<typename... Args>
+using const_handle_view = basic_handle<const entity, Args...>;
+
+
 /*! @brief Alias declaration for the most common use case. */
 using snapshot = basic_snapshot<entity>;
 

+ 39 - 11
src/entt/entity/handle.hpp

@@ -2,6 +2,7 @@
 #define ENTT_ENTITY_HANDLE_HPP
 
 
+#include "fwd.hpp"
 #include "registry.hpp"
 
 
@@ -14,8 +15,9 @@ namespace entt {
  * Tiny wrapper around a registry and an entity.
  *
  * @tparam Entity A valid entity type (see entt_traits for more details).
+ * @tparam Type Types to which to restrict the scope of a handle.
  */
-template<typename Entity>
+template<typename Entity, typename... Type>
 struct basic_handle {
     /*! @brief Underlying entity identifier. */
     using entity_type = std::remove_const_t<Entity>;
@@ -38,13 +40,13 @@ struct basic_handle {
 
     /**
      * @brief Compares two handles.
-     * @tparam Type A valid entity type (see entt_traits for more details).
+     * @tparam Args Template parameters of the handle with which to compare.
      * @param other Handle with which to compare.
      * @return True if both handles refer to the same registry and the same
      * entity, false otherwise.
      */
-    template<typename Type>
-    [[nodiscard]] bool operator==(const basic_handle<Type> &other) const ENTT_NOEXCEPT {
+    template<typename... Args>
+    [[nodiscard]] bool operator==(const basic_handle<Args...> &other) const ENTT_NOEXCEPT {
         return reg == other.registry() && entt == other.entity();
     }
 
@@ -53,8 +55,8 @@ struct basic_handle {
      * @return A const handle referring to the same registry and the same
      * entity.
      */
-    [[nodiscard]] operator basic_handle<const entity_type>() const ENTT_NOEXCEPT {
-        return reg ? basic_handle<const entity_type>{*reg, entt} : basic_handle<const entity_type>{};
+    [[nodiscard]] operator basic_handle<const entity_type, Type...>() const ENTT_NOEXCEPT {
+        return reg ? basic_handle<const entity_type, Type...>{*reg, entt} : basic_handle<const entity_type, Type...>{};
     }
 
     /**
@@ -107,6 +109,7 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     decltype(auto) emplace(Args &&... args) const {
+        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>));
         return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
     }
 
@@ -120,6 +123,7 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     decltype(auto) emplace_or_replace(Args &&... args) const {
+        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>));
         return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
     }
 
@@ -133,6 +137,7 @@ struct basic_handle {
      */
     template<typename Component, typename... Func>
     decltype(auto) patch(Func &&... func) const {
+        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>));
         return reg->template patch<Component>(entt, std::forward<Func>(func)...);
     }
 
@@ -146,6 +151,7 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     decltype(auto) replace(Args &&... args) const {
+        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>));
         return reg->template replace<Component>(entt, std::forward<Args>(args)...);
     }
 
@@ -156,7 +162,12 @@ struct basic_handle {
      */
     template<typename... Component>
     void remove() const {
-        reg->template remove<Component...>(entt);
+        if constexpr(sizeof...(Type) == 0 || sizeof...(Component) == 1) {
+            static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component..., Type>));
+            reg->template remove<Component...>(entt);
+        } else {
+            (remove<Component>(), ...);
+        }
     }
 
     /**
@@ -167,7 +178,12 @@ struct basic_handle {
      */
     template<typename... Component>
     decltype(auto) remove_if_exists() const {
-        return reg->template remove_if_exists<Component...>(entt);
+        if constexpr(sizeof...(Type) == 0 || sizeof...(Component) == 1) {
+            static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component..., Type>));
+            return reg->template remove_if_exists<Component...>(entt);
+        } else {
+            return (remove_if_exists<Component>(), ...);
+        }
     }
 
     /**
@@ -175,6 +191,7 @@ struct basic_handle {
      * @sa basic_registry::remove_all
      */
     void remove_all() const {
+        static_assert(sizeof...(Type) == 0);
         reg->remove_all(entt);
     }
 
@@ -209,7 +226,12 @@ struct basic_handle {
      */
     template<typename... Component>
     [[nodiscard]] decltype(auto) get() const {
-        return reg->template get<Component...>(entt);
+        if constexpr(sizeof...(Type) == 0 || sizeof...(Component) == 1) {
+            static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component..., Type>));
+            return reg->template get<Component...>(entt);
+        } else {
+            return std::forward_as_tuple(get<Component>()...);
+        }
     }
 
     /**
@@ -222,6 +244,7 @@ struct basic_handle {
      */
     template<typename Component, typename... Args>
     [[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const {
+        static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>));
         return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
     }
 
@@ -232,8 +255,13 @@ struct basic_handle {
      * @return Pointers to the components owned by the handle.
      */
     template<typename... Component>
-    [[nodiscard]] decltype(auto) try_get() const {
-        return reg->template try_get<Component...>(entt);
+    [[nodiscard]] auto try_get() const {
+        if constexpr(sizeof...(Type) == 0 || sizeof...(Component) == 1) {
+            static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component..., Type>));
+            return reg->template try_get<Component...>(entt);
+        } else {
+            return std::make_tuple(this->try_get<Component>()...);
+        }
     }
 
     /**

+ 27 - 10
test/entt/entity/handle.cpp

@@ -113,39 +113,56 @@ TEST(BasicHandle, Comparison) {
 TEST(BasicHandle, Component) {
     entt::registry registry;
     const auto entity = registry.create();
-    entt::handle handle{registry, entity};
+    entt::handle_view<int, char, double> handle{registry, entity};
 
     ASSERT_EQ(3, handle.emplace<int>(3));
     ASSERT_EQ('c', handle.emplace_or_replace<char>('c'));
+    ASSERT_EQ(.3, handle.emplace_or_replace<double>(.3));
 
     const auto &patched = handle.patch<int>([](auto &comp) { comp = 42; });
 
     ASSERT_EQ(42, patched);
     ASSERT_EQ('a', handle.replace<char>('a'));
-    ASSERT_TRUE((handle.has<int, char>()));
+    ASSERT_TRUE((handle.has<int, char, double>()));
+    ASSERT_EQ((std::make_tuple(42, 'a', .3)), (handle.get<int, char, double>()));
 
-    handle.remove<char>();
+    handle.remove<char, double>();
 
-    ASSERT_TRUE(registry.empty<char>());
-    ASSERT_EQ(0u, handle.remove_if_exists<char>());
+    ASSERT_TRUE((registry.empty<char, double>()));
+    ASSERT_EQ(0u, (handle.remove_if_exists<char, double>()));
 
     handle.visit([](auto info) { ASSERT_EQ(entt::type_hash<int>::value(), info.hash()); });
 
-    ASSERT_TRUE((handle.any<int, char>()));
-    ASSERT_FALSE((handle.has<int, char>()));
+    ASSERT_TRUE((handle.any<int, char, double>()));
+    ASSERT_FALSE((handle.has<int, char, double>()));
     ASSERT_FALSE(handle.orphan());
 
-    handle.remove_all();
+    handle.remove<int>();
 
     ASSERT_TRUE(registry.empty<int>());
     ASSERT_TRUE(handle.orphan());
-
+    
     ASSERT_EQ(42, handle.get_or_emplace<int>(42));
     ASSERT_EQ(42, handle.get_or_emplace<int>(1));
     ASSERT_EQ(42, handle.get<int>());
-
+    
     ASSERT_EQ(42, *handle.try_get<int>());
     ASSERT_EQ(nullptr, handle.try_get<char>());
+    ASSERT_EQ(nullptr, std::get<1>(handle.try_get<int, char, double>()));
+}
+
+TEST(BasicHandle, RemoveAll) {
+    entt::registry registry;
+    const auto entity = registry.create();
+    entt::handle handle{registry, entity};
+
+    ASSERT_EQ(3, handle.emplace<int>(3));
+    ASSERT_EQ('c', handle.emplace_or_replace<char>('c'));
+    ASSERT_TRUE((handle.has<int, char>()));
+
+    handle.remove_all();
+
+    ASSERT_FALSE((handle.any<int, char>()));
 }
 
 TEST(BasicHandle, FromEntity) {