From d7889dca70e22c9c96dc42460e3c95465827c914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Sun, 7 Jun 2015 00:51:43 +0200 Subject: [PATCH] Core/Signal: Add Connection object and Disconnection --- include/Nazara/Core/Signal.hpp | 83 +++++++++++++++++++++++++++--- include/Nazara/Core/Signal.inl | 94 +++++++++++++++++++++++++++++++--- 2 files changed, 163 insertions(+), 14 deletions(-) diff --git a/include/Nazara/Core/Signal.hpp b/include/Nazara/Core/Signal.hpp index e31dc57b08..f551e97672 100644 --- a/include/Nazara/Core/Signal.hpp +++ b/include/Nazara/Core/Signal.hpp @@ -8,26 +8,97 @@ #define NAZARA_SIGNAL_HPP #include -#include +#include +#include template class NzSignal { public: using Callback = std::function; + class Connection; + class ConnectionGuard; NzSignal() = default; + NzSignal(const NzSignal&) = delete; + NzSignal(NzSignal&& signal); ~NzSignal() = default; - void Connect(const Callback& func); - void Connect(Callback&& func); - template void Connect(O& object, void (O::*method)(Args...)); - template void Connect(O* object, void (O::*method)(Args...)); + void Clear(); + + Connection&& Connect(const Callback& func); + Connection&& Connect(Callback&& func); + template Connection&& Connect(O& object, void (O::*method)(Args...)); + template Connection&& Connect(O* object, void (O::*method)(Args...)); void operator()(Args&&... args); + NzSignal& operator=(const NzSignal&) = delete; + NzSignal& operator=(NzSignal&& signal); + + private: + struct Slot; + + using SlotPtr = std::shared_ptr; + using SlotList = std::list; + + struct Slot + { + Slot(NzSignal* me) : + signal(me) + { + } + + Callback callback; + NzSignal* signal; + typename SlotList::iterator it; + }; + + void Disconnect(const SlotPtr& slot); + + SlotList m_slots; +}; + +template +class NzSignal::Connection +{ + using BaseClass = NzSignal; + friend BaseClass; + + public: + Connection(const Connection& connection) = default; + Connection(Connection&& connection) = default; + ~Connection() = default; + + void Disconnect(); + + bool IsConnected() const; + + Connection& operator=(const Connection& connection) = default; + Connection& operator=(Connection&& connection) = default; + + private: + Connection(const SlotPtr& slot); + + std::weak_ptr m_ptr; +}; + +template +class NzSignal::ConnectionGuard +{ + using BaseClass = NzSignal; + using Connection = BaseClass::Connection; + + public: + ConnectionGuard(const Connection& connection); + ConnectionGuard(Connection&& connection); + ~ConnectionGuard(); + + Connection& operator=(const Connection& connection) = delete; + Connection& operator=(Connection&& connection) = delete; + private: - std::vector m_callbacks; + Connection m_connection; }; #include diff --git a/include/Nazara/Core/Signal.inl b/include/Nazara/Core/Signal.inl index 59aa4e954e..75a82624f1 100644 --- a/include/Nazara/Core/Signal.inl +++ b/include/Nazara/Core/Signal.inl @@ -7,20 +7,41 @@ #include template -void NzSignal::Connect(const Callback& func) +NzSignal::NzSignal(NzSignal&& signal) { - m_callbacks.push_back(func); + operator=(std::move(signal)); } template -void NzSignal::Connect(Callback&& func) +void NzSignal::Clear() { - m_callbacks.emplace_back(std::move(func)); + m_slots.clear(); +} + +template +typename NzSignal::Connection&& NzSignal::Connect(const Callback& func) +{ + return Connect(std::move(Callback(func))); +} + +template +typename NzSignal::Connection&& NzSignal::Connect(Callback&& func) +{ + auto tempPtr = std::make_shared(this); + tempPtr->callback = std::move(func); + + m_slots.emplace_back(std::move(tempPtr)); + + const auto& slotPtr = m_slots.back(); + slotPtr->it = m_slots.end(); + --slotPtr->it; + + return Connection(slotPtr); } template template -void NzSignal::Connect(O& object, void (O::*method) (Args...)) +typename NzSignal::Connection&& NzSignal::Connect(O& object, void (O::*method) (Args...)) { return Connect([&object, method] (Args&&... args) { @@ -30,7 +51,7 @@ void NzSignal::Connect(O& object, void (O::*method) (Args...)) template template -void NzSignal::Connect(O* object, void (O::*method)(Args...)) +typename NzSignal::Connection&& NzSignal::Connect(O* object, void (O::*method)(Args...)) { return Connect([object, method] (Args&&... args) { @@ -41,8 +62,65 @@ void NzSignal::Connect(O* object, void (O::*method)(Args...)) template void NzSignal::operator()(Args&&... args) { - for (const Callback& func : m_callbacks) - func(std::forward(args)...); + for (const SlotPtr& slot : m_slots) + slot->callback(std::forward(args)...); +} + +template +NzSignal& NzSignal::operator=(NzSignal&& signal) +{ + m_slots = std::move(signal.m_slots); + + // We need to update the signal pointer inside of each slot + for (SlotPtr& slot : m_slots) + slot->signal = this; + + return *this; +} + +template +void NzSignal::Disconnect(const SlotPtr& slot) +{ + m_slots.erase(slot->it); +} + + +template +NzSignal::Connection::Connection(const SlotPtr& slot) : +m_ptr(slot) +{ +} + +template +void NzSignal::Connection::Disconnect() +{ + if (SlotPtr ptr = m_ptr.lock()) + ptr->signal->Disconnect(ptr); +} + +template +bool NzSignal::Connection::IsConnected() const +{ + return !m_ptr.expired(); +} + + +template +NzSignal::ConnectionGuard::ConnectionGuard(const Connection& connection) : +m_connection(connection) +{ +} + +template +NzSignal::ConnectionGuard::ConnectionGuard(Connection&& connection) : +m_connection(std::move(connection)) +{ +} + +template +NzSignal::ConnectionGuard::~ConnectionGuard() +{ + m_connection.Disconnect(); } #include