Подключение сигналов к слотам с меньшим количеством параметров разрешено в Qt?
Это действительно, чтобы позвонить
QObject::connect(a, SIGNAL(somesig(someparam)), b, SLOT(someslot()));
без параметров? Кажется, это работает (без исключений во время выполнения), но я не могу найти ссылку в документах. Все, что я обнаружил, это то, что это возможно, если у someslot есть параметр по умолчанию. Это действительно в этом случае. Но мой метод someslot имеет не тот же набор параметров, что и по умолчанию (здесь нет параметров в примере).
Таким образом, кажется возможным подключить сигналы к слотам с меньшими параметрами?
2 ответа
Да, это нормально. В документации Signals & Slots об этом есть короткое предложение:
[...] Подпись сигнала должна соответствовать подписи принимающего слота. (На самом деле слот может иметь более короткую сигнатуру, чем сигнал, который он получает, потому что он может игнорировать дополнительные аргументы.) [...]
Внизу страницы есть даже такой пример, где объясняются аргументы по умолчанию.
С точки зрения стандарта C++ решение Qt также работает.
Передача сигнала осуществляется путем вызова метода:
emit someSignal(3.14);
emit
Ключевое слово на самом деле разрешается до пустого #define
так что строка выше просто вызывает метод someSignal
с заданными аргументами. Метод, возможно, был объявлен внутри QObject
класс получился так:
class SomeObject: public QObject {
Q_OBJECT
public slots:
void firstSlot() { /* implementation */ }
void secondSlot(double) { /* implementation */ }
signals:
void someSignal(double); /* no implementation here */
};
Это должно показаться вам знакомым, но вы, возможно, задавались вопросом, откуда происходит фактическая реализация ваших сигналов. Как вы можете догадаться, именно здесь начинается мета-компилятор (MOC) Qt. Для каждого метода, объявленного в signals
раздел, который он предоставляет в своем сгенерированном источнике реализацию, которая выглядит примерно так:
void SomeObject::someSignal(double _t1)
{
void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
Интересная часть void *_a[]
вектор, который заполнен указателями на аргументы, передаваемые сигналу. Ничего особенного здесь нет.
Вектор аргумента передается QMetaObject::activate
, который, в свою очередь, выполняет некоторые проверки безопасности потока и другую служебную работу, а затем начинает вызывать слоты, которые были подключены к сигналу, если таковые имеются. Поскольку соединения сигнал-слот разрешаются во время выполнения (способ, которым connect()
работает), небольшая помощь от MOC требуется снова. В частности, МОС также генерирует qt_static_metacall()
Реализация вашего класса:
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 */
}
Как видите, этот метод содержит другой конец для разрешения void *_a[]
вектор до вызова функции. Также вы можете видеть, что нет списка переменных аргументов (используя многоточие, ...
) или другой сомнительный обман.
Итак, чтобы прояснить исходный вопрос: когда, например, someSignal(double)
подключен к secondSlot(double)
что соответствует сигнатуре сигнала, вызов разрешается в case 1
в qt_static_metacall
и он просто передает аргумент, как и ожидалось.
При подключении сигнала к firstSlot()
который имеет меньше аргументов, чем сигнал, вызов разрешается case 0
а также firstSlot()
вызывается без аргументов. Аргумент, который был передан сигналу, просто остается нетронутым в его void *_a[]
вектор.