9

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.*

14
  • Doesn't passing a shared_ptr as a const ref negate the point of having a shared_ptr in the first place? Just pass a const ref / pointer to the object in question. Commented Jan 26, 2011 at 18:21
  • 1
    @messenger, the const reference is a optimization to avoid copying the shared_ptr when calling functions. It is a fairly common pattern. If you always pass by value (signals or normal functions) shared_ptr's become quite costly in comparison to normal pointers. Normal pointers can not be used here as the lifetime of the object from the "emit" side is not guaranteed. Commented Jan 26, 2011 at 18:35
  • @edA-qa mort-ora-y: Does the boost shared_ptr use a mutex??? Why that??? Aren't atomic increment/decrement all you need? Commented Jan 26, 2011 at 18:59
  • @edA-qa, if the lifetime isn't guaranteed, then why did you try to use references instead of copying a pointer object? Wouldn't you just get a dangling reference if the object was destroyed on the other end? Commented Jan 26, 2011 at 19:10
  • 1
    @edA-qa, oh, now I get it. You wanted to make just one copy that would get passed by reference to each connected slot. Now it's a good idea, but unfortunately Qt does things in a different way. I guess you could get around it by first passing a copy of your pointer to some proxy object living in the receiving thread, which would in turn signal all the receivers using direct connections, but I'm afraid that double signaling overhead would defeat the very point of avoiding unnecessary copying. Commented Jan 27, 2011 at 6:32

3 Answers 3

8

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.

Sign up to request clarification or add additional context in comments.

4 Comments

1) You should be able to omit the string argument from qRegisterMetaType() alltogether. 2) const ref is never needed for signal/slots and the metatype stuff, just the plain type. Also const ref. and passing by value are equivalent to the signal/slot mechanism: You can connect a signal void signal( const T& ) via SIGNAL(signal(const T&)) or SIGNAL(signal(T)), the signature is normalized to "signal(T)" anyway. When marshalled e.g. for a queued connection, the value is stored by copying, not by storing the const ref.
@Frank, for the queuing I'm fine with a copy being taken. I'm concerned with the dispatching to receivers. If I connect many slots to a signal with a reference, will a reference to the same object be used in all cases (that is, all slots within the same thread)?
qa, I just checked the sources and it looks like Qt creates a copy for each queued connection, that is, for each receiver. If you wish, check QMetaObject::activate() which loops through senders and QMetaObject::queued_activate() which does actual copying, all in the qobject.cpp.
You must add the std:: namespace inside the string, since using namespace doesn't affect that.
2

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.

1 Comment

Copying across the threads is totally okay so long as it is just using the copy constructor (or operator=). Its within one thread I don't want copying to all the receivers.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.