# ifndef __MRW_EXCEPTION_HPP__
# define __MRW_EXCEPTION_HPP__
# include <exception>
# include <string>
namespace mrw {
class StackTrace ;
/** @addtogroup StackTrace
@ section exc Exception Handling with Stack Trace
One of the main reasons for the mrw : : StackTrace class is , to be
able to store a trace where an exception is thrown . This trace
is then stored as exception information , but not yet evaluated ,
symbols are calculated only if necessary , upon request . So the
exception is still relatively cheap .
There is a class named mrw : : exception that derieves from and
behaves as @ c std : : exception , but it stores a mrw : : StackTrace on
construction and offers a method @ c mrw : : exception : : stacktrace ( )
that returns a well formatted stack trace of the point , where
the exception was created .
@ subsection excprob Common Problems with Exception Handling
Exceptions are very handy : When you have a problem , you throw an
exception and when you call a method and reach the next line ,
everything was fine . You don ' t have to care about error handling
unless you are able to handle it . Otherwise you simply let pass
any exception up in the stack .
The big disadvantage is , when you catch an exception , you don ' t
know where it was thrown . That ' s the stack trace for . Another
problem is , the exception specification problem : When you don ' t
write exception specifications , you don ' t know what a specific
method throws . If you do write exception specifications , they
are not checked at compile time , but enforced at run time . If a
wrong exception is thrown , the program stops , calls an
unexpected handler that by default aborts the program . Since the
unexpected handler must not return , the problem cannot be
recovered from . But the unexpected handler can rethrow and catch
the bad exception and it is allowed to throw a new
exception . This is what my suggested exception handling concept
makes use of .
@ subsection excsug Suggested Exception Handling Rules
- # derieve all your exceptions from mrw : : exception
- # write exception specifications as follows :
- if any exception is thrown , specify @ c throw ( mrw : : exception )
- if no exception is thrown , specify @ c throw ( std : : bad_exception )
- # document the exact exception thrown with Doxygen ' s \ @ throw tag
- # write an unexpected handler as follows
( or link to a @ ref AutoTrace " library " ) :
@ code
void unexpectedHandler ( ) {
try {
throw ;
} catch ( mrw : : exception & x ) {
// trace x.stacktrace() and x.what()
} catch ( std : : exception & x ) {
// trace x.what()
} catch ( . . . ) {
// trace unknown unexpected
}
throw std : : bad_exception ( ) ; // try to recover
}
@ endcode
What happens :
- If you throw an exception in a method that declares not to
throw an exception , the unexpected handler is called .
- It writes a stack trace for you to be able to find your bug .
- Then it throws a @ c std : : bad_exception , which is allowed to pass .
- Your program does not abort , but continues running .
- If higher in the stack you catch the exception , you may be
able to recover .
- If you throw an exception where you are allowed to , you only need to
catch mrw : : exception and you can access @ c what ( ) and @ c stacktrace ( ) .
For a proof of concept refer to
@ ref exceptionhandling . cpp " the example exceptionhandling.cpp " .
The unexpected handler is implemented ready to use in @ ref
AutoTrace " a separate library " .
*/
//@{
/** @example exceptionhandling.cpp
It is possible to recover from an unexpected exception ! A stack
trace helps you to find the source of a problem , here function
@ c fn2 ( ) in file @ c / privat / home / marc / pro / mrw - c + + / mrw / test . cpp
on line @ c 25. This example produces the following output :
@ verbatim
call fn0
enter fn0
enter fn1
enter fn2
UNEXPECTED : N3mrw9exceptionE
- - - - - - - - - - - - - - - - - - - - - - - - - - - Stack :
[ 0x8049e51 ] . . / sysdeps / i386 / elf / start . S : 105 _start
[ 0x401cfd3e ] ? ? ? ? : 0 ? ? ? ?
[ 0x804a3d0 ] examples / exceptionhandling . cpp : 50 main
[ 0x804a2a3 ] examples / exceptionhandling . cpp : 38 fn0 ( )
[ 0x804a227 ] examples / exceptionhandling . cpp : 32 fn1 ( )
[ 0x804a1c1 ] examples / exceptionhandling . cpp : 25 fn2 ( )
[ 0x804fdda ] . . / mrw / exception . cpp : 6 mrw : : exception : : exception ( )
[ 0x804a8f5 ] . . / mrw / stacktrace . cpp : 54 mrw : : StackTrace : : StackTrace ( )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EXCEPTION caught in fn0 : St13bad_exception
leave fn0
call of fn0 successful
@ endverbatim
The unexpected handler is implemented ready to use in @ ref
AutoTrace " a separate library " .
Please note , that without the exception concept and without the
unexpected handler , the program would abort in function fn2 on
line 25. The output was produced by the following code :
*/
/** @brief replacement for @c std::exception, that collects a stack trace
@ pre # include < mrw / exception . hpp >
This exception class behaves exactely like @ c std : : exception ,
but it collects a stack trace in the constructor and offers a
method to return the formatted stack trace for logging .
It is recommended , to inherit all the exceptions you ever throw
from this class . This way you can always access the stack trace
if you run into troubles . It is fursther recommended , to write a
unexpected handler , that rethrows , catches this exception , then
throws a @ c std : : bad_exception to try to continue . This is the
reason , why all the exception specifications in the MRW C + +
Library declar @ c throw ( std : : bad_exception ) instead of @ c
throw ( ) , when they throw nothing .
@ code
namespace myProject {
void unexpectedHandler ( ) {
try {
throw ;
} catch ( mrw : : exception & x ) {
// trace x.stacktrace() and x.what()
} catch ( std : : exception & x ) {
// trace x.what()
} catch ( . . . ) {
// trace unknown unexpected
}
throw std : : bad_exception ( ) ; // try to recover
}
}
int main ( ) {
std : : set_unexpected ( & myProject : : unexpectedHandler ) ;
. . .
}
@ endcode
*/
class exception : public std : : exception {
public :
exception ( ) throw ( std : : bad_exception ) ;
virtual ~ exception ( ) throw ( ) ;
const std : : string & stacktrace ( ) const throw ( std : : bad_exception ) ;
private :
StackTrace * _stacktrace ;
} ;
//@}
}
# endif