Premessa:
Il meccanismo dei signals/slots in Qt, come visto nel precedente articolo, è il metodo più semplice e funzionale per comunicare tra i thread. Se la classe che emette il signal e quella che lo riceve tramite slot sono su due thread differenti l’evento viene messo in una coda e quindi processato dal gestore eventi del thread ricevente. Tutti i parametri scambiati tra i signals e gli slots devono però essere conosciuti dalla Qt ed in particolare dal meta-object-system, che si occupa proprio della gestione dei signals/slots.
Creare una classe personalizzata:
Una classe, per essere utilizzata come parametro nei signals/slot collegati tra diversi thread, deve avere come minimo le seguenti caratteristiche:
- un costruttore pubblico predefinito
- un costruttore di copia pubblico
- un distruttore pubblico
Inoltre la classe, per essere riconosciuta dal meta-object-system, deve essere dichiarata e quindi registrata. Come nell’esempio dell’articolo precedente, creiamo un thread che calcola la media tra due numeri, ma in questo caso ogni numero è incorporato in una classe personalizzata che ne contiene anche il nome.
#include <QMetaType> class CustomNumber { public: CustomNumber(const QString &string=QString(),quint32 value=0); CustomNumber(const CustomNumber &other); ~CustomNumber(); QString string(void) const; void setString(const QString &string); quint32 value(void) const; void setValue(quint32 value); CustomNumber &operator=(const CustomNumber &other); bool operator==(const CustomNumber &other) const; bool operator!=(const CustomNumber &other) const; public: static void registerMetaType(void); private: QString m_string; quint32 m_value; }; Q_DECLARE_METATYPE(CustomNumber)
La dichiarazione della classe avviene tramite la macro Q_DECLARE_METATYPE. E’ consigliabile mettere la macro di seguito alla dichiarazione della classe, nello stesso file header. La registrazione della classe deve avvenire a runtime prima di essere utilizzata. La soluzione migliore è quella di creare una funzione pubblica e statica all’interno della classe che registrerà se stessa una volta chiamata dall’esterno.
void CustomNumber::registerMetaType(void) { if(!QMetaType::type("CustomNumber")) { qRegisterMetaType<CustomNumber>("CustomNumber"); } }
Chiamando questa funzione siamo sicuri che la classe verrà registrata correttamente ed univocamente.
Utilizzare una classe personalizzata:
Una volta implementata la classe personalizzata è necessario ricordarsi di registrarla prima di utilizzare il meccanismo dei signals/slots. Nel caso dei thread, se si utilizza il metodo spiegato nell’articolo precedente, la strada migliore per registrare la classe è quella di farlo nel costruttore della classe AbstractWorker. In questo modo si è sicuri che la classe personalizzata sia conosciuta al meta-object-system prima di essere utilizzata e nello stesso tempo la registrazione viene fatta una sola volta in un punto ben definito del codice. Se la classe personalizzata è utilizzata in più parti è consigliabile sempre eseguire la registrazione nei costruttori delle classi astratte dei worker in modo che il codice sia perfettamente riutilizzabile senza dipendere da classi esterne.
Conclusioni:
Dichiarando e registrando una classe personalizzata (che deve avere come minimo il costruttore pubblico di default, il costruttore di copia pubblico ed il distruttore pubblico) è possibile scambiare tramite signals e slots qualsiasi tipo di informazione rendendo l’utilizzo dei thread ancora più potente ma nello stesso tempo mantenendo la semplicità e la pulizia del programma.