Gestione degli errori in C ++

2

Così, nelle ultime settimane ho approfondito la programmazione in C ++ e ho programmato alcune cose in SDL. In questo modo, devi sempre gestire molto ( brutto ) codice C ++, che assomiglia più a C che a C ++. Una cosa che ho notato durante la programmazione con SDL rispetto alla programmazione college, è che faccio molte programmazioni procedurali. La ragione di ciò è che la maggior parte del codice SDL di esempio che guardo è strutturata in questo modo, ad esempio

bool init(…)
{
    bool success = true;
    thing_A = make_thing_A();
    if (thing_A.is_valid() == false
    {
        success = false;
        std::cout << "Something went wrong" << std::endl;
    }
    …
    return success;
}

... come questo. La pulizia si verificava nel chiamante. Tuttavia questo funziona solo con le funzioni, che non hanno bisogno di restituire altre variabili altrimenti. E questo crea un'interfaccia brutta, in cui non si può mai, se è possibile scrivere qualcosa come if (!init()) , il che rende necessario consultare la documentazione. Non so mai se questa è una pratica corretta di programmazione. Un altro approccio potrebbe essere:

void init(…)
{
    try
    {
        thing_A = make_thing_A();
        if (thing_A.is_valid() == false
        {
            throw ;
        }
        …
    catch (std::exception &e)
    {
        std::cout << "Something went wrong" << std::endl;
         cleanup();
    }
}

Ma poi ho letto, non dovrei mettere questo try-catch-routine nella funzione, ma piuttosto nel chiamante:

void init(…)
{

    thing_A = make_thing_A();
    if (thing_A.is_valid() == false
    {
        throw ;
    }
}

int main(void)
{
    try
    {
        init(…);
    }
    catch (std::exception &e)
    {
        std::cout << "Something went wrong" << std::endl;
        cleanup();
    }
}

Al momento questo è il mio problema numero uno, quando scrivo software, non so cosa fare. Penso che la soluzione più pulita sia la seconda, perché incapsula la logica completa nel callee e implementa DRY, perché non devi mettere la logica di pulizia in ogni chiamante. È giusto? Non ho mai sentito un'opinione assoluta su questo.

EDIT: Non sto necessariamente parlando specificamente di SDL2, ma più in generale. Per esempio. So che c'è un consenso, che dovresti preferire std::unique_ptr a std::auto_ptr , ma quando si tratta di gestione degli errori vedo molti approcci: errno , valori di ritorno, eccezioni, booleani, ma non so quale sia l'approccio go-to , che dovresti evitare, nel qual caso potrebbe essere una buona idea avvicinarti a XYZ, ecc.

    
posta hgiesel 20.02.2016 - 22:03
fonte

2 risposte

3

Ci sono due metodi per gestire gli errori di cui sono a conoscenza.

  1. Attraverso il valore di ritorno di una funzione.
  2. Aumentando le eccezioni.

Possono essere mescolati quando viene presa una cura adeguata nella progettazione della tua applicazione.

Ci sono pro e contro per entrambi gli approcci.

Gestione dell'errore come valore di ritorno

Il tipo di ritorno della funzione può essere bool dove un valore di ritorno di true indica successo e false indica un errore.

Puoi anche usare int come tipo di ritorno dove un valore di ritorno di 0 indicherà successo e qualsiasi valore diverso da zero indicherà un errore. Questo dà un po 'più di flessibilità per indicare il tipo di errore. Il valore restituito potrebbe essere un codice di errore. È possibile associare quel codice a una stringa di errore e sperare di informare l'utente in merito all'errore in modo più significativo.

Pro:

I vantaggi dell'utilizzo del valore restituito come codice di errore è che è facile eseguire il debug del codice semplicemente controllando il codice. È più facile seguire il flusso di esecuzione.

Contro:

Gli svantaggi di utilizzare il valore restituito come un codice di errore è che ogni chiamante deve gestire il valore restituito. Se una chiamata ignora un valore di ritorno, è una fonte di un bug. Non dovrebbero ignorare quella chiamata poiché potrebbero non aver ottenuto ciò che speravano di ottenere.

Gestione dell'errore sollevando un'eccezione

Pro:

Affrontare l'errore sollevando un'eccezione ti offre una flessibilità molto maggiore di quella che potresti aspettarti restituendo un codice di errore.

Potresti lanciare qualsiasi tipo di oggetto. Potrebbe essere un tipo fondamentale, un std::string , una qualsiasi delle eccezioni definite nella libreria standard o in una delle tue classi.

Il tuo codice può essere più semplice. Non devi preoccuparti di catturare eccezioni ovunque. Puoi decidere il livello a cui devono essere gestite le eccezioni.

Contro:

Il prezzo della flessibilità aggiunta è che il tuo codice deve essere preparato per gestire eventuali eccezioni che potrebbero essere generate. Inoltre, occorre prestare molta più attenzione nel decidere quando e come affrontare le eccezioni.

Devi essere sempre cosciente del fatto che qualsiasi eccezione non risolta interromperà il tuo programma.

Osservazioni conclusive

Se lavori in un team, assicurati che il metodo scelto sia appropriato per gli altri membri del tuo team.

Se lavori da solo in un progetto, fai ciò che ha più senso per te.

    
risposta data 22.02.2016 - 19:39
fonte
4

La regola che uso per la gestione degli errori è: gestisci l'errore dove puoi fare qualcosa al riguardo.

Se il modo di gestire l'errore è registrarlo e non è possibile utilizzare le eccezioni, può essere sensato farlo il prima possibile, perché è lì che si dispone di tutte le informazioni necessarie e non si hanno meccanismi semplici per passare tutte quelle informazioni in giro.

Se puoi usare le eccezioni, hai più flessibilità su dove puoi gestire gli errori. In questo caso, preferisco lasciar scoppiare l'errore fino a un livello più appropriato, e preferirei il tuo ultimo esempio.

Tuttavia, se è possibile utilizzare le eccezioni, non c'è motivo di avere una funzione di init separata, e sarebbe meglio fare quel lavoro nel costruttore. Il motivo dei metodi init è che, senza eccezioni, non c'è modo di sapere se il costruttore ha riscontrato un problema e si ha un oggetto semi-costruito tra le mani (che può essere disastroso per l'applicazione). Con eccezioni, non c'è alcun rischio, dato che hai un oggetto costruito con successo, o non hai alcun oggetto e sei nel blocco catch (o più avanti nello stack delle chiamate).

    
risposta data 20.02.2016 - 22:25
fonte

Leggi altre domande sui tag