7
votes

Say I have a Qt application where I have something like this:

connect(A, SIGNAL(a()), B, SLOT(b1()));
connect(A, SIGNAL(a()), B, SLOT(b2()));
...
void B::b1() {
  A->disconnect();
}

If a() is emitted, will the slot B::b2() still be called after disconnecting all slots from A in B::b1(), since they both respond to the same signal? Assume that both objects live in the same thread, so we have a direct connection.

I know that the disconnect() disconnects all signal connections from A, but I am not sure if the emit just schedules both the b1 and b2 slots to be called and then calls them, so that a change to the connections has no effect until the two slots (and therefore the emit) return. So it could be implemented like:

emit:
  make temprorary copy of signal's slot table
  foreach element in temporary slot table: call

disconnect:
  clear signal's slot table

Otherwise you could run into datastructure integrity problems.

2
As a vaguely related note, Qt's foreach actually makes a temporary copy of the list is operates on. If it just does a Qt foreach over the table, it would not have data integrity problems.cgmb
If it would, that would be exactly my solution and then the second slot would be called. But I suppose the meta object system is a bit more compilcated.Christian Rau

2 Answers

5
votes

A quick experiment shows that the second slot is not called.

However, I'm not sure if this is something that Qt promises.

Qt does document that certain checks are made during the iteration of connected receivers, but it's but that's pretty loose and non-specific (and I haven't come across anything better in my short search); from http://doc.qt.io/qt-5/signalsandslots.html:

This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission)

So they loosely document that certain checks are made while a signal is emitted, such as whether a receiver has been destroyed. I think checking whether the connection has been disconnected seems like a similar kind of situation, but it would be nice if that were explicitly documented.

Since it sounds like this will be a problem for you (you want all the signals to get through, right?), you may be able to work around this problem by ensuring that the signal to slot b1() is connected last. Qt does promise that slots will be called by a signal in connection order, so if you arrange for the slot that does the disconnect to be the last one, all the other slots will be seen.

I don't know how easy this might be to arrange, but it also seems kind of strange that an object would disconnect everything from inside another object's slot function (so there's probably some other refactoring that can solve this problem as well).

If none of that is acceptable, it wouldn't be too hard to come up with a proxy object for A's signals that has the behavior you need. There would be a single:

connect(A, SIGNAL(a()), proxy_obj, SLOT(proxy_slot()));

to connect A's signal to the proxy, then B can connect to the proxy's signal (which the proxy would be emit when the proxy's slot got called):

connect(proxy_obj, SIGNAL(a()), B, SLOT(b1()));
connect(proxy_obj, SIGNAL(a()), B, SLOT(b2()));

Since A's disconnect will not affect the connections that the proxy has to B, the proxy will ensure that all of B's slots get called when signal A->a() is emitted.

class A_proxy : public QObject
{
    Q_OBJECT
public slots:
    void proxy_slot() {
        emit a();
    }

signals:
    void a();
};
-3
votes

disconnect reference page answers your question.

It depends on how you call it, and the way you call it (without any parameter), it will disconnect all signals on object A, therefore the slot B:b2 will not be called after disconnect.