Nelle lingue senza gestione delle eccezioni, i codici di errore devono essere restituiti dalla funzione o dai parametri di funzione?

2

Sto scrivendo una libreria di analisi CSV in C e sto considerando se esprimere i codici di errore come valori di ritorno o come parametri passati per riferimento.

Ad esempio, qui ci sono le firme per la funzione che conta il numero di colonne in un file CSV, usando entrambi i disegni:

// Method one
// Return value: number of columns
size_t countfields(
    unsigned char *data, // First/header row in the file
    size_t len, // Length of the buffer
    errno_t *err // Error code (0 == success)
);

// Method two
// Return value: error code (0 == success)
errno_t countfields(
    unsigned char *data, // First/header row in the file
    size_t len, // Length of the data buffer
    size_t *numfields // Number of columns
);

Ovviamente, questa decisione si applica solo a linguaggi come C che non supportano eccezioni, quindi l'unico modo per verificare un errore è esaminare le variabili di mantenimento degli errori designate dopo una chiamata di funzione.

Quale metodo dovrei usare: restituire il codice di errore, o usare un parametro, e perché?

    
posta Govind Parmar 02.07.2018 - 04:43
fonte

3 risposte

7

In "Scrittura di codice solido" , Steve Maguire ha avanzato un valido argomento per il metodo 1 (restituendo i codici di errore tramite un parametro separato ): per il chiamante, è più difficile trascurare che c'è un codice di errore da gestire, almeno accidentalmente.

La seconda variante rende facile chiamare la funzione

   countfields(&data, len, &numfields);

invece di

   errno_t err = countfields(&data, len, &numfields);

e trascuri il fatto che non restituisce solo numfields , ma anche un codice di errore come valore di ritorno.

Nella prima variante, si è costretti a introdurre una variabile di ritorno per il codice di errore, quindi è più difficile non vederla. Naturalmente, se questa variabile viene ignorata volontariamente è qualcosa che la firma non può impedire.

IMHO questa è una vecchia saggezza che non è realmente cambiata da quei vecchi tempi (tranne il fatto che più persone preferiscono usare le lingue con eccezioni).

    
risposta data 02.07.2018 - 07:58
fonte
2

La risposta di Doc Brown è in generale corretta.

Tuttavia, in alcuni casi, si desidera che la gestione degli errori sia più esplicita e più dettagliata (rispetto a un singolo codice di errore intero, come errno - che oggi è una macro simile a una variabile, non più una variabile globale). Potresti (in molti casi) scegliere di stampare qualche messaggio di errore su stderr , ma a volte (pensa a un web o altro tipo di server a lunga esecuzione o a un'applicazione GUI la cui stderr non viene praticamente mai letta, o addirittura perso dopo una chiamata a daemon (3) ), potresti voler fornire dettagli sull'errore altrove (forse perché vuoi collegarli ad un file, o usare syslog , o mostrali attraverso la GUI.

Ad esempio, entrambi Jansson (una libreria JSON) e Glib (una libreria di infrastruttura per GTK e Gnome GUI) segnala il loro errore in caso contrario (rispetto a un solo integrale codice di errore).

Alcune librerie (ad esempio Ian Taylor's libbacktrace ) danno la possibilità di registrare alcuni errori nella gestione di callback (che chiamerebbero in situazioni di errore).

Un cattivo esempio è IMHO le funzioni di caricamento dinamico POSIX dlopen & dlsym . Segnalano errori tramite la funzione dlerror non reentant e statica che trovo brutta.

    
risposta data 02.07.2018 - 09:31
fonte
1

Fallo in modo coerente, e se la tua lingua o libreria ha degli standard, segui lo standard.

Ad esempio, in Objective-C lo standard non è un numero di errore ma oggetti NSError. Le funzioni devono restituire YES / NO per successo / fallimento o qualche oggetto / nil per errore riuscito e avere un parametro che è un puntatore a un errore NSError *.

Il risultato della funzione ti dice se la chiamata è riuscita o meno, e se un oggetto viene restituito, hai un valore di ritorno. Se non ti interessa l'errore che c'era, solo il successo o l'insuccesso, devi passare NULL al puntatore dell'errore. Se vuoi sapere quale errore, passi l'indirizzo di un NSError *. Se non ci sono errori, la funzione chiamata lascia l'errore invariato. Inizializza la tua variabile di errore su zero (il linguaggio lo fa automaticamente), quindi puoi passare lo stesso indirizzo a più funzioni e ottenere l'ultimo errore se ce n'erano.

Quindi, con regole rigide sugli argomenti della funzione, hai una notevole libertà su come chiamare la funzione.

    
risposta data 02.07.2018 - 23:16
fonte

Leggi altre domande sui tag