2009-09-10 10 views
22

Questa domanda è già stata posta in questo forum ma non capisco il concetto.Come vengono implementati il ​​segnale e gli slot sotto il cofano?

Stavo leggendo in giro e sembra che il segnale e gli slot siano implementati usando i puntatori di funzione, il segnale è una grande funzione che al suo interno chiama tutti gli slot collegati (puntatori di funzione). È corretto? E qual è il ruolo dei file moc generati nell'intera storia? Non capisco come la funzione di segnale sappia quali slot chiamare i quali slot sono collegati a questo segnale.

Grazie per il vostro tempo

+0

Nizza domanda. Vedi anche: http://stackoverflow.com/questions/1413777/how-boost-implements-signals-and-slots – elcuco

risposta

15

Qt implementa queste cose in un modo che assomiglia a linguaggi interpretati. Cioè costruisce tabelle di simboli che mappano i nomi dei segnali ai puntatori di funzione, li mantiene e cerca il puntatore della funzione per nome della funzione dove necessario.

Ogni volta che si emettere un segnale, vale a dire scrivere

emit something(); 

effettivamente chiamare la funzione something(), che automaticamente generato dal compilatore oggetto di meta e collocato in un file *.moc. All'interno di questa funzione viene controllato a quali slot questo segnale è collegato al momento e le appropriate funzioni dello slot (che sono state implementate nelle proprie fonti) vengono chiamate in sequenza tramite le tabelle dei simboli (nel modo descritto sopra). E emit, come altre parole chiave specifiche per Qt, vengono semplicemente scartati dal preprocessore C++ dopo che sono stati generati *.moc. Infatti, in una delle intestazioni di Qt (qobjectdefs.h), esistono tali linee:

#define slots 
#define signals protected 
#define emit 

funzione di collegamento (connect) solo modifica le tabelle dei simboli mantenute entro *.moc file e gli argomenti passati ad esso (con SIGNAL() e ` Macro SLOT) sono anche pre-elaborati per abbinare le tabelle.

Questa è l'idea generale. Nella sua altra risposta, ジョージ ci fornisce i collegamenti to trolltech mailing list e another SO question su questo argomento.

+4

Fondamentalmente corretto, è possibile impostare un punto di interruzione all'emit e quindi passare attraverso il processo di segnalazione. Mentre di solito vengono consegnati direttamente, i segnali possono essere accodati, questo è necessario quando si vogliono collegare due oggetti in thread diversi –

5

Penso che dovrei aggiungere quanto segue.

C'è another linked question - e c'è a very good article che può essere considerato un'espansione abbastanza dettagliata al suo answer; here is this article again, con l'evidenziazione della sintassi del codice migliorata (sebbene ancora non perfetta).

Ecco il mio breve racconto di esso, che possono essere soggette a errori)

Fondamentalmente, quando inseriamo la macro Q_OBJECT nella nostra definizione di classe, il preprocessore espande a una statica dichiarazione QMetaObject esempio, uno che sarebbe condivisibile da tutte le istanze della stessa classe:

class ClassName : public QObject // our class definition 
{ 
    static const QMetaObject staticMetaObject; // <--= Q_OBJECT results to this 

    // ... signal and slots definitions, other stuff ... 

} 

questo esempio, a sua volta, all'inizializzazione memorizzerà i firme ("methodname(argtype1,argtype2)") dei segnali e le fessure, ciò che permetteranno di attuare la 01.235.806,699 milachiamata, che restituisce, beh, l'indice di metodo da esso è stringa di firma:

struct Q_CORE_EXPORT QMetaObject 
{  
    // ... skip ... 
    int indexOfMethod(const char *method) const; 
    // ... skip ... 
    static void activate(QObject *sender, int signal_index, void **argv); 
    // ... skip ... 
    struct { // private data 
     const QMetaObject *superdata; // links to the parent class, I guess 
     const char *stringdata; // basically, "string1\0string2\0..." that contains signatures and other names 
     const uint *data; // the indices for the strings in stringdata and other stuff (e.g. flags) 
     // skip 
    } d; 
}; 

Ora, quando il moc crea il file moc_headername.cpp per la classe di intestazione Qt headername.h, mette lì le corde di firma e altri dati che sono necessari per inizializzazione corretta della struttura d e quindi scrive il codice di inizializzazione per il singleton staticMetaObject utilizzando questi dati.

Un'altra cosa importante che fa è la generazione del codice per il metodo dell'oggetto qt_metacall(), che prende il metodo id di un oggetto e un array di puntatori argomento e chiama il metodo tramite un lungo switch simili:

int ClassName::qt_metacall(..., int _id, void **_args) 
{ 
    // ... skip ... 
    switch (_id) { 
     case 0: signalOrSlotMethod1(_args[1], _args[2]); break; // for a method with two args 
     case 1: signalOrSlotMethod2(_args[1]); break; // for a method with a single argument 
     // ... etc ... 
    } 
    // ... skip ... 
} 

Ultimo, per ogni segnale moc genera un'implementazione, che contiene un QMetaObject::activate() chiamata:

void ClassName::signalName(argtype1 arg1, argtype2 arg2, /* ... */) 
{ 
    void *_args[] = { 0, // this entry stands for the return value 
         &arg1, // actually, there's a (void*) type conversion 
         &arg2, // in the C++ style 
         // ... 
        }; 
    QMetaObject::activate(this, 
          &staticMetaObject, 
          0, /* this is the signal index in the qt_metacall() map, I suppose */ 
          _args 
         ); 
} 

Infine, la chiamata connect() traduce la stri ng firme del metodo ai loro numeri interi (quelli usati da qt_metacall()) e mantiene un elenco di connessioni segnale-slot; quando viene emesso il segnale, il codice activate() passa attraverso questo elenco e chiama gli "slot" oggetto appropriati tramite il loro metodo qt_metacall().

Per riassumere, l'istanza statica QMetaObject memorizza la "meta-informazione" (stringhe di firma del metodo ecc.), Un metodo generato qt_metacall() fornisce "una tabella di metodi" che consente a qualsiasi segnale/slot di essere chiamato da un indice, segnale le implementazioni generate da moc utilizzano questi indici tramite activate() e infine connect() esegue il processo di mantenimento di un elenco di mappe indice da segnale a slot.

* Nota: c'è una complicazione di questo schema utilizzato nel caso in cui vogliamo fornire segnali tra thread diversi (ho il sospetto che si debba guardare il codice blocking_activate()), ma spero che l'idea generale rimanga la stessa)

Questa è la mia comprensione molto approssimativa del articolo collegato, che facilmente può essere sbagliato, quindi io consiglio di andare a leggere direttamente)

PS. Come vorrei migliorare la mia comprensione dell'implementazione di Qt - per favore fatemi sapere di eventuali incongruenze nella mia retelling!


Dal momento che la mia altra (prima) risposta è stata eliminata da alcuni editor di zelo, io aggiungerà il testo qui (mi manca alcuni dettagli che erano non incorporate nel post di Pavel Shved, e dubito che la persona che cancellata la risposta interessata.)

@Pavel Shved:

Sono abbastanza sicuro che da qualche parte nelle intestazioni Qt esiste una linea:

#define emit

solo per confermare: trovato in vecchio codice Qt da Google Code Search. È abbastanza probabile che sia ancora lì); il percorso della posizione trovato era:

ftp://ftp.slackware-brasil.com.br> Slackware-7.1> contrib> kde-1.90> qt-2.1.1.tgz> usr> lib> qt-2.1.1> src> kernel> qobjectdefs.h


Un altro collegamento complementory: http://lists.trolltech.com/qt-interest/2007-05/thread00691-0.html - vedere la risposta da Andreas Pakulat


Ed ecco un altro pezzo della risposta: Qt question: How do signals and slots work?

Problemi correlati