Solved Proper way to specify slot arguments
-
Let's say I am implementing a socket communication class that sends a QString to a remote host. In a typical C++ implementation without Qt, I would call the method as follows:
QString some_message("Hello World"); socket_class->sendData(some_message);
The prototype for that method would be:
void sendData(const QString& msg_out);
This works fine in a single-threaded application. If the socket class were running on a separate thread, I would have to change the prototype to pass by value, rather than by reference. What I am confused about is the Qt Object model in this regard. If I were to implement the same in Qt with an event loop, I would define a signal and call:
QString some_message("Hello World"); emit sendMessage(some_message);
Which would execute the slot sendData(). If the class were on the same thread as the main application, the pass by reference works as expected since the slot is executed immediately. However, if the socket code were on a separate thread, the pass by reference prototype still works. I am unclear why, and it leads me to ask what the best practice is for prototyping slots and their arguments.
-
@DRoscoe said in Proper way to specify slot arguments:
However, if the socket code were on a separate thread, the pass by reference prototype still works. I am unclear why, and it leads me to ask what the best practice is for prototyping slots and their arguments.
Always prefer pass by reference.
As to why it works: Qt will make a shallow copy of the string (it's implicitly shared) when it packs the arguments for the deferred call and ultimately will put that copy in the event loop (for later execution of the slot) so you'd get a reference to that shallow copy, not to the original string.Kind regards.
Edit: Here's the relevant part of the source if you're interested.
-
@DRoscoe said in Proper way to specify slot arguments:
I am unclear why, and it leads me to ask what the best practice is for prototyping slots and their arguments.
it works because Qt does all the work for you implicitly.
Only registered meta types can be sent to another thread (using moc = signals, etc.)
The precondition for a registered meta-type is that it has a public default constructor, a public copy constructor and a public destructor. This ensures that Qt can create copies and send them to the other thread. -
@kshegunov that link is excellent. Thanks for that! A follow up question...
If Qt uses implicit sharing and I should prefer passing by reference, does passing by value still invoke implicit sharing or is that bypassed, causing a deep copy to be created?
-
@raven-worx Thank you. That link is also an excellent resource.
-
@DRoscoe said in Proper way to specify slot arguments:
If Qt uses implicit sharing and I should prefer passing by reference, does passing by value still invoke implicit sharing or is that bypassed, causing a deep copy to be created?
If the class is implicitly shared and you pass by value you'd get a shallow copy of the object that stays in the event loop. Whether that's detached (deep copied) depends on the methods you call on it. If you call immutable functions (const methods) no detach will take place. If you however call a regular (non-const) method, then the data will be copied (i.e. a deep copy will be done).
Edit: A typical example why you should prefer const references (or at least
const
object) is if you have a vector in your slot which you only intend to iterate, e.g.:void MyClass::mySlot(QVector<int> v) { QVector<int>::Iterator i = v.begin(), end = v.end(); //< Here you will cause a detach (data copy) to happen, because it will call the non-const begin() overload. }
If you pass the vector as a
const QVector<int> &
(orconst
) then there's no needless data duplication. -
@kshegunov Thanks!
-
You're welcome!