Domanda

    

Questa domanda ha già una risposta qui:

         

I lavori appena finito su un C ++ - programma in cui ho implementato le mie eccezioni (anche se derivata da std :: eccezione). La pratica ho applicato quando un'eccezione provoca una reazione a catena, propagare l'errore alto e dando luogo ad altre eccezioni, è concatenare il messaggio di errore ad ogni passo opportuno di tutti i moduli (lettura classi). Cioè il vecchio eccezione stesso è caduto e viene creata una nuova eccezione, ma con un messaggio di errore più lungo.

Questo può aver lavorato per il mio piccolo programma, ma non ero molto soddisfatto del mio approccio alla fine. Per uno, i numeri di riga (anche se non applicate al momento) e nomi dei file non vengono mantenute, tranne per l'ultima eccezione; e davvero che l'informazione è di maggiore interesse nella prima eccezione.

Immagino che questo avrebbe potuto essere gestita meglio concatenando eccezioni insieme; cioè il vecchio eccezione è fornita nel costruttore della nuova eccezione. Ma come sarebbe che essere attuate? Non fa eccezioni muoiono quando vanno fuori portata dal metodo, impedendo in tal modo uno a puntatori uso di eccezione? E come copiare e memorizzare l'eccezione se l'eccezione può essere di qualsiasi classe derivata?

Questo in ultima analisi, mi portano a considerare se il concatenamento delle eccezioni in C ++ è una buona idea, dopo tutto. Forse si dovrebbe solo creare una sola eccezione e quindi aggiungere dati aggiuntivi che (come ho fatto, ma probabilmente in un modo molto migliore)?

Qual è la sua risposta a questo? Le eccezioni dovrebbero causati da un altro essere concatenati insieme per mantenere una sorta di "traccia eccezione" - e come dovrebbero essere implementate che? - o dovrebbe una sola eccezione essere usata e dati aggiuntivi collegato ad esso -? E come dovrebbe essere fatto che

È stato utile?

Soluzione

E 'necessario copiare i dati di un oggetto eccezione, in una catena, se si vuole sopravvivere al blocco catch che lo riceve, a parte rethrow da throw;. (Che comprende, per esempio, se che esce di blocco catch attraverso una throw obj;.)

Questo può essere fatto mettendo i dati da salvare sul mucchio, e l'attuazione di swap (move in C ++ 0x) sui vostri dati privati ??all'interno l'eccezione, per esempio.

Naturalmente, è necessario fare attenzione quando si utilizza il cumulo con le eccezioni ... ma poi di nuovo, nella maggior parte dei sistemi operativi moderni, la memoria overcommitment impedisce completamente new da sempre lancio, nel bene e nel male. Un margine di buona memoria e far cadere le eccezioni dalla catena di piena crisi dovrebbe tenerlo al sicuro.

struct exception_data { // abstract base class; may contain anything
    virtual ~exception_data() {}
};

struct chained_exception : std::exception {
    chained_exception( std::string const &s, exception_data *d = NULL )
        : data(d), descr(s) {
        try {
            link = new chained_exception;
            throw;
        } catch ( chained_exception &prev ) {
            swap( *link, prev );
        } // catch std::bad_alloc somehow...
    }

    friend void swap( chained_exception &lhs, chained_exception &rhs ) {
        std::swap( lhs.link, rhs.link );
        std::swap( lhs.data, rhs.data );
        swap( lhs.descr, rhs.descr );
    }

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

    virtual ~chained_exception() throw() {
        if ( link && link->link ) delete link; // do not delete terminator
        delete data;
    }

    chained_exception *link; // always on heap
    exception_data *data; // always on heap
    std::string descr; // keeps data on heap

private:
    chained_exception() : link(), data() {}
    friend int main();
};

void f() {
    try {
        throw chained_exception( "humbug!" );
    } catch ( std::exception & ) {
        try {
            throw chained_exception( "bah" );
        } catch ( chained_exception &e ) {
            chained_exception *ep = &e;
            for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
                std::cerr << ep->what() << std::endl;
            }
        }
    }

    try {
        throw chained_exception( "meh!" );
    } catch ( chained_exception &e ) {
        for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
            std::cerr << ep->what() << std::endl;
        }
    }
}

int main() try {
    throw chained_exception(); // create dummy end-of-chain
} catch( chained_exception & ) {
    // body of main goes here
    f();
}

uscita (opportunamente irritato):

bah
humbug!
meh!

Altri suggerimenti

Dal momento che questa domanda è stato chiesto sono state apportate modifiche importanti manifestazioni allo standard C ++ 11. Sono continuamente perdendo questo durante le discussioni circa le eccezioni, ma il seguente approccio, le eccezioni di nidificazione, fa il trucco:

std::nested_exception e std::throw_with_nested

E 'descritto su StackOverflow qui e qui , come è possibile ottenere un backtrace sulle eccezioni all'interno del vostro codice senza bisogno di un debugger o ingombrante registrazione, semplicemente scrivendo un gestore di eccezioni corretta che rigenerare eccezioni nidificate .

Dato che è possibile farlo con qualsiasi classe di eccezione derivata, è possibile aggiungere un sacco di informazioni a tale backtrace! Si può anche dare un'occhiata al mio MWE su GitHub , dove un backtrace osserverebbe qualcosa in questo modo:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

Si consiglia di guardare a questo: http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/boost-exception.html

E 'approccio un po' diverso da quello che MS ha fatto in C #, ma sembra per soddisfare le vostre esigenze.

Un'altra idea è quella di aggiungere i dati relativi al vostro oggetto eccezione quindi utilizzare un'istruzione throw; bare di ri-gettarlo. Credo che le informazioni di stack viene mantenuto in questo caso, e così ci si può comunque sapere la fonte originale di eccezione, ma il test sarebbe una buona idea.

Scommetto poiché se una pila o meno informazioni sono disponibili a tutti è implementazione definita, che le implementazioni variano ancora più ampiamente in se o non è conservato in alcun modo, dopo una dichiarazione throw; nuda.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top