From 3c5e77e4dd9f77c55909562bf8d86d25237e1e13 Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Thu, 16 Jan 2025 15:28:04 +0100 Subject: [PATCH] sigh: support uninitialized sink objects --- src/entt/signal/sigh.hpp | 33 ++++++++++++++++++++++++++------- test/entt/signal/sigh.cpp | 20 ++++++++++++++++++++ 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/entt/signal/sigh.hpp b/src/entt/signal/sigh.hpp index c0fc8e69a..b5df739f7 100644 --- a/src/entt/signal/sigh.hpp +++ b/src/entt/signal/sigh.hpp @@ -374,15 +374,26 @@ class sink> { template void disconnect_if(Func callback) { - for(auto pos = signal->calls.size(); pos; --pos) { - if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { - elem = std::move(signal->calls.back()); - signal->calls.pop_back(); + auto &ref = signal_or_assert(); + + for(auto pos = ref.calls.size(); pos; --pos) { + if(auto &elem = ref.calls[pos - 1u]; callback(elem)) { + elem = std::move(ref.calls.back()); + ref.calls.pop_back(); } } } + [[nodiscard]] auto &signal_or_assert() const noexcept { + ENTT_ASSERT(signal != nullptr, "Invalid pointer to signal"); + return *signal; + } + public: + /*! @brief Constructs an invalid sink. */ + sink() noexcept + : signal{} {} + /** * @brief Constructs a sink that is allowed to modify a given signal. * @param ref A valid reference to a signal object. @@ -395,7 +406,7 @@ class sink> { * @return True if the sink has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const noexcept { - return signal->calls.empty(); + return signal_or_assert().calls.empty(); } /** @@ -412,7 +423,7 @@ class sink> { delegate_type call{}; call.template connect(value_or_instance...); - signal->calls.push_back(std::move(call)); + signal_or_assert().calls.push_back(std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance...); @@ -445,7 +456,15 @@ class sink> { /*! @brief Disconnects all the listeners from a signal. */ void disconnect() { - signal->calls.clear(); + signal_or_assert().calls.clear(); + } + + /** + * @brief Returns true if a sink is correctly initialized, false otherwise. + * @return True if a sink is correctly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return signal != nullptr; } private: diff --git a/test/entt/signal/sigh.cpp b/test/entt/signal/sigh.cpp index 0870536eb..98eb3df65 100644 --- a/test/entt/signal/sigh.cpp +++ b/test/entt/signal/sigh.cpp @@ -2,6 +2,7 @@ #include #include #include +#include "../../common/config.h" #include "../../common/linter.hpp" struct sigh_listener { @@ -52,6 +53,25 @@ void connect_and_auto_disconnect(entt::sigh &sigh, const int &) { sink.disconnect<&connect_and_auto_disconnect>(sigh); } +ENTT_DEBUG_TEST(SinkDeathTest, Invalid) { + sigh_listener listener; + entt::sigh sigh; + entt::sink> sink{}; + + ASSERT_FALSE(sink); + + ASSERT_DEATH([[maybe_unused]] bool empty = sink.empty(), ""); + ASSERT_DEATH(sink.connect<&sigh_listener::f>(), ""); + ASSERT_DEATH(sink.disconnect<&sigh_listener::f>(), ""); + ASSERT_DEATH(sink.disconnect(&listener), ""); + ASSERT_DEATH(sink.disconnect(), ""); + + sink = entt::sink{sigh}; + + ASSERT_TRUE(sink); + ASSERT_TRUE(sink.empty()); +} + TEST(SigH, Lifetime) { using signal = entt::sigh;