const-ref when sending signals in Qt - c++

This is a thing that I never quite got with const-ref and I really hope that someone could explain it to me.
When calling a function inside of another function, I get that const-ref is the best way when passing stack objects that I don't plan to tamper with. For example:
void someInnerFunction(const QString& text) {
qDebug() << text;
}
void someFunction() {
QString test = "lala";
....
someInnerFunction(test);
}
So far so good, I guess. But what about signals? Isn't there any risk that comes with passing a reference? Even though it's const. It feels like I've been reading all the docs about const-ref but I still find a bit risky since I understand it as "sending a reference to an object and keeping it const". What if the object it's referring to goes out of scope?
For example:
void someFunction() {
connect(this, SIGNAL(someSignal(const QString&)), this, SLOT(someSlot(const QString&)));
QString test = "lala";
emit someSignal(test);
// doesnt test go out of scope here? and since im not using queued connection the QString object doesnt get copied.
}
void someSlot(const QString& test) {
qDebug() << test; // will this work?
}
What is really happening here? I frequently use const-ref on function calls where I just want to access the object but not change it. But what about signals? Most signals seems to have const-ref parm in the Qt doc, but how does it work?

According to this answer, Qt just replaces const references with copies.
EDIT: Apparently not always the case... I just did a basic test program with one thread, and the reference was passed correctly. Its const-ness remained intact, as well. Anyways, yes, you do need to be wary of the variable going out of scope, plus you can't send references across threads this way. If you do, only copies will be passed.
To answer the questions in the comments of your example, yes, it will work regardless of whether it's a direct or queued connection. If it's a direct connection, it will work because someSlot() will be executed before someFunction() finishes; if it's a queued connection, it will work because test will be copied instead of passed by reference.

Here is a good demonstration showing how Qt signals/slots manage copying: http://www.embeddeduse.com/2013/06/29/copied-or-not-copied-arguments-signals-slots/

In Qt, when emitting a signal that is connected to a slot or slots, it equates to a synchronous function call... unless you've configured your signals and slots to use queued connections, then it is an asynchronous call and you should be careful when passing stack data and should pass a copy as if passing data to another thread.

Related

How to connect slot with mutable argument to signal with const argument

I need to connect binaryMessageReceived signal of QWebSocket to my slot which modifies the QByteData
The QByteData may be large so it might be really costly to copy it again in mutable variable each time. I want to reuse the existing QByteData
when I try to compile with following slot
void route(QByteArray& msg);
I get compilation error
/usr/include/qt/QtCore/qobject.h:255:9: error: static assertion failed: Signal and slot arguments are not compatible.
255 | Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
| ^~~~~~~~~~~~~~~~~
/usr/include/qt/QtCore/qobject.h:255:9: note: ‘(((int)QtPrivate::CheckCompatibleArguments<QtPrivate::List<const QByteArray&>, QtPrivate::List<QByteArray&> >::value) != 0)’ evaluates to false
but if I change the slot to
void route(const QByteArray& msg);
it compiles just fine
I'm connecting slot like this:-
connect(this, &QWebSocket::binaryMessageReceived, this, &WSManager::route);
You probably don't want to do that.
The signal argument is not meant to be modified if passed as const &. You are not even sure of the lifetime of the binary data in the emitter object (QWebSocket).
The QByteData is emitted from here : https://code.woboq.org/qt5/qtwebsockets/src/websockets/qwebsocketdataprocessor.cpp.html#181
Nested in multiple classes hidden from the API, it's very dangerous to rely on this kind of attribute/data.
QByteArray uses the implicit-sharing Qt mecanism which avoid deep copy.
https://doc.qt.io/qt-5/implicit-sharing.html
That means you can pass the object around without concern about the performances. And if at some point you need to modify it, you might end up working on the only actual instance of the data thanks to the move operator.
I think you could connect to lambda and use const_cast inside the lambda function:
connect(this, &QWebSocket::binaryMessageReceived, this, [this](const QByteArray &message) {
this->route(const_cast<QByteArray &>(message));
});
Update: even you can cast constness away this way it doesn't mean it's a good idea to do that :) As it was pointed out in the other answer no data copying is done for unchanged QByteArray because of Qt implicit sharing mechanism.

Qt signal and slots: are reference arguments copied?

In qt framework, most library signals and slots use pointers as parameters. I was wondering, If I create a signal-slot "structure" that takes a reference as the parameter instead of the pointer, will the whole parameter be copied, or just 4 bytes (32-bit system) like in a regular c++ reference?
I am asking this because I noticed something when I create a signal/ slot methods with the reference parameter. When I then connect them, the autocomplete mechanism in QTCreator doesn't hint me with reference parameters like he would do with pointer parameters. He hints me with the regular parameter. For example:
I create a signal and slot:
...
signals:
void mySignal(int& parameter);
private slots:
void on_mySignal(int& parameter);
I then attempt to connected them and Qt doesnt add & for reference in parameter:
...
connect(this, SIGNAL(mySignal(int)), this, SLOT(on_mySignal(int)));
I have to manually change to:
connect(this, SIGNAL(mySignal(int&)), this, SLOT(on_mySignal(int&)));
Thus I am wondering, does reference even work with signal/slot? I would appreciate all help.
If you send and receive a reference, on the same thread, per default no copy will be made. If you do anything else, including sending/receiving a value or sending a reference to another thread, one, two or even three copies will be made.
What happens depends on the connection type and the assurances QT needs to know that references remain valid through the call. A direct connection on the same thread resolves to a simple function call, so very little can happen to the underlying data. A queued connection, however, offers no guarantees for when the call will actually happen, therefore QT will make copies to preserve data integrity. QT implicitly queues signals crossing thread boundaries.
If either side is pass-by-value then QT copies the data to not affect the underlying object's state.
For more information, have a look at this blog post.

Qt using boost::shared_ptr in a signal/slot

Is it possible, and if so, how can I create a signal/slot in Qt that is a const reference to a shared_ptr? I want a signal that looks like this:
void signal( shared_ptr<SomeClass> const & )
I know how to do this without a constant reference, that is simply the type shared_ptr<SomeClass> but for efficiency* reasons I'd like to avoid the copying. The same syntax for reference type isn't working:
Q_DECLARE_METATYPE(shared_ptr<SomeClass> const &)
qRegisterMetaType<shared_ptr<SomeClass> const&>();
Many of the standard APIs have QString const & so I assume it is fundamentally possible and I just can't figure out the syntax.
**The biggest problem for performance is not the copying time, but the amount of mutex locking/unlocking as the object is copied to every receiver -- there are lots of them. As multiple threads use the object this introduces a noticeable slow-down/bottleneck. If the shared_ptr does in fact just use an atomic op this cost is also trivial, the general question about const reference in signals remains however.*
So far I have found that I can simply do this:
Q_DECLARE_METATYPE(shared_ptr<SomeClass>)
qRegisterMetaType<shared_ptr<SomeClass> >();
qRegisterMetaType<shared_ptr<SomeClass> >("std::shared_ptr<SomeClass>const&");
I'm trying to verify now whether this truly works correctly. The documentation here isn't clear on what actually happens. It appears that the const reference type in a signal/slot will just be marshalled as a normal shared_ptr<SomeClass>, which is totally okay here. Having some sort of guarantee that this is supposed to work as such would be nice however.
I have the feeling that the simple shared_ptr<SomeClass> version is all that is needed and it is the boost namespace that is interfering with the signals. The second version appears just to register the signal in the global namespace for easier use.
I can confirm, from testing, that the const & part is, as alluded to, completely ignored in queued connections. Each connected slot gets a new copy of the object. This is very unfortunate. :(
Further tests show that the & is used for the slot, but in an unusual fashion. The copy of the object is still created for the queued connection, but if you don't use a reference another copy will be created for the call.
Thus there although every connect will end up copying the data for a queued connection, the references still help a bit. And also if you do have a few signals sent locally (same thread) you may avoid even more copying.
According to one of the answer in this question Argument type for Qt signal and slot, does const reference qualifiers matters? , for a queued connection, the object is copied regardless of how you connect the signal and slot.
Since you are using multiple threads, the connection is queued. If you fear the mutex cost, try using SomeClass * directly.
For the sake of completeness, I believe it should be mentioned that Qt has its own shared pointer implementation, QSharedPointer, which behaves exactly like std::shared_ptr. Eventually you will still need to register your argument type though.

Deletion of objects send by signals, Ownership of objects in signals, Qt

Here, my signal declaration:
signals:
void mySignal(MyClass *);
And how I'm using it:
MyClass *myObject=new myClass();
emit mySignal(myObject);
Here comes my problem: Who is responsible for deletion of myObject:
Sender code, what if it deletes before myObject is used? Dangling Pointer
The slot connected to signal, what if there is no slot or more than one slot which is connected to the signal? Memory Leak or Dangling Pointer
How does Qt manage this situation in its build-in signals? Does it use internal reference counting?
What are your best practices?
You can connect a signal with as many slots as you want so you should make sure that none of those slots are able to do something you would not want them to do with your object:
if you decide to pass a pointer as a parameter then you will be running in the issues you describe, memory management - here nobody can to the work for you as you will have to establish a policy for dealing with allocation/deletion. To some ideas on how to address this see the Memory Management Rules in the COM world.
if you decide to pass a parameter as a reference then you don't have to worry about memory management but only about slots modifying your object in unexpected ways. The ideea is not to pass pointers unless you have to - instead use references if you can.
if you decide to pass a const reference then, depending on your connection type, QT will pass the value of the object for you (see this for some details)
avoid any problems and pass by value :)
See also this question for some thoughts about passing pointers in signals.
For your first question, use QPointer
For your second question,
If I understood clearly, even if you are sending myObject, you still have the reference myObject in the class where you are emitting the signal. Then how will it be a memory leak or a dangling pointer? You can still access the myObject from the emitted class, isn't?
Hope am clear..
Edit :
From your comments I believe you are releasing/deleting the objects in the slots. Now I assume your problem is, what if the (memory releasing) slot gets called once,twice or not called at all.
You can use QPointer for that. From the Qt documentation,
Guarded pointers (QPointer) are useful whenever you need to store a pointer to a QObject that is owned by someone else, and therefore might be destroyed while you still hold a reference to it. You can safely test the pointer for validity.
An example from the Qt documentation itself,
QPointer<QLabel> label = new QLabel;
label->setText("&Status:");
...
if (label)
label->show();
the explanation goes on like this..
If the QLabel is deleted in the meantime, the label variable will hold 0 instead of an invalid address, and the last line will never be executed. Here QLabel will be your MyClass and label is your myObject. And before using it check for Nullity.
At 1): The sender should take care. When sending the signal synchronously (instead of queued), the object is still alive when a receiver receives it. If the receiver needs to store it, only a QPointer would help, but then MyClass needs to derive from QObject, which looks wrong from the context.
Anyway, that is a general lifetime issue, not very signal/slot-specific.
Alternatives: Use a value class and send it via const reference. If MyClass can have subclasses, pass a const QSharedPointer&
About deleteLater: deleteLater() doesn't help here. It would make queued connections any safer, and for direct connections it makes no difference. The one use where deleteLater() comes into play is if the receiver needs to delete the sender. Then one should always use deleteLater(), so the sender can complete what he was doing, which would otherwise crash.
In a word (alright, function name) - deleteLater() :) All QObjects have it. It will mark the object for deletion, and this will then happen on the next event loop update.

Force deletion of slot in boost::signals2

I have found that boost::signals2 uses sort of a lazy deletion of connected slots, which makes it difficult to use connections as something that manages lifetimes of objects. I am looking for a way to force slots to be deleted directly when disconnected. Any ideas on how to work around the problem by designing my code differently are also appreciated!
This is my scenario: I have a Command class responsible for doing something that takes time asynchronously, looking something like this (simplified):
class ActualWorker {
public:
boost::signals2<void ()> OnWorkComplete;
};
class Command : boost::enable_shared_from_this<Command> {
public:
...
void Execute() {
m_WorkerConnection = m_MyWorker.OnWorkDone.connect(boost::bind(&Command::Handle_OnWorkComplete, shared_from_this());
// launch asynchronous work here and return
}
boost::signals2<void ()> OnComplete;
private:
void Handle_OnWorkComplete() {
// get a shared_ptr to ourselves to make sure that we live through
// this function but don't keep ourselves alive if an exception occurs.
shared_ptr<Command> me = shared_from_this();
// Disconnect from the signal, ideally deleting the slot object
m_WorkerConnection.disconnect();
OnComplete();
// the shared_ptr now goes out of scope, ideally deleting this
}
ActualWorker m_MyWorker;
boost::signals2::connection m_WorkerConnection;
};
The class is invoked about like this:
...
boost::shared_ptr<Command> cmd(new Command);
cmd->OnComplete.connect( foo );
cmd->Execute();
// now go do something else, forget all about the cmd variable etcetera.
the Command class keeps itself alive by getting a shared_ptr to itself which is bound to the ActualWorker signal using boost::bind.
When the worker completes, the handler in Command is invoked. Now, since I would like the Command object to be destroyed, I disconnect from the signal as can be seen in the code above. The problem is that the actual slot object is not deleted when disconnected, it is only marked as invalid and then deleted at a later time. This in turn appears to depend on the signal to fire again, which it doesn't do in my case, leading to the slot never expiring. The boost::bind object thus never goes out of scope, holding a shared_ptr to my object that will never get deleted.
I can work around this by binding using the this pointer instead of a shared_ptr and then keeping my object alive using a member shared_ptr which I then release in the handler function, but it kind of makes the design feel a bit overcomplicated. Is there a way to force signals2 to delete the slot when disconnecting? Or is there something else I could do to simplify the design?
Any comments are appreciated!
boost::signals2 does clean up the slots during connect/invoke.
So if all the slots disconnect themselves from the signal, invoking the signal a second time will not call anything but it should clean up the slots.
To answer your comment, yes, invoking the signal again is not safe if there are be other slots connected, as they will be invoked again. In that case I suggest you go the other way around and connect a dummy slot, then disconnect it when your "real" slot is invoked. Connecting another slot will clean up stale connections, so your slot should be released.
Just make sure that you don't keep any references that need freeing in the dummy slot, or you're back where you started.
This is an incredibly annoying aspect of boost::signals2.
The approach I took to resolve it is to store the signal in a scoped_ptr, and when I want to force disconnection of all slots, I delete the signal. This only works in cases when you want to forcefully disconnect all connections to a signal.
Is the behaviour any more strict with a scoped_connection?
So, rather than:
void Execute() {
m_WorkerConnection = m_MyWorker.OnWorkDone.connect(boost::bind
(&Command::Handle_OnWorkComplete, shared_from_this());
// launch asynchronous work here and return
}
...
boost::signals2::connection m_WorkerConnection;
Instead using:
void Execute() {
boost::signals2::scoped_connection m_WorkerConnection
(m_MyWorker.OnWorkDone.connect(boost::bind
(&Command::Handle_OnWorkComplete, shared_from_this()));
// launch asynchronous work here and return
} // connection falls out of scope
(copy-constructed from a boost::signals2::connection)
I've not used any sort of signalling so it's more of a guess than anything else, but following Execute() you wouldn't need to disconnect(), since scoped_connection handles it for you. That's more of a 'simplify the design' rather than actually solving your problem. But it may mean that you can Execute() and then immediately ~Command() (or delete the shared_ptr).
Hope that helps.
EDIT: And by Execute() then immediately ~Command() I obviously mean from outside your Command object. When you construct the Command to execute it, you should then be able to say:
cmd->Execute();
delete cmd;
Or similar.
I ended up doing my own (subset) implementation of a signal, the main requirement being that a slot should be destroyed by a call to connection::disconnect().
The implementation goes along the lines of the signal storing all slots in a map from slot implementation pointer to a shared_ptr for a slot implementation instead of a list/vector, thereby giving quick access to individual slots without having to iterate over all slots. A slot implementation is in my case basically a boost::function.
Connections have a weak_ptr to the internal implementation class for the signal and a weak_ptr to the slot implementation type to allow the signal to go out of scope and to use the slot pointer as the key into the signal map as well as an indication on whether the connection is still active (can't use a raw pointer as that could potentially be reused).
When disconnect is called, both of these weak pointers are converted to shared_ptrs and if both of these succeed, the signal implementation is asked to disconnect the slot given by the pointer. This is done by simple erasing it from the map.
The map is protected by a mutex to allow for multithreaded use. To prevent deadlocks, the mutex is not held while calling the slots, however this means that a slot may be disconnected from a different thread just prior to being called by the signal. This is also the case with regular boost::signals2 and in both of these scenarios one needs to be able to handle a callback from a signal even after one has disconnected.
To simplify the code for when the signal is fired, I am forcing all slots to be disconnected during this. This is different from boost::signals2, that does a copy of the list of slots before calling them in order to handle disconnections/connections while firing the signal.
The above works well for my scenario, where the signal of interest is fired very seldom (and in that case only once) but there are a lot of short-lived connections that otherwise use up a lot of memory even when using the trick outlined in the question.
For other scenarios, I've been able to replace the use of a signal with just a boost::function (thus requiring that there can only be a single connection) or just by sticking with the workaround in the question where the listener itself manages its lifetime.
I stumbled upon the same problem and i really miss some kind of explicit cleanup in the API.
In my scenario i am unloading some plug-in dll's and i have to assure there are no dangling objects (slots) which refer to code (vftables or whatsoever) living in the unloaded dll. Simply disconnecting slots didn't work due to the lazy deletion stuff.
My first workaround was a signal wrapper which tweaks the disconnecting code a little bit:
template <typename Signature>
struct MySignal
{
// ...
template <typename Slot>
void disconnect (Slot&& s)
{
mPrivate.disconnect (forward (s));
// connect/disconnect dummy slot to force cleanup of s
mPrivate.connect (&MySignal::foo);
mPrivate.disconnect (&MySignal::foo);
}
private:
// dummy slot function with matching signature
// ... foo (...)
private:
::boost::signals2::signal<Signature> mPrivate;
};
Unfortunately this didn't work because connect() only does some cleanup. It doesn't guarantee cleanup of all unconnected slots. Signal invocation on the other hand does a full cleanup but a dummy invocation would also be an unacceptable behavioral change (as already mentioned by others).
In the absence of alternatives i ended up in patching the original signal class (Edit: i really would appreciate a built-in solution. this patch was my last resort). My patch is around 10 lines of code and adds a public cleanup_connections() method to signal. My signal wrapper invokes the cleanup at the end of the disconnecting methods. This approach solved my problems and i didn't encounter any performance problems so far.
Edit: Here is my patch for boost 1.5.3
Index: signals2/detail/signal_template.hpp
===================================================================
--- signals2/detail/signal_template.hpp
+++ signals2/detail/signal_template.hpp
## -220,6 +220,15 ##
typedef mpl::bool_<(is_convertible<T, group_type>::value)> is_group;
do_disconnect(slot, is_group());
}
+ void cleanup_connections () const
+ {
+ unique_lock<mutex_type> list_lock(_mutex);
+ if(_shared_state.unique() == false)
+ {
+ _shared_state.reset(new invocation_state(*_shared_state, _shared_state->connection_bodies()));
+ }
+ nolock_cleanup_connections_from(false, _shared_state->connection_bodies().begin());
+ }
// emit signal
result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS))
{
## -690,6 +699,10 ##
{
(*_pimpl).disconnect(slot);
}
+ void cleanup_connections ()
+ {
+ (*_pimpl).cleanup_connections();
+ }
result_type operator ()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS))
{
return (*_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS));