// -*- C++ -*- #ifndef WIBBLE_EXCEPTION_H #define WIBBLE_EXCEPTION_H /* * Generic base exception hierarchy * * Copyright (C) 2003,2004,2005,2006 Enrico Zini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include // for assert #include #include #include #include #include #include /*! \file * This file provides the root of the exception hierarchy. The goal of this * hierarchy is to provide the most possible information on what caused the * exception at the least possible cost for the programmer. * * Every exception is the descendent of Exception that, in turn, extends the * std::exception class of the STL. * * Further descendents of Exception add functionality and automatisms to error * message generation: * * - ContextException extends Exception to provide informations on the context * in which the exception was raised * - ConsistencyCheckException extends ContextException to be a base class for * all exception raised on failure of a consistency check; * IndexOutOfRangeException is one example of such exceptions, to be used * when checking that a value (such as an index) fails betweed two given * bounds. * - SystemException extends ContextException to carry informations about * error conditions reported by external services, like the Operating * System, a database interface or an interface used to communicate to some * network server. In particular, it provides the logic needed to make use * of the error descriptions provided by the external services, as * strerror(3) does for the operating system's error conditions. * FileException is an example of such exceptions, to be used to report * error conditions happened during File I/O. * * Example exception raising: * \code * void MyFile::open(const char* fname) throw (FileException) * { * if ((fd = open(fname, O_RDONLY)) == -1) * throw FileException(errno, stringf::fmt("opening %s read-only", fname)); * } * \endcode * * Example exception catching: * \code * try { * myfile.open("/tmp/foo"); * } catch (FileException& e) { * fprintf(stderr, "%.*s: aborting.\n", PFSTR(e.toString())); * exit(1); * } * \endcode */ namespace wibble { namespace exception { /// Basic unexpected handler /** * This is an unexpected handler provided by the library. It prints to stderr * a stack trace and all possible available informations about the escaped * exception. * * To have the function names in the stack trace, the executables need to be * linked using the -rdynamic flag. */ void DefaultUnexpected(); /// Install an unexpected handler for the duration of its scope. Install /// DefaultUnexpected if no handler is provided. class InstallUnexpected { protected: void (*old)(); public: InstallUnexpected(void (*func)() = DefaultUnexpected); ~InstallUnexpected(); }; // TODO this needs to be made useful with threading as well struct AddContext { static std::vector< std::string > *s_context; static std::vector< std::string > &context() { if ( s_context ) return *s_context; s_context = new std::vector< std::string >(); return *s_context; } template< typename O > static void copyContext( O out ) { std::copy( context().begin(), context().end(), out ); } std::string m_context; AddContext( std::string s ) : m_context( s ) { context().push_back( s ); } ~AddContext() { assert_eq( context().back(), m_context ); context().pop_back(); } }; /// Store context information for an exception class Context { protected: std::vector m_context; public: Context() throw () { AddContext::copyContext( std::back_inserter( m_context ) ); } Context(const std::string& context) throw () { AddContext::copyContext( std::back_inserter( m_context ) ); addContext(context); } void addContext(const std::string& c) throw () { m_context.push_back(c); } std::string formatContext() const throw () { if (m_context.empty()) return "no context information available"; std::stringstream res; std::copy( m_context.begin(), m_context.end(), std::ostream_iterator< std::string >( res, ", \n " ) ); std::string r = res.str(); return std::string( r, 0, r.length() - 7 ); } const std::vector& context() const throw () { return m_context; } }; /// Base class for all exceptions /** * This is the base class for all exceptions used in the system. It provides * an interface to get a (hopefully detailed) textual description of the * exception, and a tag describing the type of exception. Further * functionality will be provided by subclassers */ class Generic : public std::exception, public Context { protected: mutable std::string m_formatted; public: Generic() throw () {} Generic(const std::string& context) throw () : Context(context) {} virtual ~Generic() throw () {} /// Get a string tag identifying the exception type virtual const char* type() const throw () { return "Generic"; } /// Get a string describing what happened that threw the exception virtual std::string desc() const throw () { return "an unspecified problem happened; if you see this message, please report a bug to the maintainer"; } /** * Format in a string all available information about the exception. * * The formatted version is cached because this function is used to * implement the default what() method, which needs to return a stable * c_str() pointer. */ virtual const std::string& fullInfo() const throw () { if (m_formatted.empty()) m_formatted = desc() + ". Context:\n " + formatContext(); return m_formatted; } virtual const char* what() const throw () { return fullInfo().c_str(); } }; /// Exception thrown when some long event is interrupted by an external event /// (like a system call being interrupted by a signal) /** * It is a direct child of ContextException, and has the very same semantics. * * \warning Any function throwing InterruptedException must allow to be called * again with the same parameters to retry the operation */ class Interrupted : public Generic { public: Interrupted() throw () {} Interrupted(const std::string& context) throw () : Generic(context) {} virtual const char* type() const throw () { return "Interrupted"; } }; /// Exception thrown when some system wait is interrupted by a signal /** * It is a direct child of InterruptedException, and has the very same * semantics. * * \warning Any function throwing WaitInterruptedException must allow to be * called again with the same parameters to retry the operation */ class WaitInterrupted : public Interrupted { public: WaitInterrupted(const std::string& context) throw () : Interrupted(context) {} virtual const char* type() const throw () { return "WaitInterrupted"; } }; /// Exception thrown when some consistency check fails /** * It is a direct child of ContextException, and has the very same semantics. */ class Consistency : public Generic { std::string m_error; public: Consistency(const std::string& context, const std::string& error = std::string()) throw () : Generic(context), m_error(error) {} ~Consistency() throw () {} virtual const char* type() const throw () { return "Consistency"; } virtual std::string desc() const throw () { if (m_error.empty()) return "consistency check failed"; return m_error; } }; struct BadCast : public Consistency { BadCast( const std::string &context ) throw() : Consistency( context ) {} ~BadCast() throw() {} virtual std::string typeinfo() const throw() { return "unknown types"; } virtual std::string desc() const throw() { return std::string( "bad cast: " ) + typeinfo(); } }; #ifndef NO_RTTI template< typename From, typename To > struct BadCastExt : public BadCast { BadCastExt( const std::string &error = std::string() ) throw() : BadCast( error ) {} ~BadCastExt() throw() {} virtual std::string typeinfo() const throw() { return std::string( "from " ) + typeid( From ).name() + " to " + typeid( To ).name(); } }; #endif /** * Exception thrown when some value is out of range * * Usage: * \code * if (age < 200) * throw OutOfRange("age", "ensuring that no mere mortal is using the system"); * \endcode */ class OutOfRange : public Consistency { protected: std::string m_var_desc; public: OutOfRange(const std::string& var_desc, const std::string& context) throw () : Consistency(context), m_var_desc(var_desc) {} ~OutOfRange() throw () {} virtual const char* type() const throw () { return "OutOfRange"; } /// Get a short description of the variable that has been checked virtual std::string var_desc() const throw () { return m_var_desc; } virtual std::string desc() const throw () { return m_var_desc + " out of range"; } }; /// Exception thrown when index checking fails /** * This exception is to be thrown when an index checking fails, providing * informations on the acceptable index range and on the offending value. * The context should be a description of the index, possibly preceeded by a * description of in what context did the index checking happen. * * Example: * \code * if (age < 18) * throw IndexOutOfRangeException("age", age, 18, 0, "obtaining driver license"); * * if (i < 0 || i > 100) * throw IndexOutOfRangeException("percentage of items sold", i, 0, 100, "checking input consistency"); * \endcode */ template class ValOutOfRange : public OutOfRange { protected: C m_val; C m_inf; C m_sup; public: /** Construct the exception; minBound and maxBound are the bounds of the * valid index range (inclusive). */ ValOutOfRange(const std::string& var_desc, C val, C inf, C sup, const std::string& context) throw () : OutOfRange(var_desc, context), m_val(val), m_inf(inf), m_sup(sup) {} ///@name Methods used to get informations about the index and its bounds //@{ /// Get the value that caused the index to go out-of-bounds virtual C val() const throw () { return m_val; } /// Get the minimum allowed value for this index virtual C inf() const throw () { return m_inf; } /// Get the maximum allowed value for this index virtual C sup() const throw () { return m_sup; } //@} virtual const char* type() const throw () { return "ValOutOfRange<>"; } virtual std::string desc() const throw (); }; /// Base class for system exceptions /** * This is the base class for exceptions that depend on system events, like * exceptions on file or network I/O, on database access and so on. * SystemExceptions introduces the keeping of an error code with an associated * string description, and by defaults provides the textual description for * Unix errno error codes. * The exception context should be phrased like "doing X". * * Example: * \code * const char* fname = "foo.bar"; * if ((fd = open(fname, O_RDONLY)) == -1) * // Should not throw SystemException, but a more specialized derived * // class like FileException * throw SystemException(errno, stringf::fmt("opening %s read-only", fname)); * \endcode */ class System : public Generic { protected: int m_errno; public: System(const std::string& context) throw (); System(int code, const std::string& context) throw (); virtual const char* type() const throw () { return "System"; } /// Get the system error code associated to the exception virtual int code() const throw () { return m_errno; } /// Get the description of the error code virtual std::string desc() const throw (); }; /// Base class for exceptions for file I/O /** * It is a direct child of SystemException, and has the very same semantics. * Like in SystemException, the error code description provided is a * description for errno values. */ class File : public System { protected: std::string m_name; public: File(const std::string& name, const std::string& context) throw () : System(context), m_name(name) {} ~File() throw () {} virtual const char* type() const throw () { return "File"; } virtual std::string desc() const throw () { return m_name + ": " + System::desc(); } }; } } // vim:set ts=4 sw=4: #endif