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.