Question

Is it valid to call

QObject::connect(a, SIGNAL(somesig(someparam)), b, SLOT(someslot()));

without params? It seems to work (no runtime exception thrown) but I can't find a reference in the docs. All I found is that this is possible if someslot has a default parameter. It is valid in this case. But my method someslot has not the same parameter set as default (no parameter here in the example).

So it seems to be possible to wire signals to slots with less parameters?

Was it helpful?

Solution

Yes, that's fine. There's a short sentence about it in the Signals & Slots documentation:

[...] The signature of a signal must match the signature of the receiving slot. (In fact a slot may have a shorter signature than the signal it receives because it can ignore extra arguments.) [...]

There's even an example like that further down the page where default arguments are explained.

OTHER TIPS

In terms of standard C++ the Qt solution works out as well.

Emitting a signal is done by calling a method:

emit someSignal(3.14);

The emit keyword actually resolves to an empty #define, so the line above just calls the method someSignal with the given arguments. The method may have been declared inside a QObject-derived class like so:

class SomeObject: public QObject {
    Q_OBJECT
public slots:
    void firstSlot() { /* implementation */ }
    void secondSlot(double) {  /* implementation */ }

signals:
    void someSignal(double);  /* no implementation here */
};

This should seem familiar to you, yet you might have wondered where the actual implementation of your signals comes from. As you might guess, this is where Qt's meta object compiler (MOC) kicks in. For every method declared within the signals section it provides in its generated source an implementation that roughly looks like this:

void SomeObject::someSignal(double _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

The interesting part is the void *_a[] vector that is filled with pointers to the arguments passed to the signal. Nothing in particular here.

The argument vector is passed to QMetaObject::activate, which in turn does some thread-safety checks and other housekeeping and then starts calling the slots that have been connected to the signal, if any. As the signal-to-slot connections are resolved at runtime (the way that connect() works), a little help from MOC is required again. In particular, MOC also generates a qt_static_metacall() implementation your class:

void SomeObject::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        SomeObject *_t = static_cast<SomeObject *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->firstSlot(); break;
        case 1: _t->secondSlot((*reinterpret_cast< double(*)>(_a[1]))); break;
        default: ;
        }
    } /* some more magic */
}

As you can see, this method contains the other end to resolve the void *_a[] vector from before to a function call. Also you can see that there is no variadic argument list (using ellipsis, ...) or other questionable trickery involved.

So to enlighten the original question: When, e.g. someSignal(double) is connected to the secondSlot(double) that matches the signal's signature, the call resolves to case 1 in the qt_static_metacall and it just passes the argument as expected.

When connecting the signal to the firstSlot() that has fewer arguments than the signal, the call resolves to case 0 and firstSlot() gets called without arguments. The argument that has been passed to the signal simply stays untouched in its void *_a[] vector.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top