Необходимо скопировать данные из объекта исключения в цепочку, если вы хотите, чтобы они пережили блок catch
, который их получает, кроме повторного выброса бросок;
. (Что включает, например, если этот блок catch
выходит через объект throw ;
.)
Это можно сделать, поместив данные для сохранения в куче и реализовав Например, swap
( move
в C ++ 0x) на ваши личные данные внутри исключения.
Конечно, вы должны быть осторожны при использовании кучи с исключениями ... но опять же, в большинстве современных операционных систем чрезмерное использование памяти полностью предотвращает выброс new
, к лучшему или к худшему. Хороший запас памяти и удаление исключений из цепочки после полного сбоя должны обеспечивать безопасность.
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();
}
вывод (соответственно сварливый):
bah
humbug!
meh!
Возможно, вы захотите взглянуть на это: http://www.boost.org/doc/libs/1_43_0/libs/exception/doc/boost-exception.html
Это несколько иной подход, чем тот, что MS сделала в C#, но, похоже, он соответствует вашим требованиям.
Другая идея - добавить соответствующие данные в объект исключения, а затем использовать голый оператор throw;
для повторного вызова. Я думаю, что в этом случае информация о стеке сохраняется, и вы все равно будете знать первоначальный источник исключения, но тестирование было бы хорошей идеей.
Готов поспорить, что поскольку наличие или отсутствие информации о стеке вообще определяется реализацией, то реализации будут еще больше различаться в том, сохраняется ли она каким-либо образом после простого throw;
оператора.