Подключение сигналов в слоты с меньшими параметрами, разрешенными в Qt?

Действительно ли для вызова

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

без параметров? Кажется, что он работает (исключение исключений во время выполнения), но я не могу найти ссылку в документах. Все, что я нашел, это то, что это возможно, если у someslot есть параметр по умолчанию. Это справедливо в этом случае. Но мой метод someslot не имеет того же параметра, что и значение по умолчанию (здесь нет параметра).

Таким образом, кажется, что можно прокладывать сигналы в слоты с меньшим количеством параметров?

2 ответа

Да, это хорошо. Там короткое предложение об этом в документации Signals and Slots:

[...] Сигнатура сигнала должна соответствовать сигнатуре принимающего слота. (На самом деле слот может иметь более короткую подпись, чем сигнал, который он получает, поскольку он может игнорировать дополнительные аргументы.) [...]

Там даже пример, подобный этому, далее на странице, где объясняются аргументы по умолчанию.


В стандартном С++ также разрабатывается решение Qt.

Выдача сигнала осуществляется путем вызова метода:

emit someSignal(3.14);

Ключевое слово emit фактически разрешает пустой #define, поэтому строка выше просто вызывает метод someSignal с заданными аргументами. Возможно, этот метод был объявлен внутри класса QObject -derived, например:

class SomeObject: public QObject {
 Q_OBJECT
public slots:
 void firstSlot() { /* implementation */ }
 void secondSlot(******) { /* implementation */ }
signals:
 void someSignal(******); /* no implementation here */
};

Это должно показаться вам знакомым, но вы, возможно, задавались вопросом, откуда происходит реальная реализация ваших сигналов. Как вы могли догадаться, здесь встречается компилятор мета-объектов Qt (MOC). Для каждого метода, объявленного в разделе signals, он предоставляет в своем сгенерированном источнике реализацию, которая примерно выглядит следующим образом:

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

Интересной частью является вектор void *_a[], который заполняется указателями на аргументы, переданные сигналу. Здесь нет ничего особенного.

Вектор аргумента передается на QMetaObject::activate, который, в свою очередь, выполняет некоторые проверки безопасности потока и другое домашнее хозяйство, а затем начинает вызывать слоты, которые были подключены к сигналу, если таковые имеются. Поскольку соединения "сигнал-слот" разрешаются во время выполнения (способ, которым работает connect()), требуется небольшая помощь от MOC. В частности, 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< ******(*)>(_a[1]))); break;
 default: ;
 }
 } /* some more magic */
}
</someobject>

Как вы можете видеть, этот метод содержит другой конец для разрешения вектора void *_a[] от перед вызовом функции. Также вы можете видеть, что нет вариационного списка аргументов (с использованием эллипса, ...) или других сомнительных обманов.

Итак, чтобы просветить исходный вопрос: когда, например, someSignal(******) подключен к secondSlot(******), который соответствует сигнальной сигнатуре, вызов разрешается case 1 в qt_static_metacall, и он просто передает аргумент, как ожидалось.

При подключении сигнала к firstSlot(), у которого меньше аргументов, чем сигнал, вызов разрешается до case 0 и firstSlot() вызывается без аргументов. Аргумент, который был передан сигналу, просто остается нетронутым в его векторе void *_a[].

licensed under cc by-sa 3.0 with attribution.