2010-04-10 14 views
14

Sono convinto a questo punto che dovrei creare sottoclassi di std::exception per tutte le mie esigenze di lancio di eccezioni. Ora sto osservando come sovrascrivere il metodo what.Restituisce una stringa dinamica da std :: exception `what`

La situazione che sto affrontando, sarebbe davvero utile se la stringa what restituisce essere dinamici. Alcuni pezzi di codice analizzano un file XML, ad esempio, e per me è utile aggiungere una posizione o un numero di riga al messaggio di errore.

Sto cercando di seguire lo Boost Exception handling guidelines.

Quello che mi piacerebbe sapere:

  • what restituisce una const char *, che implica qualsiasi catcher è probabile che non andare a liberare la stringa. Quindi ho bisogno di un altro posto per archiviare il risultato, ma dove sarebbe? (Ho bisogno di thread-safety.)

  • what include anche throw() nella sua firma. Mentre posso impedire al mio what di lanciare qualsiasi cosa, mi sembra che questo metodo non sia destinato a qualcosa di troppo dinamico. Se what non è il posto giusto, allora dove dovrei fare questo invece?


Dalle risposte che ho ottenuto finora, sembra che l'unico modo per raggiungere questo obiettivo è memorizzando la stringa nel eccezione. Le linee guida di Boost raccomandano questo, che mi confonde, perché lo std::runtime_error fa proprio questo.

Anche se dovessi utilizzare una stringa C, dovrei usare un buffer di dimensioni statiche, o fare una gestione della memoria che può fallire anche. (Mi chiedo se questa sia l'unica cosa che può andare storta nel costruttore di copie di std::string. Ciò significherebbe che non otterrò nulla usando stringhe di C allocate dinamicamente.)

C'è qualche altra opzione sinistra?

+1

Aggiunto il punto della linea guida Boost, ri-modificare se non è adatto. –

risposta

15

mie classi di eccezioni in genere non hanno nulla, ma il costruttore e guardare in queste righe:

class MyEx: public std::runtime_error 
{ 
public: 
    MyEx(const std::string& msg, int line): 
     std::runtime_error(msg + " on line " + boost::lexical_cast<string>(line)) 
    {} 
}; 

Un esempio arbitraria, ma è la classe base che gestisce la gestione del messaggio what().

Ma se lo si desidera, è anche possibile assegnare solo la parte di base dell'oggetto di eccezione, dopo aver messo insieme il messaggio nel corpo del costruttore.

#include <stdexcept> 
#include <string> 
#include <sstream> 

class MyEx: public std::runtime_error 
{ 
public: 
    MyEx(const std::string& msg, int line): 
     std::runtime_error("") 
    { 
     std::stringstream ss; 
     ss << msg << " on line " << line; 
     static_cast<std::runtime_error&>(*this) = std::runtime_error(ss.str()); 
    } 
}; 

#include <iostream> 
int main() 
{ 
    try { 
     throw MyEx("Evil code", __LINE__); 
    } 
    catch (const std::exception& e) { 
     std::cout << e.what() << '\n'; 
    } 
} 

Tuttavia, per quanto riguarda le linee guida della spinta, forse si dovrebbe prestare attenzione al punto che i dati numerici (posizioni e linee) potrebbero essere meglio messe a disposizione come numeri attraverso altri metodi. Le linee guida dicono di preoccuparsi meno del messaggio what().

+0

Ancora più preoccupante trovo che 'std :: runtime_error' memorizza una stringa. Quindi la stessa libreria standard contraddice le linee guida di Boost? –

+0

Gli autori di Boost sono preoccupati per la (remota) possibilità che la formattazione o copia della stringa generi un errore (esaurita dalla memoria?). Gli oggetti Exception vengono sempre copiati mentre vengono lanciati. –

3

Bene, nessun problema, è possibile semplicemente implementare il costruttore della classe di eccezioni derivata per formattare la stringa restituita da what(). Libera il buffer che usi per questo nel distruttore.

+0

Questo sembra simile alla risposta di gf, o intendevi stringhe a C regolari? Non è possibile che la stringa venga liberata prima del catch, perché il runtime passa l'eccezione come valore? –

+0

Non sono sicuro che seguo, quale stringa potrebbe essere liberata? È * necessario * implementare un costruttore di copie in una classe di eccezioni che memorizza i puntatori. –

6

Le linee guida di Boost sembrano essere basate su due presupposti: la copia dell'oggetto eccezione potrebbe generare un'altra eccezione e la stringa what() non è uno strumento affidabile o affidabile. Questi sono problemi validi se stai scrivendo una libreria che vedrà ampio uso in una varietà di ambienti. Se hai una conoscenza migliore di come verrà usata l'eccezione, puoi valutare se queste preoccupazioni sono giustificate o molto rumore per nulla. La programmazione si basa su una serie di compromessi e i compromessi che hanno senso per gli sviluppatori Boost potrebbero non essere applicabili.

1

Accetto la risposta di UncleBens, perché la ritengo tecnicamente la risposta più corretta e completa alla mia domanda iniziale.

Per riferimento, in realtà ho scelto una soluzione diversa e ho smesso di usare il what del tutto. Ho refactoring del codice che ho subito a usare qualcosa di simile come la classe di eccezioni di base:

struct Exception : public virtual std::exception 
{ 
    virtual const char* what() const throw() 
    { 
    try { 
     return typeid(this).name(); 
    } 
    catch (const std::exception& e) { 
     return "<unknown exception>"; 
    } 
    } 

    // Extended description; may throw. 
    virtual void describe(std::ostream& out) const = 0; 
}; 

Fondamentalmente compilando what con la cosa più significativa che ho trovato senza preoccuparsi con esso in qualsiasi altro luogo. Vedrò come vanno le tariffe, immagino.

2

Il modo migliore per risolvere questo problema è steal the code.

Il punto illuminante per me, è stato rendersi conto che uno poteva mute stringhe. Sembrava una magia in quel momento. Mark ha fatto un duro lavoro su questo.

+0

Questo è un bell'articolo. Ho finito per utilizzare i flussi proprio come Mark, con l'unica differenza che delegare il messaggio alla classe di eccezione stessa. Dopotutto, vedo che lo svantaggio del mio metodo è che potrei avere ancora bisogno di codici di errore per raggiungere il mio obiettivo. Mentre lo svantaggio del metodo di Mark è che può 'terminate()', come indica Boost. –

+0

Sono l'unico che non è d'accordo con l'idea che le eccezioni sono inutili? – MFH

Problemi correlati