X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Utils%2FException.hh;h=42e1326fbca957754412da8b56985c5f48eb9953;hb=412024ed31a4ab4eaea7a4165a434f8efebee325;hp=9cf7c8ad27237559c211c65554dd5e4c27fe019e;hpb=a1a6c76a214ad1935032826713cabaf9ac57bf07;p=senf.git diff --git a/Utils/Exception.hh b/Utils/Exception.hh index 9cf7c8a..42e1326 100644 --- a/Utils/Exception.hh +++ b/Utils/Exception.hh @@ -23,8 +23,8 @@ /** \file \brief Exception public header */ -#ifndef HH_Exception_ -#define HH_Exception_ 1 +#ifndef HH_SENF_Utils_Exception_ +#define HH_SENF_Utils_Exception_ 1 // Custom includes #include @@ -34,151 +34,281 @@ #include #include #include +#include //#include "Exception.mpp" ///////////////////////////////hh.p//////////////////////////////////////// -/** \defgroup exception System exceptions +/** \defgroup exception Exception classes - The senf::SystemException class and it's derived class template senf::ErrnoException are used to - signal generic system failures based on \c errno codes. - - senf::SystemException is a generic \c errno based exception which carries an error number and - origin information. senf::ErrnoException is a derived class specialized for a specific error - code. This simplifies managing error conditions: + All exceptions in %senf are derived from senf::Exception. This class adds the possibility to + extend the exception description while it is processed. The functionality is provided by a mixin + class senf::ExceptionMixin: \code try { - something.open(path); - // ... + + // Some code which might raise an arbitrary senf exception + + } + catch (senf::ExceptionMixin & e) { + e << "\handling user " << user; + throw; } - catch (senf::ErrnoException & e) { - // Create new file + \endcode + + This will add the user information to any %senf exception thrown. The Exception is however not a + stream. If you need to do more extensive formating, either use an intermediate string-stream or + use Boost.Format: + + \code + try { + // ... } - catch (senf::SystemException & e) { - // Catch all other system exceptions - std::cerr << "Error accessing '" << path << "': " << e.what() << std::endl; + catch (senf::ExceptionMixin & e) { + e << boost::format("\n" "call id 0x%04x@%s") % id % address; } \endcode - This exception is normally thrown using the senf::throwErrno() helper: + senf::SystemException is thrown for all operating system errors (failures which result in the + operating system setting the errno value). It is also derived from senf::Exception and can + therefore be extended as well. + + Defining your own exception classes derived from senf::Exception is very simple: \code - if ((fd = ::open(path.c_str(), O_RDWR)) < 0) - senf::throwErrno("::open()"); + struct FooException : public senf::Exception + { FooException() : senf::Exception("Foo hit the fan") {} }; \endcode - The senf::throwErrno() helper will throw the correct exception class based on some \c errno - value. + If SENF is compiled in debug mode (SENF_DEBUG is defined), the exception message will + automatically include a stack backtrace. For this to work, you need to add the + -rdynamic option to all link commands. This feature depends on gcc and + the GNU-libc. + + To apply these features (extensibility, backtrace) to a non-senf exception, the non-senf + exception can be wrapped and re-thrown. + \code + void foo() { + try { + // ... code that might throw std::bad_cast or somelib::FooException + } + SENF_WRAP_EXC(std::bad_cast) + SENF_WRAP_EXC(somelib::FooException) + } + \endcode + The re-thrown exception can then be caught as std::bad_cast or as senf::ExceptionMixin + as needed. It is safe, to wrap an exception twice (the macro will detect this case). + \code + bar() { + try { + try { + foo(); + } + catch (senf::ExceptionMixin & ex) { + ex << "\n" "add this info"; + } + } + catch (std::bad_cast const & ex) { + std::cerr << ex.what() << std::endl; + } + \endcode + The final error output will include + \li a backtrace if compiled in debug mode + \li the original error message from the std::bad_cast exception + \li the additional error message "add this info" + + \todo Link against libcwd to add file-name/line-number information to the backtrace and remove + the dependency on -rdynamic + \todo Or better, use addr2line to obtain that information when showing the backtrace when + catched within Daemon (addr2line -fsiCe argv[0]) + \todo Add signal handlers for the bad signals which writes a backtrace to stderr and + terminates. This should probably write out a raw backtrace without de-mangling or + line-numbers since we don't want to mess with dynamic memory when the heap might be + corrupted ... Another handler for e.g. SIGUSR2 is nice to show a debug backtrace on demand */ namespace senf { - /** \brief Exception handling standard UNIX errors (errno) + /** \brief Generic extensible exception mixin - This exception is thrown to signal generic \c errno failures. + ExceptionMixin is a generic exception mixin which allows the exception to be later extended + by catching and re-throwing it (See example in \ref exception). - This exception cannot be thrown directly. Instead the derived class ErrnoException should be - thrown via one of the senf::throwErrno helpers. + \ingroup exception + */ + class ExceptionMixin + { + public: + std::string const & message() const; - The error message associated with the SystemException may be extended arbitrarily by using - the exception like a stream: - \code - try { - // This throw would normally be within some function called from here. - senf::throwErrno("::open()"); + void append(std::string text); ///< Extend exception description + /**< Adds \a text to the description text. */ - // Or you may want to use a more descriptive argument string: - senf::throwErrno("::open(\"" + filename + "\")"); + protected: + explicit ExceptionMixin(std::string const & description = ""); + ///< Initialize exception with string + /**< \a description is the initial error description + string. This should probably be a string constant + describing the exception for most derived + exceptions. */ - // Or even use boost::format here - senf::throwErrno((boost::format("::open(\"%s\")") % filename).str()); - } - catch (SystemException & e) { - // You can add further error information later by catching and re-throwing the exception - e << " [while operating on user '" << user << "']"; - throw; - } - \endcode + private: +#ifdef SENF_DEBUG + void addBacktrace(); +#endif + std::string message_; + }; + + /** \brief Extensible exception base-class + + This base-class is an exception which already includes the ExceptionMixin. All SENF + exceptions are derived from this class. Other user-exception may be defined by deriving from + this class too. + + \see \ref exception - \see ErrnoException \ingroup exception */ - class SystemException : public std::exception, public std::stringstream + class Exception + : public ExceptionMixin, public std::exception { public: - virtual char const * what() const throw(); ///< Return verbose error description + virtual ~Exception() throw(); - int errorNumber() const; ///< Error code (\c errno number) - char const * description() const; ///< Error description (strerror() value) + virtual char const * what() const throw(); - bool anyOf(int c0, int c1=0, int c2=0, int c3=0, int c4=0, int c5=0, - int c6=0, int c7=0, int c8=0, int c9=0); - ///< \c true, if errorNumber() is one of \a c0 ... \a c9 + protected: + explicit Exception(std::string const & description = ""); + }; + + /** \brief Wrapper for standard non-senf exceptions - virtual ~SystemException() throw(); + This class wraps an exception of type \a BaseException and adds functionality from + senf::ExceptionMixin. - protected: - SystemException(std::string const & where, int code); - SystemException(SystemException const & other); + \ingroup exception + */ + template + class WrapException + : public ExceptionMixin, public BaseException + { + public: + typedef BaseException Base; - private: - int const code_; // This must be const to make the derived ErrnoException - // class a valid derived class. - mutable std::string buffer_; + WrapException(BaseException const & base); + virtual ~WrapException() throw(); - friend void throwErrno(std::string const &, int); + virtual char const * what() const throw(); }; - /** \brief Error specific system exception + /** \brief Wrap a non-senf exception + + This macro allows to wrap a non-senf exception adding functionality from ExceptionMixin + using the WrapException template. For an example, see \ref exception. - This template restricts the generic SystemException to a specific, compile-time constant - error number \p Code. This allows a specific \c errno number to be cached explicitly. + \ingroup exception + */ +# define SENF_WRAP_EXC(Ex) \ + catch (Ex const & base) { \ + if (dynamic_cast(&base)) \ + throw; \ + else \ + throw senf::WrapException(base); \ + } + + template + typename boost::enable_if< boost::is_convertible, Exc & >::type + operator<<(Exc const & exc, Arg const & arg); ///< Extend exception description + /**< Adds \a arg converted to string to the end of the + exception description string. This operator allows to + use Exception instances like streams. The conversion is + performed using boost::lexical_cast and is + therefor identical to a streaming operation. + \see \ref exception */ + + +# ifdef SENF_DEBUG +# define _SENF_EXC_DEBUG_ARGS ,char const * file=0,int line=0 +# define _SENF_EXC_DEBUG_ARGS_ND ,char const *file,int line +# define _SENF_EXC_DEBUG_ARGS_P ,file,line +# else +# define _SENF_EXC_DEBUG_ARGS +# define _SENF_EXC_DEBUG_ARGS_ND +# define _SENF_EXC_DEBUG_ARGS_P +# endif + + /** \brief Exception handling standard UNIX errors (errno) + + This exception is thrown to signal generic \c errno failures. Normally the \c errno value is + automatically taken from the \c errno variable but it may also be specified explicitly: - This exception is normally thrown via one of the senf::throwErrno() helpers. These helpers - take the numeric \c errno value (either from the \c errno variable or from their - argument) and will throw the corresponding ErrnoException: \code - if ((fd = ::open(filename, O_RDWR)) < 0) - senf::throwErrno("open()"); + // Standard usage: Take \c errno from environment + throw senf::SystemException("::open()") + << " while opening configuration file: " << filename; + + // You may however explicitly specify the errno value + throw senf::SystemException("::open()", ENOFILE) + + // Or leave the location information empty + throw senf::SystemException(ENOFILE); + throw senf::SystemException(); \endcode - \see SystemException + From within SENF (and only there because it depends on the \c SENF_DEBUG symbol), + SystemException should be thrown using wrapper macros which add additional information to + the exception description: + \code + // Standard usage: Take \c errno from environment + SENF_THROW_SYSTEM_EXCEPTION() + << " while opening configuration file: " << filename; + + // You may however explicitly specify the errno value + throw senf::SystemException("::open()", ENOFILE SENF_EXC_DEBUGINFO) + \endcode \ingroup exception */ - template - class ErrnoException : public SystemException + class SystemException : public Exception { public: - static int const fixed_code = Code; + /////////////////////////////////////////////////////////////////////////// + ///\name Structors and default members + ///@{ - explicit ErrnoException(std::string const & where); - ///< ErrnoException with error location information - - ErrnoException(ErrnoException const & other); - }; + explicit SystemException(std::string const & descr = "" _SENF_EXC_DEBUG_ARGS); + explicit SystemException(int code _SENF_EXC_DEBUG_ARGS); + SystemException(std::string const & descr, int code _SENF_EXC_DEBUG_ARGS); - - /** \brief Throw ErrnoException based on current \c errno value - \ingroup exception - */ - void throwErrno(); + virtual ~SystemException() throw(); - /** \brief Throw ErrnoException based on current \c errno value (with location info) - \ingroup exception - */ - void throwErrno(std::string const & where); + ///@} + /////////////////////////////////////////////////////////////////////////// - /** \brief Throw ErrnoException based on given \c errno value - \ingroup exception - */ - void throwErrno(int code); + int errorNumber() const; ///< Error code (\c errno number) + char const * errorString() const; ///< Error string (\c strerror() value) - /** \brief Throw ErrnoException based on given \c errno value (with location info) - \ingroup exception - */ - void throwErrno(std::string const & where, int code); + bool anyOf(int c0, int c1=0, int c2=0, int c3=0, int c4=0, int c5=0, + int c6=0, int c7=0, int c8=0, int c9=0); + ///< \c true, if errorNumber() is one of \a c0 ... \a c9 + + + + private: + void init(std::string const & descr, int code _SENF_EXC_DEBUG_ARGS_ND); + + int code_; + std::string what_; + }; + +# ifdef SENF_DEBUG +# define SENF_EXC_DEBUGINFO ,__FILE__,__LINE__ +# else +# define SENF_EXC_DEBUGINFO +# endif + +# define SENF_THROW_SYSTEM_EXCEPTION(desc) throw senf::SystemException(desc SENF_EXC_DEBUGINFO) } @@ -198,3 +328,4 @@ namespace senf { // compile-command: "scons -u test" // comment-column: 40 // End: +