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?
Nizza domanda. Vedi anche: http://stackoverflow.com/questions/1413777/how-boost-implements-signals-and-slots – elcuco