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.
Related
Suppose I have a class that looks like this:
class Something : QObject {
Q_PROPERTY(int something READ getSomething NOTIFY somethingChanged)
// ...
signals:
void somethingChanged();
}
According to the documentation, declaring somethingChanged as void somethingChanged() and void somethingChanged(int) (note the parameter) are both valid. Why would I want to do it one way over the other?
Emitting the value allows you to use that value without having a reference to the object it is a property of. It is how things are usually done in the C++ API, this saves you from manually having to keep references to objects, so that you can read in the value upon a change notification. Note that even then, you can use QObject::sender() to find the sender object if necessary, but emitting the value is just more straightforward.
In QML what you end up using most of the time is bindings, which are very fast and powerful, and involve having references to the objects, and change notifications cause the binding expressions which reference the properties to automatically reevaluate. Thus it is not necessary to emit the actual value.
There is nothing stopping you from having the best of both worlds. A notification signal that emits the new value appears to work just fine with QML bindings. So if for some reason you need to emit a value, don't shy away from it, it will not jeopardize QML compatibility.
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.
Automatic disconnection
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:
When someone explicitely calls disconnect;
When sender is deleted;
When 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:
When someone explicitely calls disconnect;
When 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
when the 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.
Running a functor in a specific thread/event loop
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.
I read that QT applies signature normalization process on the signal/slot mechanism. MOC generator basically removes the const reference qualifiers from signals/slots and just pass them by value.
I have a class which generates a big data structure called BIG_DATA so frequently and some other classes need to catch this data structure every time it is emitted.
struct BIG_DATA
{
// very big data
};
class DataGenerator
{
// some methods which generate BIG_DATA
signals:
void data_updated(const BIG_DATA &);
};
What i do :
connect(&data_generator_object, SIGNAL(data_updated(const BIG_DATA &)), this, SLOT(catch_new_data(const BIG_DATA &)));
What QT does :
connect(&data_generator_object, SIGNAL(data_updated(BIG_DATA)), this, SLOT(catch_new_data(BIG_DATA)));
So, what is the benefit of removing const reference qualifiers here ? What am i going to do with the overhead of copying the whole BIG_DATA to many clients of data_updated signal ?
Seems like the best way is to use a pointer to the generated BIG_DATA object, if QT doesn't attempt to remove the pointer signature too.
Signature normalization is used only to identify signals and slots. That is, if you want to tell connect() which signal or slot to use, you need to pass normalized signature there. But your signal's and slot's signatures remain untouched. If you use direct connection (which is default for single-threaded program), your object will not be copied.
If you are using queued connections, your structure is copied anyway (see this).
Now, if you use use normalized signals, you can minimize performance hit when you are using connect (see this) :
Lookup is first attempted with the signature as-is, and only if that fails is QMetaObject::normalizedSignature() called.
That means, when using non-normalised signal/slot signatures, you not only pay for a strcpy(), but also for a doomed-to-fail first lookup attempt. Sure, connects are usually done during startup, and a profiler won’t show you, but using non-normalised signatures is hereby firmly put into the realm of premature pessimisation.
However, the performance hit is only when using connect, not when sending signals. And the connections are made usually only once. Therefore I wouldn't worry too much.
In order to avoid the structure copy, use references.
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.
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.