c++qtreturn-valuesignals-slotsboost-signals

Can Qt signals return a value?


Boost.Signals allows various strategies of using the return values of slots to form the return value of the signal. E.g. adding them, forming a vector out of them, or returning the last one.

The common wisdom (expressed in the Qt documentation) is that no such thing is possible with Qt signals.

However, when I run the moc on the following class definition:

class Object : public QObject {
    Q_OBJECT
public:
    explicit Object( QObject * parent=0 )
        : QObject( parent ) {}

public Q_SLOTS:
    void voidSlot();
    int intSlot();

Q_SIGNALS:
    void voidSignal();
    int intSignal();
};

Not only doesn't moc complain about the signal with the non-void return type, it seems to actively implement it in such a way as to allow a return value to pass:

// SIGNAL 1
int Object::intSignal()
{
    int _t0;
    void *_a[] = { const_cast<void*>(reinterpret_cast<const void*>(&_t0)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
    return _t0;
}

So: according to the docs, this thing isn't possible. Then what is moc doing here?

Slots can have return values, so can we connect a slot with a return value to a signal with a return value now? May that be possible, after all? If so, is it useful?

I'm not asking for workarounds.

It obviously isn't useful in Qt::QueuedConnection mode (neither is the QPrintPreviewWidget API, though, and still it exists and is useful). But what about Qt::DirectConnection and Qt::BlockingQueuedConnection (or Qt::AutoConnection, when it resolves to Qt::DirectConnection).


Solution

  • It seems this is possible. I was able to emit a signal, and receive value from the slot the signal was connected to. But, the problem was that it only returned the last return value from the multiple connected slots:

    Here's a simple class definition (main.cpp):

    #include <QObject>
    #include <QDebug>
    
    class TestClass : public QObject
    {
        Q_OBJECT
    public:
        TestClass();
    
    Q_SIGNALS:
        QString testSignal();
    
    public Q_SLOTS:
        QString testSlot1() {
            return QLatin1String("testSlot1");
        }
        QString testSlot2() {
            return QLatin1String("testSlot2");
        }
    };
    
    TestClass::TestClass() {
        connect(this, SIGNAL(testSignal()), this, SLOT(testSlot1()));
        connect(this, SIGNAL(testSignal()), this, SLOT(testSlot2()));
    
        QString a = emit testSignal();
        qDebug() << a;
    }
    
    int main() {
        TestClass a;
    }
    
    #include "main.moc"
    

    When main runs, it constructs one of the test classes. The constructor wires up two slots to the testSignal signal, and then emits the signal. It captures the return value from the slot(s) invoked.

    Unfortunately, you only get the last return value. If you evaluate the code above, you'll get: "testSlot2", the last return value from the connected slots of the signal.

    Here's why. Qt Signals are a syntax sugared interface to the signaling pattern. Slots are the recipients of a signal. In a direct connected signal-slot relationship, you could think of it similar to (pseudo-code):

    foreach slot in connectedSlotsForSignal(signal):
        value = invoke slot with parameters from signal
    return value
    

    Obviously the moc does a little more to help in this process (rudimentary type checking, etc), but this helps paint the picture.