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

È stato utile?

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?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top