Riepilogo
Questa lezione chiude l’ottava unità e, con essa, il corso. Raccoglie ciò che le tre lezioni dell’unità hanno ottenuto; ricostruisce la catena dei concetti che va dall’interfaccia di un modulo al debugging come metodo; separa ciò che di tutto questo vale in qualsiasi linguaggio da ciò che è proprio di C++; mostra la violazione della regola della definizione unica che nessuno diagnostica e riunisce in un elenco tutto il comportamento indefinito del corso, con ciò che rileva ciascun caso; ripete nella sua valutazione d’uscita le abilità di quella d’ingresso; propone sei sfide che combinano l’unità; e termina con il progetto finale del corso, specificato con precisione, con la sua rubrica, in quattro fasi, ciascuna con una soluzione di riferimento verificata.
Obiettivi di apprendimento
- Organizzare un programma in più moduli con header, guardie e contratti, compilarlo separatamente e diagnosticare i messaggi del linker.
- Testare un modulo a partire dalla sua specifica, con un programma di test automatici eseguito sotto i sanitizer, e verificare con asserzioni ciò che non può accadere.
- Localizzare un difetto con un metodo (riprodurre, ridurre, formulare ipotesi, bisezionare) e con il debugger, e documentare l’indagine.
- Riconoscere il comportamento indefinito che il corso ha mostrato, con ciò che rileva ciascun caso, e la violazione della regola della definizione unica che nessuno diagnostica.
- Integrare tutto il corso in un progetto: specificare, scomporre, costruire, testare, misurare il costo e fare il debug di un programma di gestione di dati tabellari.
Ciò che l’unità ha ottenuto
L’unità si era proposta di insegnare a organizzare, verificare e fare il debug di programmi che non stanno più in un file né nella testa. Lo scopo è stato raggiunto appoggiandosi su ciò che l’introduzione ha dato per scontato: la dichiarazione e la definizione, la compilazione e il collegamento, il contratto, l’invariante e la ricerca binaria. Il programma del catalogo di libri con cui si è aperta l’unità, che stava in un file, copiava parseInt ancora una volta e ometteva in silenzio «Cien años de soledad» perché scriveva < dove la specifica diceva «entrambi inclusi», viene riscritto nell’ultima parte di questa lezione come progetto finale: quattro moduli con contratti, un programma principale che risponde a comandi e un programma di test con venticinque casi. Due di questi casi, «tra, bordi» e «tra, un anno», falliscono con la condizione del programma originale, e perciò il difetto non potrebbe tornare senza che nessuno se ne accorgesse.
Più file e librerie proprie
La lezione Più file e librerie proprie ha diviso un programma in un header che dichiara, un file di implementazione che definisce e un main.cpp che usa. Ha presentato le guardie di inclusione, che impediscono la doppia inclusione all’interno di un’unità di traduzione; la regola della definizione unica, che spiega «multiple definition» e «undefined reference» e dà a inline il suo senso attuale di permesso; la compilazione separata con -c, che ricompila solo gli oggetti i cui sorgenti o header sono cambiati; e una libreria propria con funzioni di lezioni precedenti.
Testare
La lezione Testare ha insegnato a derivare i casi da una specifica, per regioni, bordi e casi speciali, con il risultato atteso calcolato per un’altra via; a scrivere un programma di test automatici che segnala solo i fallimenti e termina con codice 0 o 1; a usare assert per ciò che non può accadere, sapendo che -DNDEBUG lo cancella insieme alla sua condizione; a testare in fase di compilazione con static_assert e constexpr; e a eseguire i test con i sanitizer (quello degli errori di memoria, AddressSanitizer, e quello del comportamento indefinito, UndefinedBehaviorSanitizer). Tutto poggia sull’avvertimento di Dijkstra: un test mostra la presenza di difetti, mai la loro assenza.
Debugging
La lezione Debugging ha presentato il debugging come metodo sperimentale: riprodurre il malfunzionamento, formulare ipotesi falsificabili, decidere tra esse con esperimenti e bisezionare il tratto sospetto. Ha sviluppato lo strumento (-g senza ottimizzazione, la configurazione di Visual Studio Code nei tre sistemi, una sessione reale di GDB, i punti di interruzione semplici e condizionali, i passi e lo stack delle chiamate) e le strategie che valgono più di esso: ridurre il caso, cercare l’ultimo punto corretto con asserzioni e cominciare dal primo frame proprio del rapporto del sanitizer.
Caricamento del contenuto…