2013-08-22 25 views
6

Czy to ważne, aby zadzwonićPodłączanie sygnałów do gniazd z mniejszymi parametrami dozwolonymi w Qt?

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

bez params? Wydaje się działać (nie zgłoszono wyjątku środowiska wykonawczego), ale nie mogę znaleźć odwołania w dokumentach. Wszystko, co znalazłem, to to, że jest to możliwe, jeśli someslot ma domyślny parametr. Jest on ważny w tym przypadku. Jednak moja komenda someslot nie ma tego samego zestawu parametrów co domyślny (tutaj nie ma parametru).

Czy możliwe jest podłączenie sygnałów do gniazd o mniejszej liczbie parametrów?

Odpowiedz

9

Tak, w porządku. W dokumentacji Signals & Slots znajduje się krótkie zdanie:

[...] Podpis sygnału musi być zgodny z sygnaturą gniazda odbiorczego. (W rzeczywistości gniazdo mogą mieć krótszy niż podpis sygnału, które otrzymuje, ponieważ może ignorować dodatkowe argumenty.) [...]

Jest nawet przykładem tak dalej w dół strony, gdzie są domyślne argumenty wyjaśnione.

+0

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

+0

@Kamajii: inwokacje typu slot nie są bezpośrednimi wywołaniami funkcji. Nie ma UB, Qt dba o prawidłowe wywołanie funkcji. – Mat

+0

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

1

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[].