/** @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.cxx
    - 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.cxx

      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.cxx
      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.cxx:50
[ 0x8049265] fn0()                         /.../mrw-c++/mrw/examples/exceptionhandling.cxx:38
[ 0x80491e9] fn1()                         /.../mrw-c++/mrw/examples/exceptionhandling.cxx:32
[ 0x8049183] fn2()                         /.../mrw-c++/mrw/examples/exceptionhandling.cxx: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="\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_unix_error [label="mrw::unix_error" URL="\ref mrw::unix_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_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.hxx>

      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.cxx "the example exceptionhandling.cxx".

       The unexpected handler is implemented ready to use in @ref
       AutoTrace "a separate library".
  */

  //@}
}

#endif
