aboutsummaryrefslogtreecommitdiff
path: root/app/source/Cplt/Utils/Sigslot.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'app/source/Cplt/Utils/Sigslot.hpp')
-rw-r--r--app/source/Cplt/Utils/Sigslot.hpp165
1 files changed, 165 insertions, 0 deletions
diff --git a/app/source/Cplt/Utils/Sigslot.hpp b/app/source/Cplt/Utils/Sigslot.hpp
new file mode 100644
index 0000000..a4ab94e
--- /dev/null
+++ b/app/source/Cplt/Utils/Sigslot.hpp
@@ -0,0 +1,165 @@
+#pragma once
+
+#include <Cplt/Utils/fwd.hpp>
+
+#include <cstddef>
+#include <functional>
+#include <span>
+#include <utility>
+#include <vector>
+
+class SignalStub
+{
+public:
+ /// Non-template interface for Signal<T...> to implement (a barrier to stop template
+ /// arguments propagation).
+ class IWrapper
+ {
+ public:
+ virtual ~IWrapper() = default;
+ virtual void RemoveFunction(int id) = 0;
+ };
+
+ enum
+ {
+ InvalidId = -1,
+ };
+
+ struct Connection
+ {
+ SlotGuard* guard;
+ int slotId;
+ int id = InvalidId; // If `InvalidId`, then this "spot" is unused
+
+ bool IsOccupied() const;
+ };
+
+private:
+ std::vector<Connection> mConnections;
+ IWrapper* mWrapper;
+
+private:
+ template <class...>
+ friend class Signal;
+ friend class SlotGuard;
+
+ SignalStub(IWrapper& wrapper);
+ ~SignalStub();
+
+ SignalStub(const SignalStub&) = delete;
+ SignalStub& operator=(const SignalStub&) = delete;
+ SignalStub(SignalStub&&) = default;
+ SignalStub& operator=(SignalStub&&) = default;
+
+ std::span<const Connection> GetConnections() const;
+ Connection& InsertConnection(SlotGuard* guard = nullptr);
+ void RemoveConnection(int id);
+ void RemoveConnectionFor(SlotGuard& guard);
+ void RemoveAllConnections();
+};
+
+template <class... TArgs>
+class Signal : public SignalStub::IWrapper
+{
+private:
+ // Must be in this order so that mFunctions is still intact when mStub's destructor runs
+ std::vector<std::function<void(TArgs...)>> mFunctions;
+ SignalStub mStub;
+
+public:
+ Signal()
+ : mStub(*this)
+ {
+ }
+
+ virtual ~Signal() = default;
+
+ Signal(const Signal&) = delete;
+ Signal& operator=(const Signal&) = delete;
+ Signal(Signal&&) = default;
+ Signal& operator=(Signal&&) = default;
+
+ void operator()(TArgs... args)
+ {
+ for (auto& conn : mStub.GetConnections()) {
+ if (conn.IsOccupied()) {
+ mFunctions[conn.id](std::forward<TArgs>(args)...);
+ }
+ }
+ }
+
+ template <class TFunction>
+ int Connect(TFunction slot)
+ {
+ auto& conn = mStub.InsertConnection();
+ mFunctions.resize(std::max(mFunctions.size(), (size_t)conn.id + 1));
+ mFunctions[conn.id] = std::move(slot);
+ return conn.id;
+ }
+
+ template <class TFunction>
+ int Connect(SlotGuard& guard, TFunction slot)
+ {
+ auto& conn = mStub.InsertConnection(&guard);
+ mFunctions.resize(std::max(mFunctions.size(), (size_t)conn.id + 1));
+ mFunctions[conn.id] = std::move(slot);
+ return conn.id;
+ }
+
+ void Disconnect(int id)
+ {
+ mStub.RemoveConnection(id);
+ }
+
+ void DisconnectFor(SlotGuard& guard)
+ {
+ mStub.RemoveConnectionFor(guard);
+ }
+
+ void DisconnectAll()
+ {
+ mStub.RemoveAllConnections();
+ }
+
+ virtual void RemoveFunction(int id)
+ {
+ mFunctions[id] = {};
+ }
+};
+
+/// Automatic disconnection mechanism for Signal<>.
+/// Bind connection to this guard by using the Connect(SlotGuard&, TFunction) overload.
+/// Either DisconnectAll() or the destructor disconnects all connections bound to this guard.
+class SlotGuard
+{
+private:
+ struct Connection
+ {
+ SignalStub* stub = nullptr;
+ int stubId = SignalStub::InvalidId;
+ };
+ std::vector<Connection> mConnections;
+
+public:
+ friend class SignalStub;
+ SlotGuard();
+ ~SlotGuard();
+
+ SlotGuard(const SlotGuard&) = delete;
+ SlotGuard& operator=(const SlotGuard&) = delete;
+ SlotGuard(SlotGuard&&) = default;
+ SlotGuard& operator=(SlotGuard&&) = default;
+
+ /// DisconnectBySource all connection associated with this SlotGuard.
+ void DisconnectAll();
+
+private:
+ /// \return Slot id.
+ int InsertConnection(SignalStub& stub, int stubId);
+ /// Remove the connection data in this associated with slotId. This does not invoke
+ /// the connections' stub's RemoveConnection function.
+ void RemoveConnection(int slotId);
+ /// DisconnectBySource all connections from the given stub associated with this SlotGuard.
+ /// Implementation for SignalStub::RemoveConnectionsFor(SlotGuard&)
+ void RemoveConnectionFor(SignalStub& stub);
+};