-
Notifications
You must be signed in to change notification settings - Fork 102
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
sigslot as Qt replacement: QSignalSpy #16
Comments
Hi, If I understand correctly, this is something along the lines of this? template <typename L, typename... T>
class spy : public std::vector<std::tuple<T...>> {
public:
explicit spy(signal_base<L, T...> &sig)
: m_sig(sig)
{
sig.connect([this] (T &&... t) {
this->emplace_back(t...);
/* notify wait */
});
}
auto& signal() { return m_sig; }
bool wait(int timeout) { /* start a thread? */ }
private:
signal_base<L, T...> &m_sig;
}; |
Yea, that looks very nice. And it's great how compact it is. std::vector could be also composited but subclassing gives direct access to all its functionality and a spy should really be used in tests only anyway. I like the variadic template args to tuple flow. 👍 For QSignalSpy a call to wait() according to docs starts a separate event loop and returns only after the signal was received. So I guess that would translate here as you put in the comment to starting a new thread until the spy.size() increases and then joining back again. |
I assume the spy will be used in a multi-threaded context, wait() being a blocking call? QSignalSpy creates an event loop whenever wait() gets invoked. This loop is responsible for dispatching the signals to the slots in Qt, so timers and signals created before the wait() in the same thread can be used and will be received by the slots as expected. Sigslot cannot take on this role, we need to either enforce all the logic to be tested to happen in another thread, or build a tool tailored for a specific scenario. Can you explain how the spy would be used? A concrete example would be nice. |
Yes.
A typical example from my unit tests would be this. It is about a Wayland client connecting to a Wayland server (via a Wayland socket). There are two threads/event loops at work, the Wayland server sits in the main thread of the test/QApplication, the Wayland client sits in a second QThread (the ConnectionThread object is not a thread itself, but a normal QObject moved to the created QThread). We ask to Bottom line is that the client is pretty independent in all of that, being in a separate thread and just sending some Wayland protocol request via socket. But the server is in the main thread and as you noticed that can't be directly ported since when we block on the spy we still process events in the main event loop/thread. I don't directly know how this would port over to a signal-slot system without this integrated event-loops, would need to try out with some prototype probably. Or do you have an idea? |
There is no obvious solution, I do not think implementing a generic event-loop agnostic spy is a good idea. Generally speaking, the wait() method should start or defer the wait to the event-loop running in the current thread. If you keep Qt's event loop, the best bet would be to make wait() duplicate the logic of QSignalSpy by creating a QtEventLoop with a timeout. In the meantime, I pushed a toy signal spy in a new test in the "spy' branch: I do not know if this is relevant to the issue at hand. |
Yes, that's true. Maybe a small API with a callback to integrate with an event loop could be possible? I'm not sure it's worth it though. Would need to check it out with some prototype first.
Long term plan is to move away from Qt's event loop in library contexts and make integration with it only optional. But I currently do not yet know enough about the topic to say what that would mean in detail for usage of a library like sigslot.
Thanks, hopefully I find some time soon to try out sigslot in one of my smaller libraries and then I'll come back to this branch. |
Hi, I tried your implementation for some of my tests, here is my feedback:
The whole point of Your template implementation is really nice and allow simple unit tests in mono thread environnement: sigslot::signal<int, std::string> sig;
auto spy = sigslot::make_spy(sig);
sig(1, "test")
ASSERT_EQ(spy .size(), 1);
const auto [paramInt, paramString] = spy.front();
ASSERT_EQ(paramInt, 1);
ASSERT_EQ(paramString, "test");
spy.clear(); Maybe it could be nice to add a |
Thank you for the feedback. I actually use an improved and thread-safe variant of the Looking at the spy implementation, almost nothing would be reused for another event loop, so this may be kind of useless to strive for an even loop agnostic version. Realistically, I only use Qt and boost ASIO event loops in my own code. Providing concrete implementations for those two might make more sense. |
I'm using |
I do not, but basically signals are used to asynchronously emit results from a network request, and I use a SignalWaiter to transform this asynchronous emission into a synchronous blocking call. class Client {
/// Emits results received from the network asynchronously
sigslot::signal<Result> results;
//// Wait for a result with a timeout
std::optional<Result> getResult(int timout_ms = 10000)
{
SignalWaiter waiter(results);
if (waiter.wait(timeout_ms)) {
return waiter.front();
}
return std::nullopt;
}
};
|
I'm looking for a replacement for Qt's signal-slot system too. I tried to implement simple one base on this sigslot. |
I'm currently in the progress of splitting out libraries from some Qt projects. These libraries then are supposed to have no Qt dependency anymore.
For that I'm looking for a replacement for Qt's signal-slot system. I first thought of libsigc++ because it's used by other large open source projects but I'm scoping and I liked sigslot's results in this test.
The Qt projects I'm working on often use in unit tests QSignalSpy what is a helpful little tool to record if signals were emitted and the carried arguments. It also can block (or retry in a separate Qt event loop) until a signal is received and fails after a timeout if no signal is received.
I assume something like this would be possible with sigslot too. But before I begin to write a small utility class for it downstream would you be interested in providing such a class in sigslot directly?
I could imagine a single sigslot::spy class that takes a sigslot::signal argument and records all signal activations in a simple vector with provided argument values.
The text was updated successfully, but these errors were encountered: