diff --git a/src/entt/entity/mixin.hpp b/src/entt/entity/mixin.hpp index 515f0a2cb..5180ec9cd 100644 --- a/src/entt/entity/mixin.hpp +++ b/src/entt/entity/mixin.hpp @@ -11,6 +11,42 @@ namespace entt { +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_on_construct final: std::false_type {}; + +template +struct has_on_construct< + T, Reg, + std::void_t(), + std::declval().create()))>> + : std::true_type {}; + +template +struct has_on_update final: std::false_type {}; + +template +struct has_on_update< + T, Reg, + std::void_t(), + std::declval().create()))>> + : std::true_type {}; + +template +struct has_on_destroy final: std::false_type {}; + +template +struct has_on_destroy< + T, Reg, + std::void_t(), + std::declval().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::value) { + entt::sink{construction}.template connect<&element_type::on_construct>(); + } + + if constexpr(internal::has_on_update::value) { + entt::sink{update}.template connect<&element_type::on_update>(); + } + + if constexpr(internal::has_on_destroy::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; diff --git a/test/entt/entity/storage_signals.cpp b/test/entt/entity/storage_signals.cpp new file mode 100644 index 000000000..7cf4a3948 --- /dev/null +++ b/test/entt/entity/storage_signals.cpp @@ -0,0 +1,62 @@ + +#include +#include + +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 +struct StorageSignals: testing::Test { + using type = Type; +}; + +using StorageSignalsTypes = ::testing::Types; + +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(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(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(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); +}