Pod względem standardowego C++ rozwiązanie Qt działa również.
emitowania sygnału odbywa się poprzez wywołanie metody:
emit someSignal(3.14);
emit
kluczowe rzeczywiście postanawia pustym #define
, więc linia powyżej prostu wywołuje metodę someSignal
z podanych argumentów. Sposób może zostały zadeklarowane wewnątrz QObject
pochodzące z A klasy tak:
class SomeObject: public QObject {
Q_OBJECT
public slots:
void firstSlot() { /* implementation */ }
void secondSlot(double) { /* implementation */ }
signals:
void someSignal(double); /* no implementation here */
};
ten powinien wyglądać znajomo, ale można się zastanawiać, gdzie faktyczna realizacja twoich sygnałów pochodzi. Jak można się domyślać, to gdzie Qt meta obiekt kompilator (MOC) w rzutach Dla każdej metody deklarowanej w sekcji signals
to przewiduje w wygenerowanym źródła realizacja że z grubsza wygląda tak:.
void SomeObject::someSignal(double _t1)
{
void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
Interesującą częścią jest wektor void *_a[]
wypełniony wskaźnikami do argumentów przekazywanych do sygnału. Nic szczególnego tutaj.
Wektor argumentów jest przekazywany do QMetaObject::activate
, który z kolei wykonuje pewne kontrole bezpieczeństwa wątków i inne czynności porządkowe, a następnie rozpoczyna wywoływanie gniazd, które zostały podłączone do sygnału, jeśli takie istnieją. Ponieważ połączenia sygnał-gniazdo są rozwiązywane w czasie wykonywania (sposób, w jaki działa connect()
), wymagana jest niewielka pomoc z MOC. W szczególności, MOC generuje również qt_static_metacall()
wdrażania swoją klasę:
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 */
}
Jak widać, metoda ta zawiera drugi koniec do rozwiązania void *_a[]
wektor sprzed do wywołania funkcji. Widać również, że nie ma listy argumentów variadic (używając ellipsis, ...
) lub innych podejrzanych sztuczek.
Aby więc oświecić oryginalne pytanie: Kiedy, np.someSignal(double)
jest połączony z secondSlot(double)
, który pasuje do sygnatury sygnału, wywołanie odpowiada case 1
w qt_static_metacall
i po prostu przekazuje argument zgodnie z oczekiwaniami.
Po podłączeniu sygnału do firstSlot()
, który ma mniej argumentów niż sygnał, wywołanie zostaje zmienione na case 0
i firstSlot()
bez wywoływania argumentów. Argument przekazany do sygnału pozostaje nietknięty w jego wektorze void *_a[]
.
Czy jest to również zgodne ze standardem C++ lub czy jest to funkcja gwarantowana do pracy na Qt? Przynajmniej w zwykłym C, prowadzi to do UB przez ISO/IEC 9899: TC2 §6.5.2.2p6. – Kamajii
@Kamajii: inwokacje typu slot nie są bezpośrednimi wywołaniami funkcji. Nie ma UB, Qt dba o prawidłowe wywołanie funkcji. – Mat
Oczywiście są inwokacje wrzutowe tylko zwykłe połączenia (+ -połączone połączenia itp.). Kiedy emisja sygnału w końcu dociera do automatycznie generowanego (mocnego) elementu 'qt_static_metacall', pojawia się problem. – Kamajii