2011-01-31 2 views
7

voglio derivare da std :: exception per aggiungere informazioni specifiche ai miei file di log, ma non riesco a capire come accedere a .what() dall'eccezione std ::.derivante da std :: exception

Inoltre, so che non è sicuro creare una stringa nel mio gestore di eccezioni, ma non sono un esperto in questo argomento, quindi quali sono alcune alternative più sicure?

struct Exception : public std::exception, private boost::noncopyable 
{ 
    public: 
     Exception(std::string msg) 
      : message(msg) 
     {} 
     ~Exception() 
     {} 

     virtual const char* what() const throw 
     { 
      std::string what = message + // and now what? base.what() 
      LOG(what); // write to log file 
      return what.c_str(); 
     } 

    private: 
     std::string message; 
}; 

EDIT: ho davvero chiesto la mia domanda nel modo sbagliato. Sono piuttosto interessato alla sicurezza, ho solo pensato che sarebbe bello avere più dati per la registrazione. Mi sbagliavo.

ora, non sono così paranoico su bad_alloc che viene lanciato dalla stringa del messaggio in caso ci fosse un bad_alloc prima, preferirei avere un messaggio pulito. Detto questo ho riscritto alcune cose:

struct Exception : public std::exception 
{ 
    public: 
     Exception(std::string msg) 
      : message(msg) 
     {} 
     ~Exception() 
     {} 

     virtual const char* what() const throw 
     { 
      LOG(what); // write to log file 
      return what.c_str(); 
     } 

    private: 
     std::string message; 
}; 

ci sono ancora grandi preoccupazioni su quel codice ora? il LOG() lancia std :: exception in caso qualcosa vada storto, perché non volevo un ciclo infinito di chiamate di log per classe di derivazione derivata, e quella classe chiamava ancora log che avrebbe causato di nuovo la stessa esecuzione. Funzionerà come voglio, o un'eccezione di registrazione nella mia chiamata di classe derivata terminate() o causerà il core dump?

+2

Re: alternative più sicure. Questa è una grande domanda. Adatto per la propria domanda. –

+1

Benvenuti in Stack Overflow. Sembra che tu stia chiedendo * due * domande (il che significa che dovresti fare due post separati). Uno chiede come chiamare il metodo ereditato dalla classe base, e l'altro chiede come memorizzare un messaggio di eccezione senza come stringa (presumibilmente perché non si vuole allocare memoria e si incorre nel rischio di un 'bad_alloc'). Né ha davvero nulla a che fare con derivare da 'std :: exception' in particolare. Domande specifiche separate portano a titoli di domande più descrittivi e risposte più utili. Si prega di considerare la divisione di questa domanda. –

+2

Se il codice genera un'eccezione di tipo Exception ("..."), cosa si aspetta che std :: exception :: what() restituisca? Non penso che contenga qualcosa di utile (se contiene qualcosa). Suggerirei semplicemente di restituire message.c_str(). – Ferruccio

risposta

9

EDIT: Dopo aver scritto questa risposta, ho inciampato sulla sezione Error and Exception Handling nel documento Boost. Consiglierei quel documento su questa risposta.


Prima di tutto, rendere l'eccezione non copiabile è una cattiva idea. Quando scrivi qualcosa come

// could be any exception, doesn't matter. 
throw Exception(...); 

Il runtime crea una copia di quell'oggetto in una posizione speciale. Alcuni compilatori potrebbero ottimizzare questo e creare l'oggetto originale in quella posizione, ma lo The C++ Programming Language dice che è una copia, e credo anche che sia quello che dice lo standard, anche se non ne sono sicuro. Potresti farla franca nel tuo ambiente attuale, ma potrebbe non essere sempre così.

Quindi, tutto il resto dipende da come si è paranoici con casi angolari.

La parte di allocazione della memoria è prevalentemente instabile nella clausola di eccezione (ad esempio il costruttore). Se questa allocazione di memoria avviene a fallire (cioè std::bad_alloc si butta), ci sono due possibilità, a seconda di come si scrive il throw dichiarazione:

  1. std::string viene creato prima dell'istruzione throw, std::bad_alloc sostituisce l'eccezione si pensava si farebbe sollevare, il problema è una specie di cattivo rapporto.
  2. std::string viene creato in linea nel richiamo del costruttore. Se questo è considerato "durante la gestione delle eccezioni" dallo standard, verrà invocato il numero std::unexpected()/std::terminate() e in pratica si otterrà un core dump.

In ogni caso, sembra che non si otterrà l'effetto desiderato di riportare il proprio errore.

Si consiglia sempre di creare una sorta di stato temporaneo che non alloca memoria nel costruttore e attendere la chiamata a std::what() per creare la stringa che riporta l'errore, ma che potrebbe ancora portare al caso # 1. È possibile ricorrere ad alcune dimensioni del buffer determinate in fase di compilazione per assicurarsi che ciò non accada.

Molte persone vi diranno che non hanno mai avuto problemi con l'allocazione stringhe nei costruttori perché è improbabile std::bad_alloc sarà sollevato a meno che l'eccezione originale era std::bad_alloc in primo luogo. Quindi, dipende dal tuo livello di paranoia.

+0

Nota: non ho il libro a portata di mano per riferirlo correttamente ... –

4

Non entrerò nella miriade di problemi che ci sono con il tuo codice perché è un enorme barattolo di vermi. Ma ecco come si chiama un metodo di classe base:

std::exception::what() 
+0

BTW, ho cambiato il nome della tua variabile locale. –

+1

Il valore restituito non corrisponde al tipo restituito? – UncleBens

+0

@Uncle: Grazie per aver sottolineato la mia pigrizia. :) Risolto. –

-1

per la seconda parte. Ho diverse centinaia di kloc che funzionano su> 100 piattaforme che hanno una classe derivata da: std :: exception con i membri std :: string. Mai avuto alcun problema con esso

class myex: public std::exception 
{ 
public: 
    std::string m_msg; 
    std::string m_className; 
    int m_rc; 
... 
3

La risposta a questa domanda è, a meno che non si sia impostato esplicitamente il valore che what() deve restituire da std :: exception, quindi non si desidera chiamarlo. Il fatto è che si comporterà in modi che potresti non aspettarti e in modo dissimile da implementazioni differenti.

Si noti che lo standard non fornisce alcuna funzionalità in std :: exception per fornire il valore stringa per iniziare. Quelle implementazioni che in realtà forniscono informazioni utili dalla chiamata di std :: exception :: what() aggiungono funzionalità aggiuntive non standard alla classe. Ad esempio, MSVC ha un costruttore exception(char const* const&). Questo non è nello standard e probabilmente non vuoi dipendere da questo.

La tua migliore scommessa è quella di non chiamare mai std :: exception :: cosa da una classe derivata. Certo, fai le ripetizioni nelle tue versioni personalizzate che sottoclassi le cose UNDER std :: exception, ma non farlo nelle derivate dirette.

Se si insiste a chiamare direttamente questa funzione, allora sarebbe meglio controllare NULL perché è quello che probabilmente otterrete.

+0

+1 per una buona risposta, ma non hai risposto alla sua domanda reale che non ha nulla a che fare con le eccezioni. Come chiamare un metodo di classe base? –

+0

Se dici così, John. –

+0

grazie noah, @john, credo di aver davvero posto la domanda in modo sbagliato. Sono interessato in sicurezza, ho solo pensato di poter avere più dati per la registrazione dalla classe base. sembra che mi stavo sbagliando completamente – cppanda