C’è una buona regola quando si sviluppa codice, soprattutto in grossi progetti, e cioè abbondare nelle asserzioni di debug. In C++ in particolare, l’header <cassert> fornisce la macro assert(), utilissima per assicurarsi a runtime che valgano sempre tutte le precondizioni, le postcondizioni e gli invarianti che si intende mantenere. Assicurare la validità di tutte le assunzioni aiuta ad evitare i bug alla radice e facilita il riutilizzo del codice. Inoltre, trattandosi di una macro, ci si può lasciar andare in controlli anche molto numerosi e/o costosi perchè nella build di release la macro non verrà espansa, avendo quindi un impatto sulle performance pari a zero.
Per quanto mi riguarda, la precondizione più frequente che mi trovo ad asserire in ogni funzione è la non-nullità dei parametri di tipo puntatore.
Stamattina mi sono accorto che il mio codice era zeppo di righe del genere:
void func(SomeType *); void func(SomeType *p) { assert(p); p->method(); // ... codice ... } |
Se da un lato sono contento di essermi ricordato di riempire il codice di tutte queste assert, dall’altro mi sono accorto di alcuni punti negativi di questo approccio:
- Il codice è ripetitivo e distrae dal vero contenuto della funzione
- Non c’è traccia di queste pre-condizioni nel prototipo della funzione, ovvero l’unica cosa a cui gli utilizzatori della funzione hanno (o dovrebbero avere) accesso.
Anche se il secondo punto dovrebbe essere risolto da una buona documentazione, che comunque dovrebbe sempre esserci, penso esista un modo migliore per gestire questo caso particolare di asserzione, ovvero evitare argomenti nulli.
Non ho certo la presunzione di aver inventato una soluzione nuova. Semplicemente c’ho messo meno a implementarla che a cercarla in giro, quindi eccola qua.
Continua a leggere