Le eccezioni dovrebbero fare cose diverse da dire all'utente che qualcosa è andato storto?

3

Ho creato una classe generale che accetta una stringa quando è costruita, e sputa quella stringa quando un utente chiama what (). Questo è tutto ciò che fa; al lancio, restituisce la stringa di inizializzazione.

class Exception_As {  
private:  
    std::string message  
public:  
    Exception_As(const std::string& message) { // constructor
        this->message ="EXCEPTION: " +message;
    }

    virtual const char* what() const throw() {
        return message.c_str();
    }
}

Come uso la mia classe di eccezioni personalizzata:

bool check_range(  
    const std::vector<std::string>& list,
    const unsigned& idx_start,  // where to start checking
    const unsigned& idx_end,    // where to stop checking
    const std::string& key
) {
    if ( idx_start > idx_end ) {
        throw Exception_As("check_range: start index > end index: now fix it! :D");
    }

    ... rest of code
} // end of check_range

Come user user check_range:

// some other user code using my check_range
void main() {
    try {
        ... set up variables

        my_hero_qualified = check_range(semi_finalists, third_place, first_place, my_hero);
        ...rest of code
    }

    catch (const Exception_As& e) {
        std::cout << e.what() << std::endl;
    }
}

Se il try / catch non era presente, abortirebbe il programma a causa dell'eccezionata Exception_As. Se era lì, l'utente sarebbe stato avvisato che "check_range: start index > index finale: ora aggiustalo!: D". Per me, sicuramente batte scrivendo tonnellate di classi BadEvent1Exception, BadEvent2Exception, ecc.

Riesco a vedere che sono pigro con questo metodo, e sì, non posso fare niente di speciale come modificare valori basati su eccezioni o metamorfosi sullo stato di un oggetto; ma se hai appena inserito un territorio inaspettato, chissà se lo gestisci correttamente? Puoi anche terminarlo in modo da poter aggiustare ciò che è successo e svilupparlo correttamente.

Quindi, domanda: se tutte le eccezioni fanno è interrompere il programma in circostanze impreviste, non sarà sufficiente una singola classe di eccezioni che restituisce il problema?

... O le eccezioni dovrebbero fare comunque altre cose? In caso contrario, perché creare così tante classi di eccezioni che hanno nomi diversi, certo, ma da quello che ho visto, riportano solo che qualcosa è andato storto.

    
posta user2738698 25.03.2014 - 19:16
fonte

4 risposte

5

Risposta breve:

No

Risposta lunga:

Stai riscontrando il concetto di eccezioni sbagliato.

Le eccezioni non dicono all'utente che c'è qualcosa di sbagliato. È il programma chiamante che dovrebbe catturare l'eccezione e dire (o non dire) all'utente che qualcosa è sbagliato o recuperarlo gentilmente.

Se una "eccezione" contiene codice interno per "correggere" qualcosa, allora non è un'eccezione per definizione.

In che modo l'eccezione può accedere al contesto del programma chiamante per "ripararlo"? In che modo l'eccezione conosce l'interno del programma chiamante per correggere l'errore?

Inoltre, come può un utente chiamare what() dopo che il programma si è interrotto? L'eccezione mantiene anche il messaggio in un file o database?

Il tuo approccio è completamente nuovo e non scritto nei libri o sul Web.

Per favore, mostraci un codice.

    
risposta data 25.03.2014 - 19:38
fonte
1

È necessario distinguere tra le eccezioni per le condizioni di errore previste e le eccezioni che rappresentano errori nel programma.

Supponiamo che tu abbia proibito nulla nella tua base di codice, o almeno all'interno di una particolare classe. Se trovi mai un valore di null in una delle tue variabili private, indica in modo inequivocabile un bug. Va bene usare qualche generica eccezione "qualcosa è andato storto" in casi come questi perché non dovrebbe accadere, e se lo fa, dovrebbe andare inosservato. In questo modo l'applicazione esplode, qualcuno nota che c'è un bug e viene risolto. Suggerisco di lanciare un'Asserzione o qualcosa di simile - nessuno dovrebbe catturare quelli, ed esprime chiaramente che alcune invarianti del tuo codice sono state violate.

Questo è molto diverso da qualcuno che chiama il tuo metodo con gli argomenti sbagliati, o qualche risorsa esterna (ad esempio file) che svanisce improvvisamente - sai che può e probabilmente succederà . Dovresti fornire un tipo di eccezione sufficientemente dettagliato in modo che i chiamanti possano distinguere diversi tipi di errori (ad esempio, non generare GenericException ovunque).

Detto questo, è ancora meglio se puoi scrivere il tuo codice in modo che gli stati di errore non possano accadere o il numero di stati di errore sia ridotto.

    
risposta data 25.03.2014 - 19:41
fonte
1

Quello che hai qui è la forma più generica di Exception, che è solo di reale utilizzo in un gestore di eccezioni "globale": conosci il tipo di cosa: mostra un "messaggio" all'utente, [si spera] registri l'Eccezione in un file per il [tuo] utilizzo diagnostico successivo, quindi consentire al programma di comprimere con grazia in un heap, perdendo tutto ciò che l'utente ha fatto negli ultimi venticinque minuti.

Lo considero un "backstop", il "Lowest Common Denominator" di Exception "handling" e, mentre è il modo in cui molte persone scrivono il codice, non è quello che sono le eccezioni davvero circa.

Un'eccezione è qualcosa che normalmente non dovrebbe accadere ma, creando una classe [Exception] per esso, stai riconoscendo che potrebbe e, se e quando lo fa, il tuo codice è andando a reagire è un modo predefinito (lanciando l'eccezione).

Il codice chiamante ha la responsabilità di catturare l'Eccezione emessa se può fare qualcosa di utile con essa . Ecco perché abbiamo classi Exception distinte: il "filtro" utilizzato dagli schemi di gestione delle eccezioni strutturate è principalmente orientato a capire cosa è stato lanciato Tipo di Eccezione e, quindi, quale blocco "catch" dovrebbe essere diretto a Questo è il motivo per cui le eccezioni vengono "lanciate" due volte - una volta per trovare un gestore (e individuare qualsiasi ripulitura, infine "blocchi tra" qui "e" lì ") e di nuovo per" lanciarlo ", pronto per essere gestito.

Ecco un esempio:

Metodo A genera un nome file e chiama Metodo B , passando quel nome.

Il metodo B si spegne e tenta di memorizzare alcuni dati in un file di questo nome in qualche directory condivisa, da qualche parte. Ma il nome del file non può essere riutilizzato e (per ragioni a cui non riesco a pensare al momento) Metodo A non ha accesso al file system sottostante. Se il Metodo B viene chiamato una seconda volta, diciamo, nello stesso giorno, il file esiste già. In questo caso, Metodo B non può sovrascrivere il file senza perdere i dati. Invece genera un'eccezione "FileAlreadyExists", contenente il nome del file errante.

Senza istruzione contraria, il gestore di eccezioni predefinito, direttamente alla radice del programma (in realtà in esecuzione nel tempo di esecuzione stesso) rileva questa (e ogni altra) eccezione, mostra all'utente un "Oops!" messaggio, quindi eliminare il programma. Non del tutto utile.

Invece, Metodo A può catturare questo Tipo di Eccezione e, usando il nome del file che viene fornito, calcolare un nuovo nome per il file e prova a chiamare di nuovo Metodo B . Finché questo tentativo non genera un'eccezione un'altra , l'Eccezione originale è stata "gestita" correttamente; l'utente non ha bisogno di sapere nulla al riguardo, perché il codice ha affrontato questo evento "inaspettato ma possibile".

Quindi, una classe general-purpose, used-for-everything, "AnyOldException" non è molto utile, perché il codice chiamante non può [facilmente] risolvere ciò che è andato storto ( non prova a deselezionare la proprietà Messaggio dell'eccezione, in questo modo si trova Madness). È molto, molto meglio usare un'eccezione tipizzata e lasciare che il tuo codice possa contenere la fatica.

    
risposta data 27.03.2014 - 14:15
fonte
0

Sì: dovrebbero indicare cosa è sbagliato & il tipo di errore.

Osserviamo un paio di esempi di eccezioni.

In C ++, se si esaurisce la memoria, il nuovo comando genererà un'eccezione. Questo è IMHO un buon uso. È qualcosa che accade e amp; dovrai prendere & affrontarlo.

Un'altra eccezione che potresti avere è un'eccezione generata se i file che stai leggendo non sono formattati correttamente.

Ora consideriamo una funzione che legge da un file & lo memorizza in memoria. Potrebbe facilmente lanciare una di queste due eccezioni, ma dovrebbero essere gestite in modo diverso. L'eccezione di formato errato può essere una semplice finestra di messaggio popup. L'eccezione out-of-memory è un evento molto più grave e probabilmente dovrebbe salvare lo stato corrente del programma ed uscire.

Se usi la stessa eccezione, non puoi distinguere le due parti, a meno che non inizi ad analizzare la stringa. Quindi in questo caso vorrei andare per due diverse eccezioni in modo da poter avere due gestori diversi.

Quindi c'è un buon esempio del motivo per i vari tipi di eccezione.

    
risposta data 27.03.2014 - 07:45
fonte

Leggi altre domande sui tag