Nella prima parte di questo tutorial abbiamo visto come installare tutti gli strumenti di sviluppo e abbiamo creato il nostro primo programma sfruttando la libreria SDL 2. In questa seconda parte approfondiremo meglio l'utilizzo di g++ e valgrind, inoltre miglioreremo il nostro SDL Hello World.
Per
tutti coloro che sono nuovi utenti del mondo Linux o che comunque non
hanno esperienza nell'invocazione del compilatore g++ ho pensato di
fare un po' di chiarezza sulla sintassi di questo comando.
La
sintassi più semplice è
$ g++
[file sorgente]
L'eseguibile
avrà il nome di default a.out.
Per
specificare il nome dell'eseguibile basta aggiungere l'opzione -o
[nome eseguibile], quindi
$ g++
[file sorgente] -o [nome eseguibile]
Se ci
sono più file sorgenti basta elencarli
$ g++
[file sorgente 1] [file sorgente 2] -o [nome eseguibile]
Per
finire, se dobbiamo utilizzare una libreria, come SDL 2 nel nostro
caso, dobbiamo comunicarlo al linker tramite l'opzione -l (elle
minuscola) seguita dal nome della libreria senza spazi.
$ g++
[file sorgenti] -o [nome eseguibile] -l[nome libreria]
Per la
libreria SDL 2 scriveremo -lSDL2
Bene,
questo è quanto abbiamo visto finora nella prima parte del tutorial,
ma avrete notato che, quando la lista dei file sorgente aumenta,
invocare g++ diventa decisamente scomodo.
Per
semplificare il tutto possiamo utilizzare il comando make.
Il
funzionamento di make è molto semplice, dobbiamo creare un file di
testo dove forniremo tutte le informazioni necessarie per la
compilazione, quali il compilatore da invocare, la lista dei file
sorgente, le opzioni da passare al compilatore, ecc...
Una
volta preparato il file chiamato Makefile, basterà invocare make e
questo invocherà g++ per noi.
Questo
è un esempio di Makefile per compilare il nostro SDL Hello World
Il
contenuto del Makefile è abbastanza semplice da capire, man mano che
aggiungiamo un file sorgente o un file header basterà aggiungerlo
alla lista nel Makefile. Possiamo apportare tutte le modifiche che
vogliamo nel Makefile e basterà invocare il comando make per
compilare ed ottenere il file eseguibile.
Oltre a
questo make si occupa di verificare se dall'ultima compilazione è
cambiato qualcosa nei file sorgente, se nulla è cambiato ci verrà
notificato.
Copiate
ed incollate il testo in un file e salvatelo con il nome Makefile
nella stessa directory dei file sorgente. Ora provate ad eseguire il
comando
$ make
Decisamente
più comodo, non è vero?
La
scorsa volta avevamo installato valgrind in quanto ottimo strumento
di debugging, in particolare per gli errori nella gestione delle
allocazioni di memoria. La sintassi è semplice
$
valgrind ./[nome eseguibile]
L'eseguibile
verrà avviato all'interno dell'ambiente di debugging di valgrind, il
quale analizzerà tutte le allocazioni di memoria e le relative
de-allocazioni delle stesse. Al termine del programma verrà stampato
sul terminale un resoconto completo con un elenco degli eventuali
errori rilevati.
Proviamolo
con il nostro SDL Hello World
$
valgrind ./sdl_helloworld
L'esecuzione
risulterà più lenta e alla fine avremo sul terminale un resoconto
dettagliato.
Senza
entrare troppo nei particolari, quello che ci interessa più di tutto
il resto è l'ultima riga:
ERROR
SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 2)
Nella
sezione LEAK SUMMARY potete trovare altri dettagli sulle allocazioni
di memoria.
E'
ovvio che non è solo il vostro codice che viene analizzato, ma anche
quello delle librerie utilizzate, quindi alcuni memory leaks possono
essere causati da quest'ultime.
Bene,
torniamo all'argomento principale di questo tutorial, SDL 2.
Nello
scorso esempio abbiamo utilizzato la funzione SDL_loadBMP(), che
carica un'immagine di tipo BMP (Windows bitmap) in un oggetto
SDL_Surface.
Per
quanto questa funzione sia molto comoda e semplice da utilizzare, il
formato BMP è piuttosto limitato e scarsamente utilizzato al giorno
d'oggi.
Nella
libreria principale di SDL 2 non ci sono altre funzioni per caricare
immagini di tipo diverse, quali ad esempio PNG o JPG, ma
fortunatamente c'è un'altra libreria dedicata a questo scopo:
SDL_image.
Prima
cosa da fare è installare sul nostro sistema questa libreria.
$ su
(verrà
richiesta la password di root)
# apt
install libsdl2-image-dev
Ora che
abbiamo installato SDL_image possiamo modificare il programma per
aprire un'immagine di tipo PNG.
Aggiungiamo
l'include con il file header di questa libreria in helloengine.h
#include
<SDL2/SDL_image.h>
Come la
libreria SDL 2 anche questa va inizializzata, quindi nella funzione
init() della classe HelloEngine (helloengine.cc) aggiungiamo queste righe di codice
per inizializzare SDL_image per il caricamento di immagini PNG,
subito sotto l'inizializzazione di SDL 2.
//
inizializzazione SDL_image per i file di tipo PNG
if
((IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG) != IMG_INIT_PNG)
{
on_SDL_error(std::cout,
"IMG_Init");
return
false;
}
Ora
andiamo a modificare la parte di codice che carica l'immagine, è
sempre nella funzione init() poco più in basso. Utilizzeremo la
funzione IMG_LoadTexture() che ci restituisce direttamente un
puntatore ad un oggetto di tipo SDL_Texture, quindi non avremo
bisogno di crearla passando per un SDL_Surface.
//
carica l'immagine in texture
std::string
name = "helloworld.png";
if
((texture = IMG_LoadTexture(renderer, name.c_str())) == nullptr)
on_SDL_error(std::cout,
"IMG_LoadTexture");
Potete
convertire l'immagine BMP in una di tipo PNG utilizzando Gimp e
salvatela con il nome helloworld.png.
Prima
di compilare ricordatevi che stiamo utilizzando un'altra libreria,
quindi dovremo modificare il nostro Makefile aggiungendo -lSDL2_image
alla lista delle librerie da linkare, subito dopo -lSDL2.
Terminate le modifiche questo è quello che dovreste ottenere
Terminate le modifiche questo è quello che dovreste ottenere
Compilate
con
$ make
Ed ora
avviate il programma
$
-/sdl_helloworld
Se non
avete fatto errori il programma funzionerà come prima.
Bene,
in questa seconda parte del tutorial dedicato ad SDL 2 abbiamo preso
maggior confidenza con gli strumenti di sviluppo e debugging, inoltre
abbiamo visto come usare la libreria SDL_image.
Nel
prossimo tutorial faremo ulteriori modifiche e vedremo come funziona
la gestione degli eventi.