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

sigh mixin: automatic signal registration for components (#1168)

Terens 1 год назад
Родитель
Сommit
42a1905690
2 измененных файлов с 113 добавлено и 1 удалено
  1. 51 1
      src/entt/entity/mixin.hpp
  2. 62 0
      test/entt/entity/storage_signals.cpp

+ 51 - 1
src/entt/entity/mixin.hpp

@@ -11,6 +11,42 @@
 
 namespace entt {
 
+/*! @cond TURN_OFF_DOXYGEN */
+namespace internal {
+
+template<typename T, typename Reg, typename = void>
+struct has_on_construct final: std::false_type {};
+
+template<typename T, typename Reg>
+struct has_on_construct<
+    T, Reg,
+    std::void_t<decltype(T::on_construct(std::declval<Reg &>(),
+                                         std::declval<Reg &>().create()))>>
+    : std::true_type {};
+
+template<typename T, typename Reg, typename = void>
+struct has_on_update final: std::false_type {};
+
+template<typename T, typename Reg>
+struct has_on_update<
+    T, Reg,
+    std::void_t<decltype(T::on_update(std::declval<Reg &>(),
+                                      std::declval<Reg &>().create()))>>
+    : std::true_type {};
+
+template<typename T, typename Reg, typename = void>
+struct has_on_destroy final: std::false_type {};
+
+template<typename T, typename Reg>
+struct has_on_destroy<
+    T, Reg,
+    std::void_t<decltype(T::on_destroy(std::declval<Reg &>(),
+                                       std::declval<Reg &>().create()))>>
+    : std::true_type {};
+
+} // namespace internal
+/*! @endcond */
+
 /**
  * @brief Mixin type used to add signal support to storage types.
  *
@@ -108,7 +144,21 @@ public:
           owner{},
           construction{allocator},
           destruction{allocator},
-          update{allocator} {}
+          update{allocator} {
+        using element_type = typename Type::element_type;
+
+        if constexpr(internal::has_on_construct<element_type, Registry>::value) {
+            entt::sink{construction}.template connect<&element_type::on_construct>();
+        }
+
+        if constexpr(internal::has_on_update<element_type, Registry>::value) {
+            entt::sink{update}.template connect<&element_type::on_update>();
+        }
+
+        if constexpr(internal::has_on_destroy<element_type, Registry>::value) {
+            entt::sink{destruction}.template connect<&element_type::on_destroy>();
+        }
+    }
 
     /*! @brief Default copy constructor, deleted on purpose. */
     basic_sigh_mixin(const basic_sigh_mixin &) = delete;

+ 62 - 0
test/entt/entity/storage_signals.cpp

@@ -0,0 +1,62 @@
+
+#include <gtest/gtest.h>
+#include <entt/entity/registry.hpp>
+
+struct count_tracker final {
+    inline static void on_construct(entt::registry &, entt::entity) {
+        ++created;
+    }
+
+    inline static void on_update(entt::registry &, entt::entity) {
+        ++updated;
+    }
+
+    inline static void on_destroy(entt::registry &, entt::entity) {
+        ++destroyed;
+    }
+
+    inline static std::size_t created = 0;
+    inline static std::size_t updated = 0;
+    inline static std::size_t destroyed = 0;
+
+    inline static std::size_t alive() {
+        return created - destroyed;
+    }
+};
+
+template<typename Type>
+struct StorageSignals: testing::Test {
+    using type = Type;
+};
+
+using StorageSignalsTypes = ::testing::Types<count_tracker>;
+
+TYPED_TEST_SUITE(StorageSignals, StorageSignalsTypes, );
+
+TYPED_TEST(StorageSignals, AutoSignals) {
+    using value_type = typename TestFixture::type;
+
+    entt::registry registry;
+    auto const id = registry.create();
+
+    registry.emplace<count_tracker>(id);
+
+    ASSERT_EQ(count_tracker::created, 1);
+    ASSERT_EQ(count_tracker::updated, 0);
+    ASSERT_EQ(count_tracker::destroyed, 0);
+    ASSERT_EQ(count_tracker::alive(), 1);
+
+    registry.replace<count_tracker>(id);
+
+    ASSERT_EQ(count_tracker::created, 1);
+    ASSERT_EQ(count_tracker::updated, 1);
+    ASSERT_EQ(count_tracker::destroyed, 0);
+    ASSERT_EQ(count_tracker::alive(), 1);
+
+    registry.remove<count_tracker>(id);
+
+    ASSERT_EQ(count_tracker::created, 1);
+    ASSERT_EQ(count_tracker::updated, 1);
+    ASSERT_EQ(count_tracker::destroyed, 1);
+    ASSERT_EQ(count_tracker::alive(), 0);
+}