From 442d2d75d71bbc057e667edc301a79fa1cc813be Mon Sep 17 00:00:00 2001 From: rtk0c Date: Sat, 27 Mar 2021 23:01:07 -0700 Subject: Initial setup --- core/src/Utils/Sigslot.cpp | 214 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 core/src/Utils/Sigslot.cpp (limited to 'core/src/Utils/Sigslot.cpp') diff --git a/core/src/Utils/Sigslot.cpp b/core/src/Utils/Sigslot.cpp new file mode 100644 index 0000000..a36eb2f --- /dev/null +++ b/core/src/Utils/Sigslot.cpp @@ -0,0 +1,214 @@ +#include "Sigslot.hpp" + +#include + +bool SignalStub::Connection::IsOccupied() const { + return id != InvalidId; +} + +SignalStub::SignalStub(IWrapper& wrapper) + : mWrapper{ &wrapper } { +} + +SignalStub::~SignalStub() { + RemoveAllConnections(); +} + +std::span SignalStub::GetConnections() const { + return mConnections; +} + +SignalStub::Connection& SignalStub::InsertConnection(SlotGuard* guard) { + Connection* result; + int size = static_cast(mConnections.size()); + for (int i = 0; i < size; ++i) { + auto& conn = mConnections[i]; + if (!conn.IsOccupied()) { + result = &conn; + result->id = i; + goto setup; + } + } + + mConnections.push_back(Connection{}); + result = &mConnections.back(); + result->id = size; + +setup: + if (guard) { + result->guard = guard; + result->slotId = guard->InsertConnection(*this, result->id); + } + return *result; +} + +void SignalStub::RemoveConnection(int id) { + if (id >= 0 && id < mConnections.size()) { + auto& conn = mConnections[id]; + if (conn.IsOccupied()) { + mWrapper->RemoveFunction(conn.id); + if (conn.guard) { + conn.guard->RemoveConnection(conn.slotId); + } + + conn.guard = nullptr; + conn.slotId = SignalStub::InvalidId; + conn.id = SignalStub::InvalidId; + } + } +} + +void SignalStub::RemoveConnectionFor(SlotGuard& guard) { + guard.RemoveConnectionFor(*this); +} + +void SignalStub::RemoveAllConnections() { + for (size_t i = 0; i < mConnections.size(); ++i) { + RemoveConnection(i); + } +} + +SlotGuard::SlotGuard() { +} + +SlotGuard::~SlotGuard() { + DisconnectAll(); +} + +void SlotGuard::DisconnectAll() { + for (auto& conn : mConnections) { + if (conn.stub) { + // Also calls SlotGuard::removeConnection, our copy of the data will be cleared in it + conn.stub->RemoveConnection(conn.stubId); + } + } +} + +int SlotGuard::InsertConnection(SignalStub& stub, int stubId) { + int size = static_cast(mConnections.size()); + for (int i = 0; i < size; ++i) { + auto& conn = mConnections[i]; + if (!conn.stub) { + conn.stub = &stub; + conn.stubId = stubId; + return i; + } + } + + mConnections.push_back(Connection{}); + auto& conn = mConnections.back(); + conn.stub = &stub; + conn.stubId = stubId; + return size; +} + +void SlotGuard::RemoveConnectionFor(SignalStub& stub) { + for (auto& conn : mConnections) { + if (conn.stub == &stub) { + conn.stub->RemoveConnection(conn.stubId); + } + } +} + +void SlotGuard::RemoveConnection(int slotId) { + mConnections[slotId] = {}; +} + +TEST_CASE("Signal connect and disconnect") { + Signal<> sig; + + int counter = 0; + int id = sig.Connect([&]() { counter++; }); + + sig(); + CHECK(counter == 1); + + sig(); + CHECK(counter == 2); + + sig.Disconnect(id); + sig(); + CHECK(counter == 2); +} + +TEST_CASE("Signal with parameters") { + Signal sig; + + int counter = 0; + int id = sig.Connect([&](int i) { counter += i; }); + + sig(1); + CHECK(counter == 1); + + sig(0); + CHECK(counter == 1); + + sig(4); + CHECK(counter == 5); + + sig.Disconnect(id); + sig(1); + CHECK(counter == 5); +} + +TEST_CASE("Signal disconnectAll()") { + Signal<> sig; + + int counter1 = 0; + int counter2 = 0; + sig.Connect([&]() { counter1++; }); + sig.Connect([&]() { counter2++; }); + + sig(); + CHECK(counter1 == 1); + CHECK(counter2 == 1); + + sig(); + CHECK(counter1 == 2); + CHECK(counter2 == 2); + + sig.DisconnectAll(); + sig(); + CHECK(counter1 == 2); + CHECK(counter2 == 2); +} + +TEST_CASE("SlotGuard auto-disconnection") { + int counter1 = 0; + int counter2 = 0; + Signal<> sig; + + { + SlotGuard guard; + sig.Connect(guard, [&]() { counter1 += 1; }); + sig.Connect(guard, [&]() { counter2 += 1; }); + + sig(); + CHECK(counter1 == 1); + CHECK(counter2 == 1); + + sig(); + CHECK(counter1 == 2); + CHECK(counter2 == 2); + } + + sig(); + CHECK(counter1 == 2); + CHECK(counter2 == 2); +} + +TEST_CASE("Signal destruct before SlotGuard") { + int counter = 0; + SlotGuard guard; + + { + Signal<> sig2; + sig2.Connect(guard, [&]() { counter++; }); + + sig2(); + CHECK(counter == 1); + } + + // Shouldn't error + guard.DisconnectAll(); +} -- cgit v1.2.3-70-g09d2