2004-08-28 16:21:25 +00:00
|
|
|
/** @file
|
|
|
|
|
|
|
|
$Id$
|
|
|
|
|
|
|
|
$Date$
|
|
|
|
$Author$
|
|
|
|
|
|
|
|
@copy © Marc Wä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
|
|
|
|
|
|
|
|
*/
|
2004-04-21 06:39:20 +00:00
|
|
|
#ifndef __MRW_EXCEPTION_HPP__
|
|
|
|
#define __MRW_EXCEPTION_HPP__
|
|
|
|
|
|
|
|
#include <exception>
|
2004-04-27 20:26:50 +00:00
|
|
|
#include <stdexcept>
|
|
|
|
#include <typeinfo>
|
2004-04-21 06:39:20 +00:00
|
|
|
#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
|
2004-08-28 16:21:25 +00:00
|
|
|
-# 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)
|
2004-04-21 06:39:20 +00:00
|
|
|
- if no exception is thrown, specify @c throw(std::bad_exception)
|
2004-08-28 16:21:25 +00:00
|
|
|
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
|
2004-04-23 16:03:29 +00:00
|
|
|
-# write an unexpected handler as follows
|
|
|
|
(or link to a @ref AutoTrace "library"):
|
2004-04-21 06:39:20 +00:00
|
|
|
|
|
|
|
@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".
|
2004-04-23 16:03:29 +00:00
|
|
|
|
|
|
|
The unexpected handler is implemented ready to use in @ref
|
|
|
|
AutoTrace "a separate library".
|
2004-04-21 06:39:20 +00:00
|
|
|
*/
|
|
|
|
//@{
|
|
|
|
|
|
|
|
/** @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
|
|
|
|
|
2004-04-23 16:03:29 +00:00
|
|
|
The unexpected handler is implemented ready to use in @ref
|
|
|
|
AutoTrace "a separate library".
|
|
|
|
|
|
|
|
Please note, that without the exception concept and without the
|
2004-04-21 06:39:20 +00:00
|
|
|
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
|
2004-04-23 16:03:29 +00:00
|
|
|
@pre #include <mrw/exception.hpp>
|
2004-04-21 06:39:20 +00:00
|
|
|
|
|
|
|
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
|
2004-08-28 16:21:25 +00:00
|
|
|
*/
|
2004-04-21 06:39:20 +00:00
|
|
|
class exception: public std::exception {
|
|
|
|
public:
|
|
|
|
exception() throw(std::bad_exception);
|
|
|
|
virtual ~exception() throw();
|
2004-04-27 20:26:50 +00:00
|
|
|
virtual const char* what() const throw() {
|
|
|
|
return std::exception::what();
|
|
|
|
}
|
2004-04-21 06:39:20 +00:00
|
|
|
const std::string& stacktrace() const throw(std::bad_exception);
|
|
|
|
private:
|
|
|
|
StackTrace* _stacktrace;
|
|
|
|
};
|
|
|
|
|
2004-04-27 20:26:50 +00:00
|
|
|
/// 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();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2004-04-21 06:39:20 +00:00
|
|
|
//@}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|