Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implementing single-shot connection mechanism #68

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/kdbindings/connection_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ class ConnectionHandle
template<typename...>
friend class Signal;

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not needed anymore?


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

Expand All @@ -206,6 +208,14 @@ class ConnectionHandle
}
return nullptr;
}

// 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;
}
};

/**
Expand Down
31 changes: 31 additions & 0 deletions src/kdbindings/signal.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,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 and manage its own connection handle.
* This method is particularly useful for creating connections that can autonomously 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 reference 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 ConnectionHandle reference followed by the signal's parameter types.
* @return A ConnectionHandle to the newly established connection, allowing for advanced connection management.
*/
ConnectionHandle connectReflective(std::function<void(ConnectionHandle &, Args...)> const &slot)
{
ensureImpl();

// Create a new ConnectionHandle with no ID initially. This handle will be used to manage the connection lifecycle.
auto handle = ConnectionHandle::create(m_impl, std::nullopt);

// Prepare the lambda that matches the signature expected by m_impl->connect
auto wrappedSlot = [this, handle, slot](Args... args) mutable {
// Directly invoke the user-provided slot with the handle and args
slot(*handle, args...);
};

// Connect the wrapped slot to the signal implementation.
// The handle ID is set after successful connection to manage this connection specifically.
handle->setId(m_impl->connect(wrappedSlot));

// Return the ConnectionHandle, allowing the caller to manage the connection directly.
return *handle;
}

/**
* @brief Establishes a deferred connection between the provided evaluator and slot.
*
Expand Down
23 changes: 23 additions & 0 deletions tests/signal/tst_signal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,29 @@ 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](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); // Since the slot is disconnected, this should not affect 'val'

// Check if the value remains unchanged after the second emit
REQUIRE(val == 10); // 'val' was incremented once to 10 by the first emit and should remain at 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
Loading