Skip to content

Commit

Permalink
implementing single-shot connection mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
phyBrackets authored and LeonMatthesKDAB committed Jul 3, 2024
1 parent deaaa9e commit 223ba2c
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/kdbindings/connection_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,19 @@ class ConnectionHandle
return false;
}

// Factory method to create a ConnectionHandle
static std::shared_ptr<ConnectionHandle> create(const std::weak_ptr<Private::SignalImplBase>& signalImpl, std::optional<Private::GenerationalIndex> id) {
auto handle = std::shared_ptr<ConnectionHandle>(new ConnectionHandle(signalImpl, id));
handle->self = handle; // Keep a weak reference to self
return handle;
}

private:
template<typename...>
friend class Signal;

std::weak_ptr<ConnectionHandle> self; // Allows for safe self-reference

std::weak_ptr<Private::SignalImplBase> m_signalImpl;
std::optional<Private::GenerationalIndex> m_id;

Expand Down
31 changes: 31 additions & 0 deletions src/kdbindings/signal.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,37 @@ class Signal
return ConnectionHandle{ m_impl, m_impl->connect(slot) };
}

/**
* Establishes a connection between a signal and a slot, allowing the slot to access its own connection handle.
* This method is particularly useful for creating connections that can manage themselves, such as disconnecting
* after being triggered a certain number of times or under specific conditions. It wraps the given slot function
* to include a shared pointer to the ConnectionHandle as the first parameter, enabling the slot to interact with
* its own connection state directly.
*
* @param slot A std::function that takes a shared_ptr<ConnectionHandle> followed by the signal's parameter types.
* @return A shared_ptr to the ConnectionHandle associated with this connection, allowing for advanced connection management.
*/
std::shared_ptr<ConnectionHandle> connectReflective(std::function<void(std::shared_ptr<ConnectionHandle>, Args...)> const &slot)
{
ensureImpl();

// Create a placeholder handle (with no ID yet)
auto handle = ConnectionHandle::create(m_impl, std::nullopt);

auto wrappedSlot = [this, slot, weakHandle = std::weak_ptr<ConnectionHandle>(handle)](Args... args) {
if (auto handle = weakHandle.lock()) { // Ensure the handle is still valid
slot(handle, args...);
}
};

// Connect the wrapped slot
auto id = m_impl->connect(wrappedSlot);

// Assign the ID to the handle now that it's known
handle->setId(id);
return handle;
}

/**
* @brief Establishes a deferred connection between the provided evaluator and slot.
*
Expand Down
21 changes: 21 additions & 0 deletions tests/signal/tst_signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,27 @@ TEST_CASE("Signal connections")
REQUIRE(anotherCalled);
}

SUBCASE("Single Shot Connection")
{
Signal<int> mySignal;
int val = 5;

// Connect a reflective slot to the signal
auto handle = mySignal.connectReflective([&val](std::shared_ptr<ConnectionHandle> selfHandle, int value) {
val += value;

// Disconnect after handling the signal once
selfHandle->disconnect();
});

mySignal.emit(5); // This should trigger the slot and then disconnect it
REQUIRE(!handle->isActive());

mySignal.emit(5); // Signal Already disconnected

REQUIRE(val == 10);
}

SUBCASE("A signal with arguments can be connected to a lambda and invoked with l-value args")
{
Signal<std::string, int> signal;
Expand Down

0 comments on commit 223ba2c

Please sign in to comment.