Nei titoli e nei testi troverete qualche rimando cinematografico (ebbene si, sono un cinefilo). Se non vi interessano fate finta di non vederli, già che non sono fondamentali per la comprensione dei post...

Di questo blog ho mandato avanti, fino a Settembre 2018, anche una versione in Spagnolo. Potete trovarla su El arte de la programación en C. Buona lettura.

domenica 9 settembre 2012

No Comment 2 - La vendetta
come scrivere i commenti nel C - pt.2

Oggi è venuto il momento di completare il discorso del No Comment che trovate qui. Avevamo, per così dire, parlato a livello micro (i commenti ai blocchi di codice), ma abbiamo tralasciato il livello macro, e cioè il contenitore stesso (il file) e i grossi blocchi di codice che sono le funzioni. Ditemi la verità: se lavorate (o avete lavorato) in team, quante volte avete aperto un file scritto da un collega (appartenente alla religione No Comment, devoto e praticante) e avete scoperto che l'unico indizio sul codice contenuto era il nome del file stesso ? Poi, visto che per la Legge di Murphy, l'esimio collega era in ferie (giuste e meritate), avete impiegato un giorno intero solo per capire a cosa servisse quel file, e, solo a quel punto, avete incominciato a decifrare il codice grazie alla Stele di Rosetta.

Un file ha bisogno di un header, e le funzioni (almeno quelle importanti, che di solito sono quelle globali) hanno bisogno di una intestazione fatta come si deve (comunque anche le funzioni statiche si meritano un minimo di presentazione). Spero che il messaggio sia chiaro.

Va bene, e allora come lo facciamo un header ? Premettendo che le varianti possibili sono infinite, direi che, per cominciare, anche qualcosa semplice semplice, come questa, potrebbe andare:
/*!
 *  FILE
 *     getComments.c - estrazione commenti da un sorgente C
 *  PROJECT
 *     myDoc - generazione automatica documentazione
 *  FUNCTIONS
 *     globali:
 *        getComments()
 *     statiche:
 *        formatLine()
 *  AUTHOR
 *     A.Abate
 *  OPERATING SYSTEM
 *     Linux (all versions)
 *  COMPILER
 *     GNU gcc
 *  RELEASE
 *     1.0 (Settembre 2012)
 */ 

Evidentemente si può abbondare più o meno con le descrizioni. Per esempio, se il file contiene il codice di un protocollo di comunicazione, si potrebbe aggiungere una descrizione a blocchi (un disegno in modo text) con le entità implicate e i flussi di comunicazione dei messaggi.

Per quanto riguarda le funzioni importanti, si può usare la classica sintassi delle Man Pages di Unix (e Linux), e quindi fare qualcosa del genere:
/*!
 *  NAME
 *     getComments - esamina un sorgente C per estrarre i commenti
 *  SYNOPSIS
 *     int getComments(
 *        char *inpath,
 *        char *oupath)
 *  DESCRIPTION
 *     Estrae i commenti da un sorgente C con pathname <inpath> e li
 *     scrive in un file di documentazione con pathname <outpath>.
 *  RETURN VALUE
 *     0 a operazione effettuata. Un valore negativo in caso di
 *     errore.
 */ 
E, anche qui, si può abbondare più o meno con le descrizioni, aggiungendo, per esempio, dettagli su tutti i codici di ritorno, e, se necessario, inserendo un ulteriore appartato EXAMPLES, con brevi esempi d'uso.

Come i più osservatori avranno notato, la prima riga dei due esempi qui sopra comincia con "/*!", ossia contiene un simbolo di riconoscimento per un eventuale tool di auto-generazione della  documentazione (tipo Doxygen). E, sempre i più osservatori, avranno notato che anche il contenuto dei due esempi parla di auto-documentazione. Il fatto è che ho pensato di proporre un caso reale, e cosa c'è di meglio che scrivere, ad-hoc, un semplicissimo tool di documentazione che ho scritto e testato in pochissimo tempo (è veramente molto semplice, ma si può perfezionare).

Beh, il mini-tool sarebbe (quasi) tutto qua:
int getComments(
    char *inpath,
    char *oupath)
{
    char *line = NULL;
    size_t len = 0;
    int retcode;
    bool start_comment = false;

    // apro il file di input
    FILE *fpin;
    if (fpin = fopen(inpath, "r")) {
        // apro il file di output
        FILE *fpout;
        if (fpout = fopen(oupath, "w")) {
            // leggo il file di input
            while (getline(&line, &len, fpin) != -1) {
                // test fine commenti per reset flag start_comment
                if (strstr(line, "*/") && start_comment) {
                    start_comment = false;
                    fprintf (fpout, "\n\n\n");
                }

                // test flag start_comment
                if (start_comment) {
                    // formatto e scrivo linea su file output
                    fprintf(fpout, "%s", formatLine(line));
                }

                // test inizio commenti per set flag start_comment
                if (strstr(line, "/*!"))
                    start_comment = true;
            }

            // libera risorse e set retcode=OK
            fclose(fpout);
            free(line);
            retcode = 0;
        }
        else
            retcode = -2;    // fopen err: set retcode=err

        fclose(fpin);    // libera risorse
    }
    else
        retcode = -1;    // fopen err: set retcode=err

    // esce con il retcode settato
    return retcode;
}
Direi che il codice è così intuitivo (e commentato) che non c'è molto da aggiungere: si cerca il codice di riferimento (/*!) e si copia in un file di documentazione tutto quello che lo segue fino a fine commento. La funzione formatLine() (che ho omesso) può non fare nulla (ossia restituire direttamente la linea passata), o fare qualche elaborazione più o meno sofisticata (come minimo raccomando di togliere gli inizi linea del tipo " *"). Io, per testare l'applicazione, ho scritto un main() che ricorre tutti i sorgenti di una directory e scrive i risultati in un altra directory (e, direi, funziona bene).

Quella presentata è una alternativa rapida ai vari tool tipo Doxigen, che sono, ovviamente, raccomandabili per grossi progetti (ma anche l'alternativa artigianale descritta non è male).

Chiudo con un messaggio per quelli che pensano (come già fatto notare qui) che scrivere i commenti (e, a maggior ragione, headers e descrizioni di funzioni) sia tempo perso che allunga i tempi di sviluppo. Bisogna avere larghezza di vedute, ragionare a lunga scadenza: anche se aumento del 10% i tempi di sviluppo (e documentare non richiede molto tempo, specie se lo fai mentre scrivi il codice), verrà il giorno in cui si recupererà con gli interessi il tempo perso in partenza, per esempio alla prima occasione di manutenzione o modifica (provate a farlo su un codice No Comment, poi mi raccontate...). E il tempo è denaro.

Ciao e al prossimo post.

Nessun commento:

Posta un commento