2013-08-22 11 views
6

E 'valida per chiamareCollegare i segnali agli slot con meno parametri consentiti in Qt?

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

senza params? Sembra funzionare (nessuna eccezione di runtime generata) ma non riesco a trovare un riferimento nei documenti. Tutto quello che ho trovato è che questo è possibile se someslot ha un parametro predefinito. È valido in questo caso. Ma il mio metodo someslot non ha lo stesso parametro impostato come predefinito (nessun parametro qui nell'esempio).

Quindi sembra possibile collegare i segnali alle slot con meno parametri?

risposta

9

Sì, va bene. C'è una breve frase a riguardo nella documentazione Signals & Slots:

[...] La firma di un segnale deve corrispondere alla firma dello slot di ricezione. (Infatti uno slot può avere una firma più breve del segnale che riceve, perché può ignorare argomenti extra.) [...]

C'è anche un esempio del genere, più in basso nella pagina in cui gli argomenti di default sono ha spiegato.

+0

Questo è anche conforme a qualsiasi standard C++ o è una funzione garantita per Qt? Almeno in bianco C, questo porta a UB tramite ISO/IEC 9899: TC2 §6.5.2.2p6. – Kamajii

+0

@Kamajii: le chiamate di slot non sono chiamate di funzione diretta. Non c'è UB, Qt si occupa di chiamare correttamente la funzione. – Mat

+0

Ovviamente le invocazioni di slot sono solo chiamate regolari (connessioni + -queued, ecc.). Quando l'emissione del segnale raggiunge finalmente il membro autogenerato (moc) 'qt_static_metacall', sorge il problema. – Kamajii

1

In termini di standard C++, la soluzione Qt funziona altrettanto bene.

emettere un segnale viene fatto chiamando il metodo:

emit someSignal(3.14); 

Il emit parola effettivamente risolve ad un vuoto #define, quindi la linea sopra invoca semplicemente il metodo someSignal con gli argomenti dati. Il metodo potrebbe essere stata dichiarata all'interno di una classe -derived QObject in questo modo:

class SomeObject: public QObject { 
    Q_OBJECT 
public slots: 
    void firstSlot() { /* implementation */ } 
    void secondSlot(double) { /* implementation */ } 

signals: 
    void someSignal(double); /* no implementation here */ 
}; 

Questo dovrebbe sembrare familiare a voi, ma si potrebbe avere chiesti dove l'effettiva attuazione dei vostri segnali proviene. Come si può immaginare, questo è dove meta oggetto compilatore Qt (MOC) entra in gioco Per ogni metodo dichiarato all'interno della sezione signals fornisce nella sua sorgente generato un implementazione che assomiglia più o meno in questo modo:.

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

La parte interessante è il vettore void *_a[] riempito con puntatori agli argomenti passati al segnale. Niente di particolare qui.

Il vettore dell'argomento viene passato a QMetaObject::activate, che a sua volta esegue alcuni controlli di sicurezza dei thread e altre operazioni di manutenzione e quindi inizia a chiamare gli slot che sono stati collegati al segnale, se presenti. Poiché le connessioni segnale-area sono risolte in fase di esecuzione (il modo in cui funziona connect()), è necessario un nuovo aiuto da parte di MOC. In particolare, MOC genera anche un qt_static_metacall() implementazione tua classe:

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 */ 
} 

Come si può vedere, questo metodo contiene l'altra estremità per risolvere il void *_a[] vettore da prima a una chiamata di funzione. Inoltre puoi vedere che non c'è nessun elenco di argomenti variadici (usando i puntini di sospensione, ...) o altri trucchi discutibili coinvolti.

Quindi, per chiarire la domanda originale: quando, ad es.someSignal(double) è collegato allo secondSlot(double) che corrisponde alla firma del segnale, la chiamata si risolve in case 1 nello qt_static_metacall e passa semplicemente l'argomento come previsto.

Quando si collega il segnale a firstSlot() con meno argomenti rispetto al segnale, la chiamata viene risolta in case 0 e firstSlot() viene richiamato senza argomenti. L'argomento che è stato passato al segnale rimane semplicemente intatto nel suo vettore void *_a[].