Browse Source

issue #31: multi component get

Michele Caini 8 years ago
parent
commit
6040f8f263
5 changed files with 179 additions and 20 deletions
  1. 26 15
      README.md
  2. 44 2
      src/entt/entity/registry.hpp
  3. 93 0
      src/entt/entity/view.hpp
  4. 4 0
      test/entt/entity/registry.cpp
  5. 12 3
      test/entt/entity/view.cpp

+ 26 - 15
README.md

@@ -377,7 +377,7 @@ registry.assign<Position>(entity, 0., 0.);
 
 // ...
 
-auto &velocity = registry.assign<Velocity>(entity);
+Velocity &velocity = registry.assign<Velocity>(entity);
 velocity.dx = 0.;
 velocity.dy = 0.;
 ```
@@ -390,7 +390,7 @@ registry.replace<Position>(entity, 0., 0.);
 
 // ...
 
-auto &velocity = registry.replace<Velocity>(entity);
+Velocity &velocity = registry.replace<Velocity>(entity);
 velocity.dx = 0.;
 velocity.dy = 0.;
 ```
@@ -405,7 +405,7 @@ registry.accomodate<Position>(entity, 0., 0.);
 
 // ...
 
-auto &velocity = registry.accomodate<Velocity>(entity);
+Velocity &velocity = registry.accomodate<Velocity>(entity);
 velocity.dx = 0.;
 velocity.dy = 0.;
 ```
@@ -464,13 +464,16 @@ their components are destroyed:
 Finally, references to components can be retrieved simply by doing this:
 
 ```cpp
-// either a non-const reference ...
 entt::DefaultRegistry registry;
-auto &position = registry.get<Position>(entity);
-
-// ... or a const one
 const auto &cregistry = registry;
-const auto &position = cregistry.get<Position>(entity);
+
+// const and non-const reference
+const Position &position = cregistry.get<Position>(entity);
+Position &position = registry.get<Position>(entity);
+
+// const and non-const references
+std::tuple<const Position &, const Velocity &> tup = cregistry.get<Position, Velocity>(entity);
+std::tuple<Position &, Velocity &> tup = registry.get<Position, Velocity>(entity);
 ```
 
 The `get` member function template gives direct access to the component of an
@@ -516,11 +519,11 @@ References to tags can be retrieved simply by doing this:
 ```cpp
 // either a non-const reference ...
 entt::DefaultRegistry registry;
-auto &player = registry.get<PlayingCharacter>();
+PlayingCharacter &player = registry.get<PlayingCharacter>();
 
 // ... or a const one
 const auto &cregistry = registry;
-const auto &camera = cregistry.get<Camera>();
+const Camera &camera = cregistry.get<Camera>();
 ```
 
 The `get` member function template gives direct access to the tag as stored in
@@ -705,7 +708,7 @@ To iterate a single component standard view, either use it in range-for loop:
 auto view = registry.view<Renderable>();
 
 for(auto entity: view) {
-    auto &renderable = view.get(entity);
+    Renderable &renderable = view.get(entity);
 
     // ...
 }
@@ -749,8 +752,12 @@ To iterate a multi component standard view, either use it in range-for loop:
 auto view = registry.view<Position, Velocity>();
 
 for(auto entity: view) {
-    auto &position = view.get<Position>(entity);
-    auto &velocity = view.get<Velocity>(entity);
+    // a component at a time ...
+    Position &position = view.get<Position>(entity);
+    Velocity &velocity = view.get<Velocity>(entity);
+
+    // ... or multiple components at once
+    std::tuple<Position &, Velocity &> tup = view.get<Position, Velocity>(entity);
 
     // ...
 }
@@ -817,8 +824,12 @@ To iterate a persistent view, either use it in range-for loop:
 auto view = registry.persistent<Position, Velocity>();
 
 for(auto entity: view) {
-    auto &position = view.get<Position>(entity);
-    auto &velocity = view.get<Velocity>(entity);
+    // a component at a time ...
+    Position &position = view.get<Position>(entity);
+    Velocity &velocity = view.get<Velocity>(entity);
+
+    // ... or multiple components at once
+    std::tuple<Position &, Velocity &> tup = view.get<Position, Velocity>(entity);
 
     // ...
 }

+ 44 - 2
src/entt/entity/registry.hpp

@@ -2,6 +2,7 @@
 #define ENTT_ENTITY_REGISTRY_HPP
 
 
+#include <tuple>
 #include <vector>
 #include <memory>
 #include <utility>
@@ -9,6 +10,7 @@
 #include <cstdint>
 #include <cassert>
 #include <algorithm>
+#include <type_traits>
 #include "../core/family.hpp"
 #include "sparse_set.hpp"
 #include "traits.hpp"
@@ -644,7 +646,7 @@ public:
      *
      * @tparam Component Type of component to get.
      * @param entity A valid entity identifier.
-     * @return A reference to the instance of the component owned by the entity.
+     * @return A reference to the component owned by the entity.
      */
     template<typename Component>
     const Component & get(entity_type entity) const noexcept {
@@ -664,13 +666,53 @@ public:
      *
      * @tparam Component Type of component to get.
      * @param entity A valid entity identifier.
-     * @return A reference to the instance of the component owned by the entity.
+     * @return A reference to the component owned by the entity.
      */
     template<typename Component>
     Component & get(entity_type entity) noexcept {
         return const_cast<Component &>(const_cast<const Registry *>(this)->get<Component>(entity));
     }
 
+    /**
+     * @brief Returns a reference to the given components for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to get components from an entity
+     * that doesn't own them results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode in case of
+     * invalid entity or if the entity doesn't own instances of the given
+     * components.
+     *
+     * @tparam Component Type of components to get.
+     * @param entity A valid entity identifier.
+     * @return References to the components owned by the entity.
+     */
+    template<typename... Component>
+    std::enable_if_t<(sizeof...(Component) > 1), std::tuple<const Component &...>>
+    get(entity_type entity) const noexcept {
+        return { get<Component>(entity)... };
+    }
+
+    /**
+     * @brief Returns a reference to the given components for an entity.
+     *
+     * @warning
+     * Attempting to use an invalid entity or to get components from an entity
+     * that doesn't own them results in undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode in case of
+     * invalid entity or if the entity doesn't own instances of the given
+     * components.
+     *
+     * @tparam Component Type of components to get.
+     * @param entity A valid entity identifier.
+     * @return References to the components owned by the entity.
+     */
+    template<typename... Component>
+    std::enable_if_t<(sizeof...(Component) > 1), std::tuple<Component &...>>
+    get(entity_type entity) noexcept {
+        return { get<Component>(entity)... };
+    }
+
     /**
      * @brief Replaces the given component for an entity.
      *

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

@@ -5,6 +5,7 @@
 #include <tuple>
 #include <utility>
 #include <algorithm>
+#include <type_traits>
 #include "sparse_set.hpp"
 
 
@@ -188,6 +189,52 @@ public:
         return const_cast<Comp &>(const_cast<const PersistentView *>(this)->get<Comp>(entity));
     }
 
+    /**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `Registry::get` during iterations. It has
+     * far better performance than its companion function.
+     *
+     * @warning
+     * Attempting to use invalid component types results in a compilation error.
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if
+     * the view doesn't contain the given entity.
+     *
+     * @tparam Comp Types of the components to get.
+     * @param entity A valid entity identifier.
+     * @return The components assigned to the entity.
+     */
+    template<typename... Comp>
+    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
+    get(entity_type entity) const noexcept {
+        return { get<Comp>(entity)... };
+    }
+
+    /**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `Registry::get` during iterations. It has
+     * far better performance than its companion function.
+     *
+     * @warning
+     * Attempting to use invalid component types results in a compilation error.
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if
+     * the view doesn't contain the given entity.
+     *
+     * @tparam Comp Types of the components to get.
+     * @param entity A valid entity identifier.
+     * @return The components assigned to the entity.
+     */
+    template<typename... Comp>
+    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
+    get(entity_type entity) noexcept {
+        return { get<Comp>(entity)... };
+    }
+
     /**
      * @brief Iterate the entities and applies them the given function object.
      *
@@ -457,6 +504,52 @@ public:
         return const_cast<Comp &>(const_cast<const View *>(this)->get<Comp>(entity));
     }
 
+    /**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `Registry::get` during iterations. It has
+     * far better performance than its companion function.
+     *
+     * @warning
+     * Attempting to use invalid component types results in a compilation error.
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if
+     * the view doesn't contain the given entity.
+     *
+     * @tparam Comp Types of the components to get.
+     * @param entity A valid entity identifier.
+     * @return The components assigned to the entity.
+     */
+    template<typename... Comp>
+    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<const Comp &...>>
+    get(entity_type entity) const noexcept {
+        return { get<Comp>(entity)... };
+    }
+
+    /**
+     * @brief Returns the components assigned to the given entity.
+     *
+     * Prefer this function instead of `Registry::get` during iterations. It has
+     * far better performance than its companion function.
+     *
+     * @warning
+     * Attempting to use invalid component types results in a compilation error.
+     * Attempting to use an entity that doesn't belong to the view results in
+     * undefined behavior.<br/>
+     * An assertion will abort the execution at runtime in debug mode if
+     * the view doesn't contain the given entity.
+     *
+     * @tparam Comp Types of the components to get.
+     * @param entity A valid entity identifier.
+     * @return The components assigned to the entity.
+     */
+    template<typename... Comp>
+    std::enable_if_t<(sizeof...(Comp) > 1), std::tuple<Comp &...>>
+    get(entity_type entity) noexcept {
+        return { get<Comp>(entity)... };
+    }
+
     /**
      * @brief Iterate the entities and applies them the given function object.
      *

+ 4 - 0
test/entt/entity/registry.cpp

@@ -57,6 +57,10 @@ TEST(DefaultRegistry, Functionalities) {
     ASSERT_TRUE(registry.has<char>(e3));
     ASSERT_EQ(registry.get<int>(e1), 42);
     ASSERT_EQ(registry.get<char>(e1), 'c');
+
+    ASSERT_EQ(std::get<0>(registry.get<int, char>(e1)), 42);
+    ASSERT_EQ(std::get<1>(static_cast<const entt::DefaultRegistry &>(registry).get<int, char>(e1)), 'c');
+
     ASSERT_EQ(registry.get<int>(e1), registry.get<int>(e3));
     ASSERT_EQ(registry.get<char>(e1), registry.get<char>(e3));
     ASSERT_NE(&registry.get<int>(e1), &registry.get<int>(e3));

+ 12 - 3
test/entt/entity/view.cpp

@@ -90,10 +90,13 @@ TEST(View, MultipleComponent) {
 
     view.get<char>(e1) = '1';
     view.get<char>(e2) = '2';
+    view.get<int>(e2) = 42;
 
     for(auto entity: view) {
         const auto &cview = static_cast<const decltype(view) &>(view);
-        ASSERT_TRUE(cview.get<char>(entity) == '2');
+        ASSERT_EQ(std::get<0>(cview.get<int, char>(entity)), 42);
+        ASSERT_EQ(std::get<1>(view.get<int, char>(entity)), '2');
+        ASSERT_EQ(cview.get<char>(entity), '2');
     }
 
     registry.remove<char>(e1);
@@ -161,10 +164,13 @@ TEST(PersistentView, Prepare) {
 
     view.get<char>(e1) = '1';
     view.get<char>(e2) = '2';
+    view.get<int>(e2) = 42;
 
     for(auto entity: view) {
         const auto &cview = static_cast<const decltype(view) &>(view);
-        ASSERT_TRUE(cview.get<char>(entity) == '2');
+        ASSERT_EQ(std::get<0>(cview.get<int, char>(entity)), 42);
+        ASSERT_EQ(std::get<1>(view.get<int, char>(entity)), '2');
+        ASSERT_EQ(cview.get<char>(entity), '2');
     }
 
     ASSERT_EQ(*(view.data() + 0), e2);
@@ -199,10 +205,13 @@ TEST(PersistentView, NoPrepare) {
 
     view.get<char>(e1) = '1';
     view.get<char>(e2) = '2';
+    view.get<int>(e2) = 42;
 
     for(auto entity: view) {
         const auto &cview = static_cast<const decltype(view) &>(view);
-        ASSERT_TRUE(cview.get<char>(entity) == '2');
+        ASSERT_EQ(std::get<0>(cview.get<int, char>(entity)), 42);
+        ASSERT_EQ(std::get<1>(view.get<int, char>(entity)), '2');
+        ASSERT_EQ(cview.get<char>(entity), '2');
     }
 
     ASSERT_EQ(*(view.data() + 0), e2);