/* -*- C++ -*- * lemon/error.h - Part of LEMON, a generic C++ optimization library * * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi * Kutatocsoport (Egervary Research Group on Combinatorial Optimization, * EGRES). * * Permission to use, modify and distribute this software is granted * provided that this copyright notice appears in all copies. For * precise terms see the accompanying LICENSE file. * * This software is provided "AS IS" with no warranty of any kind, * express or implied, and with no claim as to its suitability for any * purpose. * */ #ifndef LEMON_ERROR_H #define LEMON_ERROR_H //! \ingroup exceptions //! \file //! \brief Basic exception classes and error handling. #include #include #include #include #include #include namespace lemon { /// \addtogroup exceptions /// @{ /// \brief Exception safe wrapper class. /// /// Exception safe wrapper class to implement the members of exceptions. template class ExceptionMember { public: typedef _Type Type; ExceptionMember() throw () { try { ptr.reset(new Type()); } catch (...) {} } ExceptionMember(const Type& type) throw () { try { ptr.reset(new Type()); if (ptr.get() == 0) return; *ptr = type; } catch (...) {} } ExceptionMember(const ExceptionMember& copy) throw() { try { if (!copy.valid()) return; ptr.reset(new Type()); if (ptr.get() == 0) return; *ptr = copy.get(); } catch (...) {} } ExceptionMember& operator=(const ExceptionMember& copy) { if (ptr.get() == 0) return; try { if (!copy.valid()) return; *ptr = copy.get(); } catch (...) {} } void set(const Type& type) { if (ptr.get() == 0) return; try { *ptr = type; } catch (...) {} } const Type& get() const { return *ptr; } bool valid() const { return ptr.get() != 0; } private: std::auto_ptr<_Type> ptr; }; /// Exception-safe convenient "error message" class. /// Helper class which provides a convenient ostream-like (operator << /// based) interface to create a string message. Mostly useful in /// exception classes (therefore the name). class ErrorMessage { protected: ///\e ///\todo The good solution is boost:shared_ptr... mutable std::auto_ptr buf; ///\e bool init() throw() { try { buf.reset(new std::ostringstream); } catch(...) { buf.reset(); } return buf.get(); } public: ///\e ErrorMessage() throw() { init(); } ErrorMessage(const ErrorMessage& em) throw() : buf(em.buf) { } ///\e ErrorMessage(const char *message) throw() { init(); *this << message; } ///\e ErrorMessage(const std::string &message) throw() { init(); *this << message; } ///\e template ErrorMessage& operator<<(const T &t) throw() { if( ! buf.get() ) return *this; try { *buf << t; } catch(...) { buf.reset(); } return *this; } ///\e const char* message() throw() { if( ! buf.get() ) return 0; const char* mes = 0; try { mes = buf->str().c_str(); } catch(...) {} return mes; } }; /** * \brief Generic exception class. * * Base class for exceptions used in LEMON. */ class Exception : public std::exception { public: ///\e Exception() {} ///\e virtual ~Exception() throw() {} ///\e virtual const char* exceptionName() const { return "lemon::Exception"; } ///\e virtual const char* what() const throw() { return exceptionName(); } }; /** * \brief One of the two main subclasses of \ref Exception. * * Logic errors represent problems in the internal logic of a program; * in theory, these are preventable, and even detectable before the * program runs (e.g., violations of class invariants). * * A typical example for this is \ref UninitializedParameter. */ class LogicError : public Exception { public: virtual const char* exceptionName() const { return "lemon::LogicError"; } }; /** * \brief \ref Exception for uninitialized parameters. * * This error represents problems in the initialization * of the parameters of the algorithms. */ class UninitializedParameter : public LogicError { public: virtual const char* exceptionName() const { return "lemon::UninitializedParameter"; } }; /** * \brief One of the two main subclasses of \ref Exception. * * Runtime errors represent problems outside the scope of a program; * they cannot be easily predicted and can generally only be caught as * the program executes. */ class RuntimeError : public Exception { public: virtual const char* exceptionName() const { return "lemon::RuntimeError"; } }; ///\e class RangeError : public RuntimeError { public: virtual const char* exceptionName() const { return "lemon::RangeError"; } }; ///\e class IOError : public RuntimeError { public: virtual const char* exceptionName() const { return "lemon::IOError"; } }; ///\e class DataFormatError : public IOError { protected: ExceptionMember _message; ExceptionMember _file; int _line; mutable ExceptionMember _message_holder; public: DataFormatError(const DataFormatError &dfe) : IOError(dfe), _message(dfe._message), _file(dfe._file), _line(dfe._line) {} ///\e explicit DataFormatError(const char *the_message) : _message(the_message), _line(0) {} ///\e DataFormatError(const std::string &file_name, int line_num, const char *the_message) : _message(the_message), _line(line_num) { file(file_name); } ///\e void line(int line) { _line = line; } ///\e void message(const std::string& message) { _message.set(message); } ///\e void file(const std::string &file) { _file.set(file); } ///\e int line() const { return _line; } ///\e const char* message() const { if (_message.valid() && !_message.get().empty()) { return _message.get().c_str(); } else { return 0; } } /// \brief Returns the filename. /// /// Returns \e null if the filename was not specified. const char* file() const { if (_file.valid() && !_file.get().empty()) { return _file.get().c_str(); } else { return 0; } } ///\e virtual const char* what() const throw() { try { std::ostringstream ostr; ostr << exceptionName() << ": "; if (message()) ostr << message(); if( file() || line() != 0 ) { ostr << " ("; if( file() ) ostr << "in file '" << file() << "'"; if( file() && line() != 0 ) ostr << " "; if( line() != 0 ) ostr << "at line " << line(); ostr << ")"; } _message_holder.set(ostr.str()); } catch (...) {} if( _message_holder.valid()) return _message_holder.get().c_str(); return exceptionName(); } virtual const char* exceptionName() const { return "lemon::DataFormatError"; } virtual ~DataFormatError() throw() {} }; class IOParameterError : public LogicError { protected: ExceptionMember _message; ExceptionMember _file; mutable ExceptionMember _message_holder; public: IOParameterError(const IOParameterError &ile) : LogicError(ile), _message(ile._message), _file(ile._file) {} ///\e explicit IOParameterError(const char *the_message) : _message(the_message) {} ///\e IOParameterError(const char *file_name, const char *the_message) : _message(the_message), _file(file_name) {} ///\e void message(const std::string& message) { _message.set(message); } ///\e void file(const std::string &file) { _file.set(file); } ///\e const char* message() const { if (_message.valid()) { return _message.get().c_str(); } else { return 0; } } /// \brief Returns the filename. /// /// Returns \e null if the filename was not specified. const char* file() const { if (_file.valid()) { return _file.get().c_str(); } else { return 0; } } ///\e virtual const char* what() const throw() { try { std::ostringstream ostr; if (message()) ostr << message(); if (file()) ostr << "(when reading file '" << file() << "')"; _message_holder.set(ostr.str()); } catch (...) {} if( _message_holder.valid() ) return _message_holder.get().c_str(); return exceptionName(); } virtual const char* exceptionName() const { return "lemon::IOParameterError"; } virtual ~IOParameterError() throw() {} }; ///\e class AssertionFailedError : public LogicError { protected: const char *assertion; const char *file; int line; const char *function; const char *message; mutable ExceptionMember _message_holder; public: ///\e AssertionFailedError(const char *_file, int _line, const char *func, const char *msg, const char *_assertion = 0) : assertion(_assertion), file(_file), line(_line), function(func), message(msg) {} ///\e const char* get_assertion() const { return assertion; } ///\e const char* get_message() const { return message; } ///\e const char* get_file() const { return file; } ///\e const char* get_function() const { return function; } ///\e int get_line() const { return line; } virtual const char* what() const throw() { try { std::ostringstream ostr; ostr << file << ":" << line << ": "; if( function ) ostr << function << ": "; ostr << message; if( assertion ) ostr << " (assertion '" << assertion << "' failed)"; _message_holder.set(ostr.str()); return ostr.str().c_str(); } catch(...) {} if( _message_holder.valid() ) return _message_holder.get().c_str(); return exceptionName(); } virtual const char* exceptionName() const { return "lemon::AssertionFailedError"; } virtual ~AssertionFailedError() throw() {} }; /**************** Macros ****************/ inline void assert_fail(const char *file, int line, const char *func, const char *message, const char *assertion = 0, bool do_abort=true) { using namespace std; cerr << file << ":" << line << ": "; if( func ) cerr << func << ": "; cerr << message; if( assertion ) cerr << " (assertion '" << assertion << "' failed)"; cerr << endl; if(do_abort) abort(); } inline void assert_fail_throw(const char *file, int line, const char *func, const char *message, const char *assertion = 0, bool = true) { throw AssertionFailedError(file, line, func, message, assertion); } /// @} } #endif // LEMON_ERROR_H #undef LEMON_ASSERT #undef LEMON_FIXME #ifndef LEMON_ASSERT_ABORT # define LEMON_ASSERT_ABORT 1 #endif #ifndef LEMON_ASSERT_HANDLER # ifdef LEMON_ASSERT_EXCEPTION # define LEMON_ASSERT_HANDLER ::lemon::assert_fail_throw # else # define LEMON_ASSERT_HANDLER ::lemon::assert_fail # endif #endif #if defined(NDEBUG) || defined(LEMON_DISABLE_ASSERTS) # define LEMON_ASSERT(exp, msg) (static_cast (0)) #else /** * \brief Macro for assertions with customizable message * * Macro for assertions with customizable message. * * The behaviour can be customized with LEMON_ASSERT_HANDLER, * LEMON_ASSERT_EXCEPTION and LEMON_ASSERT_ABORT defines. Asserts can be * disabled by defining either NDEBUG or LEMON_DISABLE_ASSERTS macros. * * \todo We should provide some way to reset to the default behaviour, * shouldn't we? * * \todo This whole 'assert' business should be placed in a separate * include file. * * \todo __PRETTY_FUNCTION__ should be replaced by something * compiler-independent, like BOOST_CURRENT_FUNCTION */ # define LEMON_ASSERT(exp, msg) \ (static_cast (!!(exp) ? 0 : ( \ LEMON_ASSERT_HANDLER(__FILE__, __LINE__, \ __PRETTY_FUNCTION__, \ (msg), #exp, LEMON_ASSERT_ABORT), 0))) #endif // NDEBUG || LEMON_DISABLE_ASSERTS /** * \brief Macro for mark not yet implemented features. * * \todo Is this the right place for this? It should be used only in * modules under development. * * \todo __PRETTY_FUNCTION__ should be replaced by something * compiler-independent, like BOOST_CURRENT_FUNCTION */ # define LEMON_FIXME(msg) \ (LEMON_ASSERT_HANDLER(__FILE__, __LINE__, __PRETTY_FUNCTION__, \ "FIXME: " msg))