diff --git a/mrw/exception.hpp b/mrw/exception.hpp index e1e073a..51ca78a 100644 --- a/mrw/exception.hpp +++ b/mrw/exception.hpp @@ -9,6 +9,10 @@ @license LGPL, see file COPYING $Log$ + 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 @@ -72,58 +76,6 @@ namespace mrw { 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". */ //@{ @@ -162,6 +114,106 @@ call of fn0 successful unexpected handler, the program would abort in function fn2 on line 25. The output was produced by the following 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"]; + 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="\ref mrw::exception"]; + mrw_bad_alloc [label="mrw::bad_alloc" URL="\ref mrw::bad_alloc"]; + mrw_bad_cast [label="mrw::bad_cast" URL="\ref mrw::bad_cast"]; + mrw_logic_error [label="mrw::logic_error" URL="\ref mrw::logic_error"]; + mrw_domain_error [label="mrw::domain_error" URL="\ref mrw::domain_error"]; + mrw_invalid_argument [label="mrw::invalid_argument" URL="\ref mrw::invalid_argument"]; + mrw_length_error [label="mrw::length_error" URL="\ref mrw::length_error"]; + mrw_out_of_range [label="mrw::out_of_range" URL="\ref mrw::out_of_range"]; + mrw_runtime_error [label="mrw::runtime_error" URL="\ref mrw::runtime_error"]; + mrw_overflow_error [label="mrw::overflow_error" URL="\ref mrw::overflow_error"]; + mrw_range_error [label="mrw::range_error" URL="\ref mrw::range_error"]; + mrw_underflow_error [label="mrw::underflow_error" URL="\ref mrw::underflow_error"]; + mrw_bad_exception [label="mrw::bad_exception" URL="\ref mrw::bad_exception"]; + mrw_bad_typeid [label="mrw::bad_typeid" URL="\ref 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_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 @@ -213,132 +265,191 @@ call of fn0 successful }; /// Replacement for @c std::bad_alloc, but with stack trace - class bad_alloc: - virtual public mrw::exception, virtual public std::bad_alloc { + class bad_alloc: public mrw::exception { public: + ~bad_alloc() throw() {} virtual const char* what() const throw() { - return std::bad_alloc::what(); + return "mrw::bad_alloc"; } }; /// Replacement for @c std::bad_cast, but with stack trace - class bad_cast: - virtual public mrw::exception, virtual public std::bad_cast { + class bad_cast: public mrw::exception { public: + ~bad_cast() throw() {} virtual const char* what() const throw() { - return std::bad_cast::what(); + return "mrw::bad_cast"; } }; /// Replacement for @c std::bad_exception, but with stack trace - class bad_exception: - virtual public mrw::exception, virtual public std::bad_exception { + class bad_exception: public mrw::exception { public: + ~bad_exception() throw() {} virtual const char* what() const throw() { - return std::bad_exception::what(); + return "mrw::bad_exception"; } }; /// Replacement for @c std::bad_typeid, but with stack trace - class bad_typeid: - virtual public mrw::exception, virtual public std::bad_typeid { + class bad_typeid: public mrw::exception { public: + ~bad_typeid() throw() {} virtual const char* what() const throw() { - return std::bad_typeid::what(); + return "mrw::bad_typeid"; } }; /// Replacement for @c std::logic_error, but with stack trace - class logic_error: - virtual public mrw::exception, virtual public std::logic_error { + class logic_error: public mrw::exception { public: - logic_error(const std::string& arg): std::logic_error(arg) {} + ~logic_error() throw() {} + logic_error(const std::string& arg) throw(): _what(arg) {} virtual const char* what() const throw() { - return std::logic_error::what(); + return _what.c_str(); } + private: + std::string _what; }; /// Replacement for @c std::domain_error, but with stack trace - class domain_error: - virtual public mrw::exception, virtual public std::domain_error { + class domain_error: public mrw::logic_error { public: - domain_error(const std::string& arg): std::domain_error(arg) {} + ~domain_error() throw() {} + domain_error(const std::string& arg) throw(): mrw::logic_error(arg) {} virtual const char* what() const throw() { - return std::domain_error::what(); + return mrw::logic_error::what(); } }; /// Replacement for @c std::invalid_argument, but with stack trace - class invalid_argument: - virtual public mrw::exception, virtual public std::invalid_argument { + class invalid_argument: public mrw::logic_error { public: - invalid_argument(const std::string& arg): std::invalid_argument(arg) {} + ~invalid_argument() throw() {} + invalid_argument(const std::string& arg) throw(): mrw::logic_error(arg) {} virtual const char* what() const throw() { - return std::invalid_argument::what(); + return mrw::logic_error::what(); } }; /// Replacement for @c std::length_error, but with stack trace - class length_error: - virtual public mrw::exception, virtual public std::length_error { + class length_error: public mrw::logic_error { public: - length_error(const std::string& arg): std::length_error(arg) {} + ~length_error() throw() {} + length_error(const std::string& arg) throw(): mrw::logic_error(arg) {} virtual const char* what() const throw() { - return std::length_error::what(); + return mrw::logic_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 { + class out_of_range: public mrw::logic_error { public: - out_of_range(const std::string& arg): std::out_of_range(arg) {} + ~out_of_range() throw() {} + out_of_range(const std::string& arg) throw(): mrw::logic_error(arg) {} virtual const char* what() const throw() { - return std::out_of_range::what(); + return mrw::logic_error::what(); } }; /// Replacement for @c std::runtime_error, but with stack trace - class runtime_error: - virtual public mrw::exception, virtual public std::runtime_error { + class runtime_error: public mrw::exception { public: - runtime_error(const std::string& arg): std::runtime_error(arg) {} + ~runtime_error() throw() {} + runtime_error(const std::string& arg) throw(): _what(arg) {} virtual const char* what() const throw() { - return std::runtime_error::what(); + return _what.c_str(); } + private: + std::string _what; }; /// Replacement for @c std::overflow_error, but with stack trace - class overflow_error: - virtual public mrw::exception, virtual public std::overflow_error { + class overflow_error: public mrw::runtime_error { public: - overflow_error(const std::string& arg): std::overflow_error(arg) {} + ~overflow_error() throw() {} + overflow_error(const std::string& arg) throw(): mrw::runtime_error(arg) {} virtual const char* what() const throw() { - return std::overflow_error::what(); + return mrw::runtime_error::what(); } }; /// Replacement for @c std::range_error, but with stack trace - class range_error: - virtual public mrw::exception, virtual public std::range_error { + class range_error: public mrw::runtime_error { public: - range_error(const std::string& arg): std::range_error(arg) {} + ~range_error() throw() {} + range_error(const std::string& arg) throw(): mrw::runtime_error(arg) {} virtual const char* what() const throw() { - return std::range_error::what(); + return mrw::runtime_error::what(); } }; /// Replacement for @c std::underflow_error, but with stack trace - class underflow_error: - virtual public mrw::exception, virtual public std::underflow_error { + class underflow_error: public mrw::runtime_error { public: - underflow_error(const std::string& arg): std::underflow_error(arg) {} + ~underflow_error() throw() {} + underflow_error(const std::string& arg) throw(): mrw::runtime_error(arg) {} virtual const char* what() const throw() { - return std::underflow_error::what(); + 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