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.