C++ Library containing a lot of needful things: Stack Trace, Command Line Parser, Resource Handling, Configuration Files, Unix Command Execution, Directories, Regular Expressions, Tokenizer, Function Trace, Standard Extensions.
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
170 lines
6.3 KiB
170 lines
6.3 KiB
#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: |
|
|
|
@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". |
|
*/ |
|
//@{ |
|
|
|
/** @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 |
|
|
|
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 |
|
|
|
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
|
|
|