I'v read the documentation for QObject::connect (for Qt 5.4), but I have a question about the overload
QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, const QObject * context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)
What exactly is the context
parameter? What is its purpose? Can it be used to build connections in local event loops in threads?
Can someone provide examples of how/when to use this overload (when the context is not this
)?
The context object is used in two scenarios.
Let's first do a step back and ask ourselves: when does Qt break a connection?
With the usual connect(sender, signal, receiver, slot)
connect, there are three possibilities:
disconnect
;sender
is deleted;receiver
is deleted.Especially in cases #2 and #3, it just makes sense for Qt to behave that way (actually, it must behave that way, otherwise you'd have resource leaks and/or crashes).
Now: when using the connect
overload taking a functor, when does Qt break a connection?
Note that without the context
parameter, there's only one QObject involved: the sender. Hence the answer is:
disconnect
;sender
is deleted.Of course, there's no receiver object here! So only the sender automatically controls the lifetime of a connection.
Now, the problem is that the functor may capture some extra state that can become invalid, in which case is desirable that the connection gets broken automatically. The typical case is with lambdas:
connect(sender, &Sender::signal,
[&object1, &object2](Param p)
{
use(object1, object2, p);
}
);
What happens if object1
or object2
get deleted? The connection will still be alive, therefore emitting the signal will still invoke the lambda, which in turn will access destroyed objects. And that's kind of bad...
For this reason, when it comes to functors, a connect
overload taking a context object has been introduced. A connection established using that overload will be disconnected automatically also
context
object is deleted.You're probably right when you say that a good number of times you're going to see there the very same "main" object used in the functor, for instance
connect(button,
&QPushButton::clicked,
otherWidget,
[otherWidget]()
{
otherWidget->doThis(); otherWidget->doThat();
}
);
That's just a pattern in Qt -- when setting up connections for sub-objects, you typically connect them to slots on this
object, hence this
is probably the most common context. However, in general, you may also end up with something like
// manages the lifetime of the resources; they will never outlive this object
struct ResourceManager : QObject
{
Resource res1; // non-QObjects
OtherResource res2;
};
ResourceManager manager;
connect(sender, signal, manager, [&manager](){ use(manager.res1, ...); });
// or, directly capture the resources, not the handle
So, you're capturing part of the state of manager
.
In the most general case, when no context
object is available, if there's the chance that the objects captured by the lambda survive the connection, then you must capture them by weak pointers, and try to lock those pointers inside the lambda before trying to access them.
Very shortly: when specifying a context object, the functor will be run into the context's thread, just like normal connections employing a receiver object. Indeed, note that the connect
overload that takes a context also takes a connection type (while the one without context doesn't take one -- connection is always direct).
Again, this is useful because QObject is not reentrant or thread safe, and you must use a QObject only in the thread it lives in. If your functor accesses an object living in another thread, it must be executed in that thread; specifying that object as the context solves the issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With