2011-09-16 14 views
5

Quali sono le migliori pratiche per scrivere le funzioni C o C++ che restituiscono un int che rappresenta un codice di stato?Stato restituzione C o C++

In particolare, desidero conoscere l'utilizzo del client, ma altri suggerimenti sono benvenuti.

Per esempio, posso scrivere qualcosa del genere:

int foo() { 
    return 0; // because everything was cool 
} 

e quindi utilizzarlo come questo?

if (foo()) { 
    // what to do if false, e.g. non-zero, e.g. not OK 
} else { 
    // what to do if true, e.g. zero, e.g. OK 
} 

Questo dovrebbe funzionare, perché le migliori pratiche in genere impongono che un codice di stato di 0 significa tutto era OK e anche 0 significa false in una dichiarazione booleana.

Tuttavia, questo non sarebbe bene, a destra:

if (!foo()) { 
    // what to do if true 
} else { 
    // what to do if false 
} 
+9

'0' non significa' vero' in un'istruzione booleana. – Chad

+1

Si potrebbe voler esaminare la gestione delle eccezioni in C++, che è stato in parte progettato per eliminare questo stile di codifica. – templatetypedef

+0

@Chad: sembra consapevole di quello –

risposta

9

Usiamo questo in C dove lavoro:

int err = foo(); 
if (err) { 
    // armageddon 
} 

L'assegnazione e se potessero essere aggregati, ma con la funzione più complicata chiama diventa più confusa e alcune persone sono confusi da incarico in un condizionale (e gcc lo odia).

Per C++, preferirei eccezioni se disponibili, altrimenti quanto sopra.

Modifica: Si consiglia di restituire 0 in caso di esito positivo e qualsiasi altra cosa in caso di errore. Questo è ciò che fanno le utilità della riga di comando di Unix.

+0

// E anche Armageddon ne è stufo. :-) –

+6

Aggiungo che il ** motivo ** di solito è meglio restituire 0 in caso di successo e non zero su errore (al contrario di booleano 1/0 che significa vero/falso) è che di solito ci sono molte ragioni per cui un'operazione può fallire ma solo un tipo di successo. –

+2

In sé e per sé, non importa. Il significato di 0 è che i programmatori sono pigri e questo permette al terse 'if (foo())' di funzionare. Il successo potrebbe facilmente essere 42, e potremmo tutti scrivere 'if (foo()! = 42)' –

1

Gli stati di ritorno dovrebbero essere definiti nell'interfaccia e noti per il chiamante. Alcuni restituiscono 0 in caso di errore (perché è facile controllare con !), alcuni restituiscono 0 in caso di successo (perché hanno enum di codici di errore, con OK come primo elemento).

Non esiste una legge o uno standard, ogni interfaccia definisce le proprie convenzioni. In C++: usa le eccezioni.

-2
int foo() { 
    try{ 
    ... 
    return 1 
    } 
    catch 
    { 
    return 0; // because everything was cool 
    } 
} 

Vorrei iniziare avvolgendo tutto in un blocco try/catch. Inoltre, invece di usare e int potrebbe rendere più scene per restituire un valore booleano. Questo è solo un po 'più intuitivo quando si esegue il test nella dichiarazione if.

+0

Ingerire le eccezioni è una cattiva pratica, per molti motivi: http://www.google.com.ar/search? sourceid = chrome & ie = UTF-8 & q = swallowing + exceptions –

1

La migliore pratica è quella di documentare il codice in modo che tu e gli altri siate in grado di individuare rapidamente quali saranno i codici di ritorno durante il controllo degli errori.

5

Se davvero si desidera utilizzare codici di stato in quel modo, usarli con un enum o un blocco di #define dichiarazioni che descrivono il intento del codice di stato.

Ad esempio:

enum 
{ 
    kSuccess = 0, 
    kFailure = -1, 
} 

function foo() 
{ 
    return kSuccess; 
} 

if (kSuccess == foo()) 
{ 
    // Handle successful call to foo 
} 
else 
{ 
    // Handle failed call to foo 
} 

In questo modo, l'intenzione è chiara e non c'è congetture soggetto a errori quando qualcuno vuole usare o mantenere il codice in futuro.

+0

Cosa significa "k"? –

+0

È solo una cosa di stile, utilizzata per aiutare i valori costanti a distinguersi da variabili e altri identificatori. – adpalumbo

+0

@adpalumbo: Ma perché hanno bisogno di distinguersi?:) – GManNickG

0

Basta saltare a bordo con un'altra opzione che può essere appropriato in circostanze:

enum fooret { GOOD, BAD, UGLY, WORSE }; 

fooret foo(); // defined elsewhere 

switch(foo()) 
{ 
case BAD: 
case UGLY: 
    // maybe a recoverable failure(s)... 
    // take appropriate actions 
    break; 
case WORSE: 
    // maybe non-recoverable 
    break; 
case GOOD: 
    // successful, take appropriate actions 
    break; 
} 
2
if (foo()) { 
    // what to do if false 
} else { 
    // what to do if true 
} 

Il problema di questo approccio è l'eccesso di nidificazione. Supponiamo di avere tre funzioni che si desidera chiamare:

if(foo1()) { 
    if(foo2()) { 
     if(foo3()) { 
      // the rest of your code 
     } else { 
      // handle error 
     } 
    } else { 
     // handle error 
    } 
} else { 
    // handle error 
} 

per risolvere il problema di nidificazione in eccesso, invertire il valore di ritorno:

if(!foo1()) { 
    // handle error 
    return; 
} 

if(!foo2()) { 
    // handle error 
    return; 
} 

if(!foo3()) { 
    // handle error 
    return; 
} 

Questa soluzione soffre di un altro problema. Combina la logica del programma con il codice di gestione degli errori. Questo complica tutto. Idealmente, si desidera separare la logica del programma e la gestione degli errori. Questo problema può essere risolto con il goto

if(!foo1()) 
    goto error1; 

if(!foo2()) 
    goto error2; 

if(!foo3()) 
    goto error3; 

return; 

error1: 
    // handle error 
    return; 
error2: 
    // handle error 
    return; 
error3: 
    // handle error 
    return; 

Molto più pulito.

Inoltre, il goto può risolvere il problema della deallocazione delle risorse. Vedi Using goto for error handling in C di Eli Bendersky per maggiori dettagli.

+0

Aspetta, ma "non" - qualcosa di diverso da zero non lo rende necessariamente zero, giusto? –

+0

"il risultato è 1 se l'operando è 0 e il risultato è 0 se l'operando non è 0." http://c.comsci.us/etymology/operator/logicalnot.html – Jay

+0

Sì, ma poiché non esiste un tipo booleano di prima classe in C, non è il! solo una negazione aritmetica sull'operando intero? Non è una negazione logica, giusto? Semplicemente capovolge i bit. –