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.
Related
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.
I am wondering why we need both std::promise and std::future ? why c++11 standard divided get and set_value into two separate classes std::future and std::promise?
In the answer of this post, it mentioned that :
The reason it is separated into these two separate "interfaces" is to
hide the "write/set" functionality from the "consumer/reader".
I don't understand the benefit of hiding here. But isn't it simpler if we have only one class "future"? For example: promise.set_value can be replaced by future.set_value.
The problem that promise/future exist to solve is to shepherd a value from one thread to another. It may also transfer an exception instead.
So the source thread must have some object that it can talk to, in order to send the desired value to the other thread. Alright... who owns that object? If the source has a pointer to something that the destination thread owns, how does the source know if the destination thread has deleted the object? Maybe the destination thread no longer cares about the value; maybe something changed such that it decided to just drop your thread on the floor and forget about it.
That's entirely legitimate code in some cases.
So now the question becomes why the source doesn't own the promise and simply give the destination a pointer/reference to it? Well, there's a good reason for that: the promise is owned by the source thread. Once the source thread terminates, the promise will be destroyed. Thus leaving the destination thread with a reference to a destroyed promise.
Oops.
Therefore, the only viable solution is to have two full-fledged objects: one for the source and one for the destination. These objects share ownership of the value that gets transferred. Of course, that doesn't mean that they couldn't be the same type; you could have something like shared_ptr<promise> or somesuch. After all, promise/future must have some shared storage of some sort internally, correct?
However, consider the interface of promise/future as they currently stand.
promise is non-copyable. You can move it, but you can't copy it. future is also non-copyable, but a future can become a shared_future that is copyable. So you can have multiple destinations, but only one source.
promise can only set the value; it can't even get it back. future can only get the value; it cannot set it. Therefore, you have an asymmetric interface, which is entirely appropriate to this use case. You don't want the destination to be able to set the value and the source to be able to retrieve it. That's backwards code logic.
So that's why you want two objects. You have an asymmetric interface, and that's best handled with two related but separate types and objects.
I would think of a promise/future as an asynchronous queue (that's only intended to hold a single value).
The future is the read end of the queue. The promise is the write end of the queue.
The use of the two is normally distinct: the producer normally just writes to the "queue", and the consume just reads from it. Although, as you've noted, it's possible for a producer to read the value, there's rarely much reason for it to do that, so optimizing that particular operation is rarely seen as much of a priority.
In the usual scheme of things, the producer produces the value, and puts it into the promise. The consumer gets the value from the future. Each "client" uses one simple interface dedicated exclusively to one simple task, so it's easier to design and document the code, as well as ensuring that (for example) the consumer code doesn't mess with something related to producing the value (or vice versa). Yes, it's possible to do that, but enough extra work that it's fairly unlikely to happen by accident.
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.
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.
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.