Come segnale e slot sono implementati sotto il cofano?
-
05-07-2019 - |
Domanda
Questa domanda è già stata posta in questo forum ma non capisco il concetto.
Stavo leggendo in giro e sembra che il segnale e gli slot siano implementati usando i puntatori a funzione, cioè il segnale è una grande funzione che al suo interno chiama tutti gli slot collegati (puntatori a funzione). È corretto? E qual è il ruolo dei file moc generati nell'intera storia? Non capisco come la funzione del segnale sappia quali slot chiamare, ovvero quali slot sono collegati a questo segnale.
Grazie per il tuo tempo
Soluzione
Qt implementa queste cose in un modo che ricorda le lingue interpretate. Cioè costruisce tabelle di simboli che mappano i nomi dei segnali ai puntatori a funzione, li mantiene e cerca il puntatore a funzione in base al nome della funzione dove necessario.
Ogni volta che si emette un segnale, ovvero scrivere
emit something();
in realtà si chiama la funzione qualcosa ()
, che ha generato automaticamente dal compilatore di meta oggetti e inserito in un file * .moc
. All'interno di questa funzione viene verificato a quali slot questo segnale è attualmente collegato e le funzioni di slot appropriate (che sono state implementate nelle proprie fonti) vengono chiamate in sequenza tramite le tabelle dei simboli (nel modo sopra descritto). E emit
, come altre parole chiave specifiche di Qt, vengono scartati dal preprocessore C ++ dopo la generazione di * .moc
. In effetti, in una delle intestazioni Qt ( qobjectdefs.h ), esistono tali righe:
#define slots
#define signals protected
#define emit
La funzione di connessione ( connect
) modifica solo le tabelle dei simboli gestite all'interno dei file * .moc
e gli argomenti passati ad essa (con SIGNAL ()
e `macro SLOT) sono anche preelaborate per abbinare le tabelle.
Questa è l'idea generale. Nella sua altra risposta, ? ? ? ? ci fornisce i collegamenti alla mailing list di trolltech e a un'altra domanda SO su questo argomento.
Altri suggerimenti
Penso che dovrei aggiungere quanto segue.
C'è un'altra domanda collegata - e c'è < a href = "http://www.ntcore.com/files/qtrev.htm" rel = "nofollow noreferrer"> un ottimo articolo che può essere considerato un'espansione abbastanza dettagliata del suo answer ; ecco di nuovo questo articolo , con miglioramenti (anche se non ancora perfetti) evidenziazione della sintassi del codice.
Ecco la mia breve rivisitazione di ciò, che potrebbe essere soggetto a errori)
Fondamentalmente quando inseriamo la macro Q_OBJECT
nella definizione della nostra classe, il preprocessore la espande in una dichiarazione di istanza QMetaObject
statica, che sarebbe condivisa 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 ...
}
Questa istanza, a sua volta, durante l'inizializzazione memorizzerà le firme ( " methodname (argtype1, argtype2) "
) dei segnali e degli slot, cosa consentire di implementare la chiamata indexOfMethod ()
, che restituisce l'indice del metodo con la sua 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 moc
crea il file moc_headername.cpp
per l'intestazione della classe Qt headername.h
, inserisce le stringhe di firma e altri dati necessari per la corretta inizializzazione della struttura d
, 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 qt_metacall ()
dell'oggetto, che accetta l'ID metodo di un oggetto e una matrice di puntatori argomento e chiama il metodo tramite un lungo cambia
in questo modo:
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 ...
}
Infine, per ogni segnale moc
genera un'implementazione, che contiene una chiamata QMetaObject :: activ ()
:
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 le firme del metodo stringa nei loro ID interi (quelli usati da qt_metacall ()
) e mantiene un elenco di segnali -slot connessioni; quando viene emesso il segnale, il codice activ ()
passa attraverso questo elenco e chiama l'oggetto appropriato "slot". tramite il loro metodo qt_metacall ()
.
Per riassumere, l'istanza statica QMetaObject
memorizza le "meta-informazioni" (stringhe di firma del metodo ecc.), un metodo qt_metacall ()
generato fornisce " una tabella di metodi " che consente a qualsiasi segnale / slot di essere chiamato da un indice, le implementazioni del segnale generate da moc
utilizzano questi indici tramite activ ()
e infine connect ()
svolge il compito di mantenere un elenco di mappe indice segnale-slot.
* Nota: c'è una complicazione di questo schema usato per il caso quando vogliamo fornire segnali tra thread diversi (sospetto che uno debba guardare il codice blocking_activate ()
), ma io spero che l'idea generale rimanga la stessa)
Questa è la mia comprensione molto approssimativa dell'articolo collegato, che potrebbe facilmente essere errata, quindi consiglio di andare a leggerlo direttamente)
PS. Poiché vorrei migliorare la mia comprensione dell'implementazione di Qt, per favore fatemi sapere di eventuali incongruenze nella mia ripetizione!
Poiché la mia altra (precedente) risposta è stata cancellata da un editore zelante, aggiungerò qui il testo (mi mancano alcuni dettagli che non sono stati inseriti nel post di Pavel Shved e dubito che la persona a chi ha eliminato la risposta interessata.)
@Pavel Shved:
Sono abbastanza sicuro che da qualche parte nelle intestazioni di Qt esiste una linea:
#define emit
Solo per confermare: l'ho trovato nel vecchio codice Qt da Ricerca codice Google. È abbastanza probabile che sia ancora lì); il percorso di localizzazione trovato era:
ftp://ftp.slackware-brasil.com.br & # 8250; Slackware 7.1 & # 8250; contrib & # 8250; kde-1.90 & # 8250; qt-2.1.1.tgz & # 8250; usr & # 8250; lib & # 8250; qt-2.1.1 & # 8250; src & # 8250; kernel & # 8250; qobjectdefs.h
Un altro collegamento complementare: http: //lists.trolltech .com / qt-interest / 2007-05 / thread00691-0.html - vedi la risposta di Andreas Pakulat
Ed ecco un altro pezzo di risposta: Domanda Qt: come funzionano i segnali e gli slot?