Vai al contenuto
Topos Uranos

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

  1. Organizzare un programma in più moduli con header, guardie e contratti, compilarlo separatamente e diagnosticare i messaggi del linker.
  2. 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.
  3. Localizzare un difetto con un metodo (riprodurre, ridurre, formulare ipotesi, bisezionare) e con il debugger, e documentare l’indagine.
  4. 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.
  5. 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.