I previously asked a question about how to chaining exceptions in C++, and one of the answers provided a nifty solution to how it can be done. The problem is that I don't understand the code, and trying to have this kind of discussion in the comments is just too much of a bother. So I figured it's better to start a new question entirely.
The code is included below and I've clearly marked each section which I don't get. A description of what I don't understand is included below the code. The code was written by Potatoswatter.
Code
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;
// ----------------------------------------------------------------
// How does this work (section 1)?
throw;
// ----------------------------------------------------------------
} catch ( chained_exception &prev ) {
// ----------------------------------------------------------------
// How does this work (section 2)?
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() {
// --------------------------------------------------------------------
// How does this work (section 3)?
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 ) {
// Print ep->what() to std::cerr
}
}
}
try {
throw chained_exception( "meh!" );
} catch ( chained_exception &e ) {
for ( chained_exception *ep = &e; ep->link; ep = ep->link ) {
// Print ep->what() to std::cerr
}
}
}
int main() try {
// ------------------------------------------------------------------------
// How does this work (section 4)?
throw chained_exception(); // create dummy end-of-chain
// ------------------------------------------------------------------------
} catch( chained_exception & ) {
// body of main goes here
f();
}
Running the code gives the following output:
bah
humbug!
meh!
What I don't understand
throw;
insidetry
-block: I've never seen this before. The only place I've thoughtthrow;
to be valid was inside acatch
-block to rethrow what was caught. So what does this do? Some debugging apparently shows that the thrown exception is what was thrown previously, but that was inside a completely differenttry
-block. In fact, it was even outside thestruct
declaration!Swap fields: Why do we need to swap the exception fields? Wouldn't just copying of the pointers be enough? Is this to prevent the structures to which the fields point at from being deleted from the heap prematurely?
Check
link
andlink
's link: I can understand checking thatlink
is notNULL
(even though deleting aNULL
pointer has no effect), but why the need to check thelink
's link?Throw dummy exception: Why is this dummy needed? It's thrown but then dropped. Why do we need this as an end to the chain?
virtual
method to clone itself onto the heap, and using that method in the public constructor. – Hyacinthhyacintha