Molti conosceranno sicuramente le Librerie QT: esse sono infatti il principale tool-kit grafico della suite desktop KDE nonché di molti altre applicazioni e sistemi embedded. In quanto tali, quando si parla di Qt, molti pensano ad esse solamente come mere librerie grafiche, in realtà, tali librerie vanno ben oltre la semplice gestione e visualizzazione di finestre e widget: esse sono infatti una libreria che racchiude un gran numero di layer che semplificano lo sviluppo software in generale. In questo breve articolo infatti mostrerò uno dei tanti modi in cui l’uso delle librerie può Qt semplificare la vita agli sviluppatori: il meccanismo Meta-Object System (moc) e l’accoppiata signals e slots.
Segnali (signals) e slot (slots) sono due elementi fondamentali della programmazione con le Qt. Essi permettono di legare insieme due oggetti senza che nessuno dei due sappia nulla dell’altro. È possibile, ad esempio, costruire due classi A e B e fare in modo che ad un evento di A corrisponda un azione di B e viceversa senza doversi minimamente preoccupare di inserire in A riferimenti di B (e rispettivamente in B riferimenti ad A).
Un segnale è infatti un avviso emesso da un oggetto al verificarsi di determinate condizioni. Ad ogni segnale è collegato uno slot che non è nulla di diverso da una comune funzione C/C++: non appena è emesso il segnale X viene automaticamente eseguita la funzione Y.
Per collegare un segnale ad uno slot si usa il comando
connect(sendre, SIGNAL(signal), receiver, SLOT(slot));
dove
sender
e receiver
sono puntatori a due oggetti (oggetti “qt” per la precisione) e signal
e slot
non sono altro che dichiarazioni di funzioni senza il nome dei parametri. Possiamo avere varie forme di connessioni:- PIÙ SEGNALI COLLEGATI AD UN SOLO SLOT. In questo caso la stessa funzione viene lanciata in corrispondenza di due o più segnali differenti.
- UN SOLO SEGNALE COLLEGATO A PIÙ SLOT. Viceversa, in questo caso, l’arrivo di un segnale avvia l’esecuzione di più funzioni. Tali funzioni vengono eseguite una dopo l’altra in un ordine indefinito (fate quindi molta attenzione alla sincronizzazione di tali funzioni).
- UN SEGNALE COLLEGATO AD UN SEGNALE. In questo caso l’arrivo di un segnale causa semplicemente il lancio di un nuovo segnale (che può essere la propagazione del primo o un segnale del tutto diverso).
“Fantastico!” direte voi, ma cosa è necessario per poter usufruire di tale meraviglia? La cosa è ancora più semplice. Il primo requisito è che sia gli oggetti che lanciano e ricevono segnali sia quelli che definiscono gli slot ereditino dalla classe QObject e definiscano al loro interno la macro Q_OBJECT. Secondo, quando connettiamo un segnale ad uno slot dobbiamo assicurarci che contengano lo stesso numero di parametri, dello stesso tipo e nello stesso ordine. Se le nostre classi rispondono a questi due requisiti siamo pronti ad utilizzare segnali e slot.
Questo meccanismo è usato massicciamente nello sviluppo di applicazioni grafiche in Qt, tuttavia possiamo vedere dall’esempio seguente come esso possa essere applicato in classi che non hanno nulla a che vedere con il lato “grafico” delle Qt.
class Impiegato : public QObject {
Q_OBJECT
public:
Impiegato() { miosalario = 0; }
int salario() const { return miosalario; }
public slots:
void setSalario(int nuovo);
signals:
void salarioCambiato(int nuovo);
private:
int miosalario;
};
void Impiegato::setSalario(int nuovo) {
if (nuovo != miosalario) {
miosalario = nuovo;
emit salarioCambiato(miosalario);
}
}
Da questo stralcio di codice possiamo ricavare due osservazioni interessanti. La prima è che le funzioni slot sono del tutto identiche a classiche funzioni C++ e questo significa che possono tranquillamente essere invocate nel modo tradizionale. Il secondo punto è l’introduzione di alcune nuove parole chiave come signals, slots e emit. Queste parole chiave in realtà non sono altro che macro definite nella libreria Qt ma che svolgono un ruolo molto importante nel Meta-Object System.
Un altra cosa che è bene precisare è l’implementazione di setSalario: essa è infatti progettata in modo tale da cambiare solamente se nuovo è diverso da miosalario in modo tale da evitare che si cada in un ciclo infinito in caso di connessione ciclica (ad esempio connettere il segnale salarioCambiato ad un istanza di Impiegato).
L’uso di questo meccanismo crea però un anello in più nella fase di compilazione. Per assicurare che il processo moc vada a buon fine è necessario generare il file di compilazione (makefile) tramita un applicazione ad-hoc: qmake. L’uso di qmake è eccessivamente semplice quindi non approfondirò l’uso di questo tool.
A questo punto avete a disposizione uno strumento meravigliosamente efficiente per la comunicazione fra oggetti della vostra applicazione, sia essa con interfaccia grafica sia senza. Non vi resta che approfondire questa tecnologia con qualche manciata di tutorial.