/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.12 2005 / 11 / 29 12 : 39 : 42 marc
make it compilable with gcc 4.0 .2 and newer doxygen
Revision 1.11 2005 / 03 / 14 16 : 25 : 23 marc
changed size of dot image so that it fits an A4
Revision 1.10 2005 / 02 / 08 12 : 34 : 14 marc
added errno / unix_error
Revision 1.9 2005 / 01 / 28 12 : 18 : 37 marc
workaround for doxygen warning ( doxygen misinterpreted class forward declaration )
Revision 1.8 2005 / 01 / 28 12 : 13 : 11 marc
interference between group name StackTrace and class name StackTrace
Revision 1.7 2005 / 01 / 28 12 : 13 : 11 marc
interference between group name StackTrace and class name StackTrace
Revision 1.6 2005 / 01 / 28 07 : 51 : 24 marc
improved and corrected trace formatting
Revision 1.5 2004 / 10 / 07 09 : 25 : 34 marc
new group for suggestions
new inheritance
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 ; } // work around Doxygen warning
namespace mrw {
/** @addtogroup grpStackTrace
@ 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 .
*/
//@{
/** @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 :
[ 0x8048e11 ] _start . . / sysdeps / i386 / elf / start . S : 105
[ 0x4016e92b ] __libc_start_main ? ? ? ? : 0
[ 0x8049392 ] main / . . . / mrw - c + + / mrw / examples / exceptionhandling . cpp : 50
[ 0x8049265 ] fn0 ( ) / . . . / mrw - c + + / mrw / examples / exceptionhandling . cpp : 38
[ 0x80491e9 ] fn1 ( ) / . . . / mrw - c + + / mrw / examples / exceptionhandling . cpp : 32
[ 0x8049183 ] fn2 ( ) / . . . / mrw - c + + / mrw / examples / exceptionhandling . cpp : 25
[ 0x400494a4 ] mrw : : exception : : exception ( ) ? ? ? ? : 0
[ 0x4005ace5 ] mrw : : StackTrace : : StackTrace ( ) ? ? ? ? : 0
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 :
*/ /* here follows the example code... */
/** @defgroup exceptions Exceptions with Stack Trace
The following diagram shows the inheritance of the MRW exception
classes and how they are related to the C + + standard
exceptions . @ c mrw : : exception inherits @ c std : : exception , then a
inheritance corresponding to the standard is implemented below
@ c mrw : : exception . To prevent diamond - shaped inheritance , the MRW
exceptions below the @ c mrw : : exception base class do not inherit
from their corresponding standard exception .
The exception classes are meant as a replacement to the standard
exceptions that provide stack trace information . I suggest
exception handling according to @ ref excsug .
@ dot
digraph ExceptionInheritance {
graph [ rankdir = " LR " , size = " 7,8 " ] ;
node [ shape = record , fontname = Helvetica , fontsize = 8 ] ;
edge [ dir = back , headport = w , tailport = e , arrowtail = empty ] ;
std_exception [ label = " std::exception " ] ;
std_bad_alloc [ label = " std::bad_alloc " ] ;
std_bad_cast [ label = " std::bad_cast " ] ;
std_logic_error [ label = " std::logic_error " ] ;
std_domain_error [ label = " std::domain_error " ] ;
std_invalid_argument [ label = " std::invalid_argument " ] ;
std_length_error [ label = " std::length_error " ] ;
std_out_of_range [ label = " std::out_of_range " ] ;
std_runtime_error [ label = " std::runtime_error " ] ;
std_overflow_error [ label = " std::overflow_error " ] ;
std_range_error [ label = " std::range_error " ] ;
std_underflow_error [ label = " std::underflow_error " ] ;
std_bad_exception [ label = " std::bad_exception " ] ;
std_bad_typeid [ label = " std::bad_typeid " ] ;
mrw_exception [ label = " mrw::exception " URL = " \r ef mrw::exception " ] ;
mrw_bad_alloc [ label = " mrw::bad_alloc " URL = " \r ef mrw::bad_alloc " ] ;
mrw_bad_cast [ label = " mrw::bad_cast " URL = " \r ef mrw::bad_cast " ] ;
mrw_logic_error [ label = " mrw::logic_error " URL = " \r ef mrw::logic_error " ] ;
mrw_domain_error [ label = " mrw::domain_error " URL = " \r ef mrw::domain_error " ] ;
mrw_invalid_argument [ label = " mrw::invalid_argument " URL = " \r ef mrw::invalid_argument " ] ;
mrw_length_error [ label = " mrw::length_error " URL = " \r ef mrw::length_error " ] ;
mrw_out_of_range [ label = " mrw::out_of_range " URL = " \r ef mrw::out_of_range " ] ;
mrw_runtime_error [ label = " mrw::runtime_error " URL = " \r ef mrw::runtime_error " ] ;
mrw_overflow_error [ label = " mrw::overflow_error " URL = " \r ef mrw::overflow_error " ] ;
mrw_range_error [ label = " mrw::range_error " URL = " \r ef mrw::range_error " ] ;
mrw_underflow_error [ label = " mrw::underflow_error " URL = " \r ef mrw::underflow_error " ] ;
mrw_unix_error [ label = " mrw::unix_error " URL = " \r ef mrw::unix_error " ] ;
mrw_bad_exception [ label = " mrw::bad_exception " URL = " \r ef mrw::bad_exception " ] ;
mrw_bad_typeid [ label = " mrw::bad_typeid " URL = " \r ef mrw::bad_typeid " ] ;
{ rank = same ; std_exception ; mrw_exception ; }
{ rank = same ; std_bad_alloc ; mrw_bad_alloc ; }
{ rank = same ; std_bad_cast ; mrw_bad_cast ; }
{ rank = same ; std_logic_error ; mrw_logic_error ; }
{ rank = same ; std_domain_error ; mrw_domain_error ; }
{ rank = same ; std_invalid_argument ; mrw_invalid_argument ; }
{ rank = same ; std_length_error ; mrw_length_error ; }
{ rank = same ; std_out_of_range ; mrw_out_of_range ; }
{ rank = same ; std_runtime_error ; mrw_runtime_error ; }
{ rank = same ; std_overflow_error ; mrw_overflow_error ; }
{ rank = same ; std_range_error ; mrw_range_error ; }
{ rank = same ; std_underflow_error ; mrw_underflow_error ; }
{ rank = same ; std_bad_exception ; mrw_bad_exception ; }
{ rank = same ; std_bad_typeid ; mrw_bad_typeid ; }
mrw_exception - > mrw_bad_alloc ;
mrw_exception - > mrw_bad_cast ;
mrw_exception - > mrw_logic_error ;
mrw_logic_error - > mrw_domain_error ;
mrw_logic_error - > mrw_invalid_argument ;
mrw_logic_error - > mrw_length_error ;
mrw_logic_error - > mrw_out_of_range ;
mrw_exception - > mrw_runtime_error ;
mrw_runtime_error - > mrw_overflow_error ;
mrw_runtime_error - > mrw_range_error ;
mrw_runtime_error - > mrw_underflow_error ;
mrw_runtime_error - > mrw_unix_error ;
mrw_exception - > mrw_bad_exception ;
mrw_exception - > mrw_bad_typeid ;
std_exception - > std_bad_alloc ;
std_exception - > std_bad_cast ;
std_exception - > std_logic_error ;
std_logic_error - > std_domain_error ;
std_logic_error - > std_invalid_argument ;
std_logic_error - > std_length_error ;
std_logic_error - > std_out_of_range ;
std_exception - > std_runtime_error ;
std_runtime_error - > std_overflow_error ;
std_runtime_error - > std_range_error ;
std_runtime_error - > std_underflow_error ;
std_exception - > std_bad_exception ;
std_exception - > std_bad_typeid ;
std_exception - > mrw_exception ;
}
@ enddot
*/
//@{
/** @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 : public mrw : : exception {
public :
~ bad_alloc ( ) throw ( ) { }
virtual const char * what ( ) const throw ( ) {
return " mrw::bad_alloc " ;
}
} ;
/// Replacement for @c std::bad_cast, but with stack trace
class bad_cast : public mrw : : exception {
public :
~ bad_cast ( ) throw ( ) { }
virtual const char * what ( ) const throw ( ) {
return " mrw::bad_cast " ;
}
} ;
/// Replacement for @c std::bad_exception, but with stack trace
class bad_exception : public mrw : : exception {
public :
~ bad_exception ( ) throw ( ) { }
virtual const char * what ( ) const throw ( ) {
return " mrw::bad_exception " ;
}
} ;
/// Replacement for @c std::bad_typeid, but with stack trace
class bad_typeid : public mrw : : exception {
public :
~ bad_typeid ( ) throw ( ) { }
virtual const char * what ( ) const throw ( ) {
return " mrw::bad_typeid " ;
}
} ;
/// Replacement for @c std::logic_error, but with stack trace
class logic_error : public mrw : : exception {
public :
~ logic_error ( ) throw ( ) { }
logic_error ( const std : : string & arg ) throw ( ) : _what ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return _what . c_str ( ) ;
}
private :
std : : string _what ;
} ;
/// Replacement for @c std::domain_error, but with stack trace
class domain_error : public mrw : : logic_error {
public :
~ domain_error ( ) throw ( ) { }
domain_error ( const std : : string & arg ) throw ( ) : mrw : : logic_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return mrw : : logic_error : : what ( ) ;
}
} ;
/// Replacement for @c std::invalid_argument, but with stack trace
class invalid_argument : public mrw : : logic_error {
public :
~ invalid_argument ( ) throw ( ) { }
invalid_argument ( const std : : string & arg ) throw ( ) : mrw : : logic_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return mrw : : logic_error : : what ( ) ;
}
} ;
/// Replacement for @c std::length_error, but with stack trace
class length_error : public mrw : : logic_error {
public :
~ length_error ( ) throw ( ) { }
length_error ( const std : : string & arg ) throw ( ) : mrw : : logic_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return mrw : : logic_error : : what ( ) ;
}
} ;
/// Replacement for @c std::out_of_range, but with stack trace
class out_of_range : public mrw : : logic_error {
public :
~ out_of_range ( ) throw ( ) { }
out_of_range ( const std : : string & arg ) throw ( ) : mrw : : logic_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return mrw : : logic_error : : what ( ) ;
}
} ;
/// Replacement for @c std::runtime_error, but with stack trace
class runtime_error : public mrw : : exception {
public :
~ runtime_error ( ) throw ( ) { }
runtime_error ( const std : : string & arg ) throw ( ) : _what ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return _what . c_str ( ) ;
}
private :
std : : string _what ;
} ;
/// Replacement for @c std::overflow_error, but with stack trace
class overflow_error : public mrw : : runtime_error {
public :
~ overflow_error ( ) throw ( ) { }
overflow_error ( const std : : string & arg ) throw ( ) : mrw : : runtime_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return mrw : : runtime_error : : what ( ) ;
}
} ;
/// Replacement for @c std::range_error, but with stack trace
class range_error : public mrw : : runtime_error {
public :
~ range_error ( ) throw ( ) { }
range_error ( const std : : string & arg ) throw ( ) : mrw : : runtime_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return mrw : : runtime_error : : what ( ) ;
}
} ;
/// Replacement for @c std::underflow_error, but with stack trace
class underflow_error : public mrw : : runtime_error {
public :
~ underflow_error ( ) throw ( ) { }
underflow_error ( const std : : string & arg ) throw ( ) : mrw : : runtime_error ( arg ) { }
virtual const char * what ( ) const throw ( ) {
return mrw : : runtime_error : : what ( ) ;
}
} ;
//@}
/** @defgroup 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 " .
*/
//@}
}
# endif