/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.4 2004 / 08 / 28 16 : 21 : 25 marc
mrw - c + + - 0.92 ( mrw )
- new file : version . cpp
- new file header for all sources
- work around warning in mrw : : auto < T >
- possibility to compile without log4cxx
- work around bugs in demangle . h and libiberty . h
- corrections in documentation
- added simple tracing mechanism
- more warnings
- small corrections in Auto < > : : Free and a new test for it
- possibility to compile without stack trace
*/
# ifndef __MRW_EXCEPTION_HPP__
# define __MRW_EXCEPTION_HPP__
# include <exception>
# include <stdexcept>
# include <typeinfo>
# 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 : @ n
( this specification is " binary " , it only declares whether an exception
is thrown or not , but it does not specify which exact exception can
be thrown )
- if no exception is thrown , specify @ c throw ( std : : bad_exception )
instead of @ c throw ( ) as you would normally specify
- if any exception is thrown specify @ c throw ( std : : exception ) @ n
( @ b Note : If you need a more specific declaration , you must also
declare @ c std : : bad_exception in addition to your exceptions ! )
- only declare @ c throw ( ) if you are 100 % sure , that it is absolutely
impossible that this method ever throws an exception , that means
this method calls no other function or method ( not even from a
system library ) that does not declare @ c throw ( )
- # document the exact exception thrown with Doxygen ' s @ c \ @ 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 ( ) ;
virtual const char * what ( ) const throw ( ) {
return std : : exception : : what ( ) ;
}
const std : : string & stacktrace ( ) const throw ( std : : bad_exception ) ;
private :
StackTrace * _stacktrace ;
} ;
/// Replacement for @c std::bad_alloc, but with stack trace
class bad_alloc :
virtual public mrw : : exception , virtual public std : : bad_alloc {
public :
virtual const char * what ( ) const throw ( ) {
return std : : bad_alloc : : what ( ) ;
}
} ;
/// Replacement for @c std::bad_cast, but with stack trace
class bad_cast :
virtual public mrw : : exception , virtual public std : : bad_cast {
public :
virtual const char * what ( ) const throw ( ) {
return std : : bad_cast : : what ( ) ;
}
} ;
/// Replacement for @c std::bad_exception, but with stack trace
class bad_exception :
virtual public mrw : : exception , virtual public std : : bad_exception {
public :
virtual const char * what ( ) const throw ( ) {
return std : : bad_exception : : what ( ) ;
}
} ;
/// Replacement for @c std::bad_typeid, but with stack trace
class bad_typeid :
virtual public mrw : : exception , virtual public std : : bad_typeid {
public :
virtual const char * what ( ) const throw ( ) {
return std : : bad_typeid : : what ( ) ;
}
} ;
/// Replacement for @c std::logic_error, but with stack trace
class logic_error :
virtual public mrw : : exception , virtual public std : : logic_error {
public :
logic_error ( const std : : string & arg ) : std : : logic_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : logic_error : : what ( ) ;
}
} ;
/// Replacement for @c std::domain_error, but with stack trace
class domain_error :
virtual public mrw : : exception , virtual public std : : domain_error {
public :
domain_error ( const std : : string & arg ) : std : : domain_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : domain_error : : what ( ) ;
}
} ;
/// Replacement for @c std::invalid_argument, but with stack trace
class invalid_argument :
virtual public mrw : : exception , virtual public std : : invalid_argument {
public :
invalid_argument ( const std : : string & arg ) : std : : invalid_argument ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : invalid_argument : : what ( ) ;
}
} ;
/// Replacement for @c std::length_error, but with stack trace
class length_error :
virtual public mrw : : exception , virtual public std : : length_error {
public :
length_error ( const std : : string & arg ) : std : : length_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : length_error : : what ( ) ;
}
} ;
/// Replacement for @c std::out_of_range, but with stack trace
class out_of_range :
virtual public mrw : : exception , virtual public std : : out_of_range {
public :
out_of_range ( const std : : string & arg ) : std : : out_of_range ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : out_of_range : : what ( ) ;
}
} ;
/// Replacement for @c std::runtime_error, but with stack trace
class runtime_error :
virtual public mrw : : exception , virtual public std : : runtime_error {
public :
runtime_error ( const std : : string & arg ) : std : : runtime_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : runtime_error : : what ( ) ;
}
} ;
/// Replacement for @c std::overflow_error, but with stack trace
class overflow_error :
virtual public mrw : : exception , virtual public std : : overflow_error {
public :
overflow_error ( const std : : string & arg ) : std : : overflow_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : overflow_error : : what ( ) ;
}
} ;
/// Replacement for @c std::range_error, but with stack trace
class range_error :
virtual public mrw : : exception , virtual public std : : range_error {
public :
range_error ( const std : : string & arg ) : std : : range_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : range_error : : what ( ) ;
}
} ;
/// Replacement for @c std::underflow_error, but with stack trace
class underflow_error :
virtual public mrw : : exception , virtual public std : : underflow_error {
public :
underflow_error ( const std : : string & arg ) : std : : underflow_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return std : : underflow_error : : what ( ) ;
}
} ;
//@}
}
# endif