[solved] Qt5: Can't define connection type for functions and functors
-
Do you have C++11 support? How are you making the connection?
-
bq. Do you have C++11 support?
Yes, I'm using Qt Creator 2.7.2 on Windows which seems to include MingW 4.8.x
bq. How are you making the connection?
I'm trying the following (pseudocode):
@
QThread *thread = new QThread;
connect( thread, &QThread::started, [this] {
statusBar()->showMessage( "Thread started." );
});
@This gives me, as expected, at runtime: ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread
So I wanted to use Qt::QueuedConnection which gave me a compiler error and when I followed the original call to the actual connect implementation, I saw that it uses Qt::DirectConnection (qobject.h, line 314) for functors and for functions (qobject.h, line 261)
So I wonder whether there is a good reason why I can't specify the connection type or if this is simply a bug in Qt?
Regards Peter
-
Ah, you're right -- Neither MSVC2012 nor MinGW 4.7 will let me connect to a lambda/functor while specifying the connection type.
Having said that, I had a look at the "documentation":http://qt-project.org/doc/qt-5.1/qtcore/qobject.html , and it DOESN'T say that you can specify the connection type here. Notice that the Qt::ConnectionType parameter is only available if the receiver is a QObject -- The signature for functor connections is simply
@
QMetaObject::Connection QObject::connect(const QObject * sender, PointerToMemberFunction signal, Functor functor);
@It makes sense I think: QObjects have a "thread affinity":http://qt-project.org/doc/qt-5.1/qtcore/threads-qobject.html, and a queued connection is handled by the thread which the receiving QObject lives in. On the other hand, functors have no concept of thread affinity, so queued connections are meaningless to them (which thread do you expect to run the functor?)
In the example you posted, the correct way to do things it to create a slot in your widget to update the status bar.
-
bq. Having said that, I had a look at the documentation [qt-project.org] , and it DOESN’T say that you can specify the connection type here.
Oops, sorry my mistake.
bq. On the other hand, functors have no concept of thread affinity, so queued connections are meaningless to them (which thread do you expect to run the functor?)
My understanding is:
- If I use QueuedConnection then the call is executed in the context of the receiver thread (which has to have an event loop).
- If I use DirectConnection then the call is executed in the context of the sender thread.
So in my case I want the functor/function to be called in the context of the GUI thread and I can't come up with a reason why this shouldn't be possible. Using AutoConnection might not be possible because, as you said, what is the thread affinity of the functor/function, but specifying the connection type explicitly should IMHO work.
-
[quote]If I use QueuedConnection then the call is executed in the context of the receiver thread (which has to have an event loop).[/quote]Correct. Also, remember that the receiver needs to know which thread it belongs to.
[quote]If I use DirectConnection then the call is executed in the context of the sender thread.[/quote]Be careful here; that statement is ambiguous. There is a subtle difference between the "sender object's thread" and the "sending thread".Example: If you create a QThread in the GUI thread, the QThread object lives in the GUI thread (even though it represents/manages the secondary thread). So, if you explicitly emit a QThread signal in QThread::run(), the signal is sent from the secondary thread, even though the QThread lives in the GUI thread.
[quote]So in my case I want the functor/function to be called in the context of the GUI thread and I can't come up with a reason why this shouldn't be possible. Using AutoConnection might not be possible because, as you said, what is the thread affinity of the functor/function, but specifying the connection type explicitly should IMHO work. [/quote]Let's put it another way: Whenever you specify a queued connection, Qt needs to find the thread that the receiver lives in. Since a functor does not live in any thread, Qt can't find out which thread should run your functor.
To run a function in the context of the GUI thread, just make that function a part of a QObject that lives in the GUI thread. That means, turn that function into a QObject's slot.
-
bq. Be careful here; that statement is ambiguous. There is a subtle difference between the “sender object’s thread” and the “sending thread”.
I mean the actual OS thread.
bq. Let’s put it another way: Whenever you specify a queued connection, Qt needs to find the thread that the receiver lives in. Since a functor does not live in any thread, Qt can’t find out which thread should run your functor.
What if QObject::connect() would get the current thread (i.e. the receiver thread) with QThread::currentThread() and stores it together with the copied functor or function pointer? When it is time to emit the signal from the sender thread, then the receiver thread is known and the call can be posted to that event queue.
-
[quote author="P. Most" date="1373385675"]What if QObject::connect() would get the current thread (i.e. the receiver thread) with QThread::currentThread() and stores it together with the copied functor or function pointer? When it is time to emit the signal from the sender thread, then the receiver thread is known and the call can be posted to that event queue.[/quote]In theory, that's possible to implement. However, that would be inconsistent with all other connections:
- Connection type determines the relationship between the emitting thread and the receiver thread only -- it shouldn't care about the thread which called connect()
- The thread is determined when the slot is invoked -- not when the connection is made. This way, when objects are moved to a new thread, existing auto/queued connections will correctly invoke slots in the new thread
Besides, why should Qt::QueuedConnection consider the current thread (and not some other thread) as the receiver thread? What if a developer wants the functor to run in a thread that's different from the one that made the connection?
-
bq. However, that would be inconsistent with all other connections:
Isn't it already inconsistent since you can't specify the connection type for functors and functions?
bq. Connection type determines the relationship between the emitting thread and the receiver thread only — it shouldn’t care about the thread which called connect()
But for functions and functors this would be the only way.
bq. This way, when objects are moved to a new thread, existing auto/queued connections will correctly invoke slots in the new thread
Since you can't move functions or functors to another thread this shouldn't be a problem.
bq. What if a developer wants the functor to run in a thread that’s different from the one that made the connection?
Well, either you simply can't or the returned Connection class could be extended so you could change the receiver thread. But I could live with "you can't" because as we have established functions and functors are already limited in the way they are being called, so if one could specify the connection type then one had to live with the imposed restrictions.
But anyway thanks for the help/clarification. We can discuss this further if you like, but I think we are at a point where it would make more sense to open a bug report and discuss the options there.
Regards Peter
-
Filed a bug report: https://bugreports.qt-project.org/browse/QTBUG-32339
-
[quote author="P. Most" date="1373444701"]Isn't it already inconsistent since you can't specify the connection type for functors and functions? [/quote]That's true :) I guess I don't usually think about specifying connection type, because Qt::AutoConnection should make the correct choice 99% of the time.
By the way, I tried the code that you posted above, and it worked for me in debug mode (as it should, since both the QThread object and the QMainWindow live in the GUI thread, so a direct connection is the way to go). Are you sure that's the source of your ASSERT failure?
[quote author="P. Most" date="1373444701"][quote author="JKSH" date="1373392088"] This way, when objects are moved to a new thread, existing auto/queued connections will correctly invoke slots in the new thread[/quote]Since you can't move functions or functors to another thread this shouldn't be a problem.[/quote]I guess not.
[quote author="P. Most" date="1373444701"][quote author="JKSH" date="1373392088"]What if a developer wants the functor to run in a thread that’s different from the one that made the connection?[/quote]Well, either you simply can't or the returned Connection class could be extended so you could change the receiver thread. But I could live with "you can't" because as we have established functions and functors are already limited in the way they are being called, so if one could specify the connection type then one had to live with the imposed restrictions.[/quote]I guess the question now becomes, "Where do we draw the line with the restrictions -- disallow specifying connection type? Disallow specifying the thread? Or have no restrictions on either?"
[quote]But anyway thanks for the help/clarification. We can discuss this further if you like, but I think we are at a point where it would make more sense to open a bug report and discuss the options there.[/quote]
You're welcome. If you wish to bring this idea to Qt's engineers and other contributors, I recommend posting to the "Development mailing list":http://lists.qt-project.org/mailman/listinfo/development -- that way it will be seen by many more people. The bug tracker is heavily backlogged at the moment, and tickets tend to be read by relatively few people.