Saturday 16 October 2010

La trappola del C++ (prima parte)

Il titolo è provocatorio, lo dico subito. Se pensate che qualsiasi programma è meglio scriverlo in C++ dimenticate l'esistenza di questo post, visto che vi farà solo venire acidità di stomaco.
Se, invece, siete disposti a in discuterne, mettetevi comodi: ci sarà da divertirsi.


Non mi dilungherò troppo sui vantaggi del C++, perché penso li conosciate tutti abbastanza bene: velocità, astrazione, programmazione ad oggetti, librerie, gestione eccezioni, template ecc. Queste caratteristiche rendono il C++ uno strumento di eccezionale potenza, tant'è che un buon 80-90% dei programmi che usate sarà stato scritto in questo linguaggio. Perché allora parlo di un "tranello"?

Da quanto ho sperimentato di persona, quando si comincia a lavorare a un progetto C++, consci della potenza del linguaggio, si inizia con un alto livello di astrazione ben chiara. Più di una volta ci si trova all'inizio pensando «creerò una classe generale per gestire questo; da essa ne deriverò due per poter fare queste altre due cose; nasconderò all'interno
di un gruppo di funzioni questi aspetti di basso livello e il gioco è fatto». E' l'inizio della fine.

E' vero che si può astrarre molto utilizzando il C++ (è un linguaggio a oggetti!), ma la presenza di diversi dettagli di basso livello cui bisogna fare attenzione provoca una serie di "seccature" in fase di sviluppo.
Un esempio è l'assenza di una funzione/metodo simile allo String.split di Java; è vero che può essere sostituita spesso con la funzione C sscanf, ma non è sempre vero: String.split supporta le espressioni regolari, mentre sscanf carica dati come interi e stringhe separati da spazi. Qualcuno obietterà che si può ricorrere alla strtok. E' vero, ma tra String.split e strtok cosa trovate più comodo?

Per fare un esempio pratico delle noie che bisogna affrontare col C++,  voglio esporre il tentativo di creare un semplice motore grafico in 2D (si, due dimensioni) in OpenGL, utilizzando il C++ e partendo da zero. Sia chiaro: questo non vuol dire che è sbagliato fare i motori grafici in C++: vuol solo sottolineare che E' DIFFICILE e che sopravvalutare le proprie abilità col C++ può essere mortificante.

Tornando all'esempio, pensiamo al programmatore medio che, dopo aver guardato con attenzione i concetti e gli esempi dell'OpenGL Red Book. Ora sa cosa può fare attraverso la libreria. La sua idea (corretta) è di trasformare tutti i blocchi logici che userà spesso in metodi di classe. Fatto un po' di ordine, probabilmente il suo progetto prevederà
  1. una classe per contenere le immagini (Image)
  2. una classe/struttura dati per il caching delle immagini (MediaCache)
  3. una classe che gestisca gli sprite e le loro animazioni (Sprite)
  4. una classe per gestire la telecamera (Camera)
  5. una classe per le mappe presenti su un file (Map)
  6. una classe utilizzata solo per la gestione dell'I/O dell'utente (IOManager)
Si tratta di una schematizzazione di alto livello. Probabilmente è incompleta, ma serve a dare un'idea di come si potrebbe affrontare il problema. Il ruolo delle classi è abbastanza chiaro ma, a scanso di equivoci, lo spiego comunque

  • Un oggetto Map rappresenta una mappa da visualizzare. Conterrà una array 2D di interi che rappresenta le tiles (tessere) da visualizzare. Map caricherà queste informazioni da un file di testo;
  • L'oggetto Camera avrà il compito di leggere i dati presenti su Map e di utilizzare le routine OpenGL per visualizzare la porzione di mappa che vogliamo visualizzare, i fotogrammi correnti degli Sprite e, in generale, tutti gli oggetti di tipo Image;
  • MediaCache è una struttura dati che terrà in memoria tutti gli oggetti Image che andremo a caricare. Questo ci permetterà di tenere una sola istanza in memoria e di far riferimento a quella quando dovremo visualizzarla.
(continua domani con la seconda parte)

No comments: