alpar@906: /* -*- C++ -*- alpar@906: * alpar@1956: * This file is a part of LEMON, a generic C++ optimization library alpar@1956: * alpar@2391: * Copyright (C) 2003-2007 alpar@1956: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport alpar@1956: * (Egervary Research Group on Combinatorial Optimization, EGRES). alpar@906: * alpar@906: * Permission to use, modify and distribute this software is granted alpar@906: * provided that this copyright notice appears in all copies. For alpar@906: * precise terms see the accompanying LICENSE file. alpar@906: * alpar@906: * This software is provided "AS IS" with no warranty of any kind, alpar@906: * express or implied, and with no claim as to its suitability for any alpar@906: * purpose. alpar@906: * alpar@906: */ alpar@883: alpar@921: #ifndef LEMON_ERROR_H alpar@921: #define LEMON_ERROR_H alpar@883: alpar@1151: //! \ingroup exceptions alpar@883: //! \file klao@1067: //! \brief Basic exception classes and error handling. alpar@883: alpar@883: #include alpar@883: #include alpar@883: #include klao@1067: #include klao@1067: #include klao@1157: #include alpar@883: alpar@921: namespace lemon { alpar@883: deba@1207: /// \addtogroup exceptions deba@1207: /// @{ deba@1207: deba@1207: /// \brief Exception safe wrapper class. deba@1207: /// deba@1207: /// Exception safe wrapper class to implement the members of exceptions. deba@1207: template deba@1207: class ExceptionMember { deba@1207: public: deba@1207: typedef _Type Type; deba@1207: deba@1207: ExceptionMember() throw () { deba@1207: try { deba@1207: ptr.reset(new Type()); deba@1207: } catch (...) {} deba@1207: } deba@1207: deba@1207: ExceptionMember(const Type& type) throw () { deba@1207: try { deba@1207: ptr.reset(new Type()); deba@1207: if (ptr.get() == 0) return; deba@1207: *ptr = type; deba@1207: } catch (...) {} deba@1207: } deba@1207: deba@1207: ExceptionMember(const ExceptionMember& copy) throw() { deba@1207: try { deba@1207: if (!copy.valid()) return; deba@1207: ptr.reset(new Type()); deba@1207: if (ptr.get() == 0) return; deba@1207: *ptr = copy.get(); deba@1207: } catch (...) {} deba@1207: } deba@1207: deba@1207: ExceptionMember& operator=(const ExceptionMember& copy) { deba@1207: if (ptr.get() == 0) return; deba@1207: try { deba@1207: if (!copy.valid()) return; deba@1207: *ptr = copy.get(); deba@1207: } catch (...) {} deba@1207: } deba@1207: deba@1207: void set(const Type& type) { deba@1207: if (ptr.get() == 0) return; deba@1207: try { deba@1207: *ptr = type; deba@1207: } catch (...) {} deba@1207: } deba@1207: deba@1207: const Type& get() const { deba@1207: return *ptr; deba@1207: } deba@1207: deba@1207: bool valid() const { deba@1207: return ptr.get() != 0; deba@1207: } deba@1207: deba@1207: private: deba@1207: std::auto_ptr<_Type> ptr; deba@1207: }; alpar@1151: klao@1056: /// Exception-safe convenient "error message" class. klao@1067: klao@1067: /// Helper class which provides a convenient ostream-like (operator << klao@1067: /// based) interface to create a string message. Mostly useful in klao@1067: /// exception classes (therefore the name). klao@1056: class ErrorMessage { klao@1056: protected: klao@1067: ///\e alpar@1536: alpar@1536: ///\todo The good solution is boost::shared_ptr... alpar@1536: /// klao@1157: mutable klao@1157: std::auto_ptr buf; klao@1056: klao@1067: ///\e klao@1056: bool init() throw() { klao@1056: try { klao@1056: buf.reset(new std::ostringstream); klao@1056: } klao@1056: catch(...) { klao@1056: buf.reset(); klao@1056: } klao@1157: return buf.get(); klao@1056: } klao@1056: klao@1056: public: klao@1056: klao@1067: ///\e klao@1056: ErrorMessage() throw() { init(); } klao@1056: klao@1157: ErrorMessage(const ErrorMessage& em) throw() : buf(em.buf) { } klao@1157: klao@1067: ///\e deba@2386: ErrorMessage(const char *msg) throw() { klao@1056: init(); deba@2386: *this << msg; klao@1056: } klao@1056: klao@1067: ///\e deba@2386: ErrorMessage(const std::string &msg) throw() { klao@1056: init(); deba@2386: *this << msg; klao@1056: } klao@1056: klao@1067: ///\e klao@1056: template klao@1056: ErrorMessage& operator<<(const T &t) throw() { klao@1157: if( ! buf.get() ) return *this; klao@1056: klao@1056: try { klao@1056: *buf << t; klao@1056: } klao@1056: catch(...) { klao@1056: buf.reset(); klao@1056: } deba@1207: return *this; klao@1056: } klao@1056: klao@1067: ///\e klao@1056: const char* message() throw() { klao@1157: if( ! buf.get() ) return 0; klao@1056: klao@1056: const char* mes = 0; klao@1056: try { klao@1056: mes = buf->str().c_str(); klao@1056: } klao@1056: catch(...) {} klao@1056: return mes; klao@1056: } klao@1056: klao@1056: }; klao@1056: alpar@883: /** alpar@883: * \brief Generic exception class. alpar@883: * klao@1056: * Base class for exceptions used in LEMON. alpar@883: */ klao@1067: class Exception : public std::exception { alpar@883: public: klao@1067: ///\e klao@1120: Exception() {} klao@1067: ///\e alpar@883: virtual ~Exception() throw() {} klao@1067: ///\e alpar@883: virtual const char* what() const throw() { alpar@2151: return "lemon::Exception"; klao@1056: } klao@1056: }; klao@1056: klao@1120: /** klao@1120: * \brief One of the two main subclasses of \ref Exception. klao@1120: * klao@1120: * Logic errors represent problems in the internal logic of a program; klao@1120: * in theory, these are preventable, and even detectable before the klao@1120: * program runs (e.g., violations of class invariants). klao@1120: * alpar@1125: * A typical example for this is \ref UninitializedParameter. klao@1120: */ klao@1056: class LogicError : public Exception { klao@1067: public: alpar@2151: virtual const char* what() const throw() { klao@1120: return "lemon::LogicError"; klao@1120: } klao@1056: }; klao@1056: alpar@1125: /** alpar@1125: * \brief \ref Exception for uninitialized parameters. alpar@1125: * alpar@1125: * This error represents problems in the initialization alpar@1125: * of the parameters of the algorithms. alpar@1125: */ alpar@1125: class UninitializedParameter : public LogicError { alpar@1125: public: alpar@2151: virtual const char* what() const throw() { alpar@1125: return "lemon::UninitializedParameter"; alpar@1125: } alpar@1125: }; alpar@1125: klao@1120: klao@1120: /** klao@1120: * \brief One of the two main subclasses of \ref Exception. klao@1120: * klao@1120: * Runtime errors represent problems outside the scope of a program; klao@1120: * they cannot be easily predicted and can generally only be caught as klao@1120: * the program executes. klao@1120: */ klao@1056: class RuntimeError : public Exception { klao@1067: public: alpar@2151: virtual const char* what() const throw() { klao@1120: return "lemon::RuntimeError"; klao@1120: } klao@1056: }; klao@1056: klao@1067: ///\e klao@1056: class RangeError : public RuntimeError { klao@1067: public: alpar@2151: virtual const char* what() const throw() { klao@1120: return "lemon::RangeError"; klao@1120: } klao@1056: }; klao@1056: alpar@1061: ///\e alpar@2153: class IoError : public RuntimeError { klao@1067: public: alpar@2151: virtual const char* what() const throw() { alpar@2153: return "lemon::IoError"; klao@1120: } klao@1056: }; klao@1056: alpar@1061: ///\e alpar@2153: class DataFormatError : public IoError { klao@1067: protected: deba@1207: ExceptionMember _message; deba@1207: ExceptionMember _file; klao@1120: int _line; klao@1157: deba@1207: mutable ExceptionMember _message_holder; klao@1067: public: klao@1157: klao@1157: DataFormatError(const DataFormatError &dfe) : alpar@2153: IoError(dfe), _message(dfe._message), _file(dfe._file), deba@1207: _line(dfe._line) {} klao@1157: klao@1067: ///\e klao@1120: explicit DataFormatError(const char *the_message) klao@1120: : _message(the_message), _line(0) {} deba@1207: klao@1067: ///\e klao@1056: DataFormatError(const std::string &file_name, int line_num, klao@1120: const char *the_message) klao@1120: : _message(the_message), _line(line_num) { file(file_name); } klao@1056: klao@1067: ///\e deba@2386: void line(int ln) { _line = ln; } klao@1067: ///\e deba@2386: void message(const std::string& msg) { _message.set(msg); } klao@1120: ///\e deba@2386: void file(const std::string &fl) { _file.set(fl); } deba@1207: deba@1207: ///\e deba@1207: int line() const { return _line; } deba@1207: ///\e deba@1207: const char* message() const { deba@1207: if (_message.valid() && !_message.get().empty()) { deba@1207: return _message.get().c_str(); deba@1207: } else { deba@1207: return 0; klao@1056: } alpar@883: } alpar@883: klao@1067: /// \brief Returns the filename. klao@1067: /// deba@1207: /// Returns \e null if the filename was not specified. klao@1120: const char* file() const { deba@1207: if (_file.valid() && !_file.get().empty()) { deba@1207: return _file.get().c_str(); deba@1207: } else { deba@1207: return 0; deba@1207: } klao@1067: } klao@1067: klao@1067: ///\e klao@1067: virtual const char* what() const throw() { klao@1056: try { klao@1056: std::ostringstream ostr; alpar@2151: ostr << "lemon:DataFormatError" << ": "; deba@1207: if (message()) ostr << message(); deba@1207: if( file() || line() != 0 ) { klao@1056: ostr << " ("; deba@1207: if( file() ) ostr << "in file '" << file() << "'"; deba@1207: if( file() && line() != 0 ) ostr << " "; deba@1207: if( line() != 0 ) ostr << "at line " << line(); klao@1067: ostr << ")"; klao@1056: } deba@1207: _message_holder.set(ostr.str()); klao@1056: } deba@1207: catch (...) {} deba@1207: if( _message_holder.valid()) return _message_holder.get().c_str(); alpar@2151: return "lemon:DataFormatError"; klao@1056: } klao@1067: klao@1067: virtual ~DataFormatError() throw() {} alpar@883: }; alpar@883: deba@1746: ///\e alpar@2153: class FileOpenError : public IoError { deba@1746: protected: deba@1746: ExceptionMember _file; deba@1746: deba@1746: mutable ExceptionMember _message_holder; deba@1746: public: deba@1746: deba@1746: FileOpenError(const FileOpenError &foe) : alpar@2153: IoError(foe), _file(foe._file) {} deba@1746: deba@1746: ///\e deba@2386: explicit FileOpenError(const std::string& fl) deba@2386: : _file(fl) {} deba@1746: deba@1746: deba@1746: ///\e deba@2386: void file(const std::string &fl) { _file.set(fl); } deba@1746: deba@1746: /// \brief Returns the filename. deba@1746: /// deba@1746: /// Returns \e null if the filename was not specified. deba@1746: const char* file() const { deba@1746: if (_file.valid() && !_file.get().empty()) { deba@1746: return _file.get().c_str(); deba@1746: } else { deba@1746: return 0; deba@1746: } deba@1746: } deba@1746: deba@1746: ///\e deba@1746: virtual const char* what() const throw() { deba@1746: try { deba@1746: std::ostringstream ostr; alpar@2151: ostr << "lemon::FileOpenError" << ": "; deba@1746: ostr << "Cannot open file - " << file(); deba@1746: _message_holder.set(ostr.str()); deba@1746: } deba@1746: catch (...) {} deba@1746: if( _message_holder.valid()) return _message_holder.get().c_str(); deba@1746: return "lemon::FileOpenError"; deba@1746: } deba@1746: virtual ~FileOpenError() throw() {} deba@1746: }; deba@1746: alpar@2153: class IoParameterError : public IoError { deba@1207: protected: deba@1207: ExceptionMember _message; deba@1207: ExceptionMember _file; deba@1207: deba@1207: mutable ExceptionMember _message_holder; deba@1207: public: deba@1207: alpar@2153: IoParameterError(const IoParameterError &ile) : alpar@2153: IoError(ile), _message(ile._message), _file(ile._file) {} deba@1207: deba@1207: ///\e alpar@2153: explicit IoParameterError(const char *the_message) deba@1213: : _message(the_message) {} deba@1207: deba@1207: ///\e alpar@2153: IoParameterError(const char *file_name, const char *the_message) deba@1393: : _message(the_message), _file(file_name) {} deba@1207: deba@1207: ///\e deba@2386: void message(const std::string& msg) { _message.set(msg); } deba@1207: ///\e deba@2386: void file(const std::string &fl) { _file.set(fl); } deba@1207: deba@1207: ///\e deba@1207: const char* message() const { deba@1207: if (_message.valid()) { deba@1207: return _message.get().c_str(); deba@1207: } else { deba@1207: return 0; deba@1207: } deba@1207: } deba@1207: deba@1207: /// \brief Returns the filename. deba@1207: /// deba@1207: /// Returns \e null if the filename was not specified. deba@1207: const char* file() const { deba@1207: if (_file.valid()) { deba@1207: return _file.get().c_str(); deba@1207: } else { deba@1207: return 0; deba@1207: } deba@1207: } deba@1207: deba@1207: ///\e deba@1207: virtual const char* what() const throw() { deba@1207: try { deba@1207: std::ostringstream ostr; deba@1207: if (message()) ostr << message(); deba@1207: if (file()) ostr << "(when reading file '" << file() << "')"; deba@1207: _message_holder.set(ostr.str()); deba@1207: } deba@1207: catch (...) {} deba@1207: if( _message_holder.valid() ) return _message_holder.get().c_str(); alpar@2153: return "lemon:IoParameterError"; deba@1207: } alpar@2153: virtual ~IoParameterError() throw() {} deba@1207: }; deba@1207: klao@1056: klao@1068: ///\e klao@1067: class AssertionFailedError : public LogicError { klao@1067: protected: klao@1067: const char *assertion; klao@1067: const char *file; klao@1067: int line; klao@1067: const char *function; klao@1067: const char *message; deba@1213: deba@1213: mutable ExceptionMember _message_holder; klao@1067: public: klao@1067: ///\e klao@1067: AssertionFailedError(const char *_file, int _line, const char *func, klao@1067: const char *msg, const char *_assertion = 0) : klao@1067: assertion(_assertion), file(_file), line(_line), function(func), klao@1067: message(msg) {} klao@1067: klao@1067: ///\e klao@1067: const char* get_assertion() const { return assertion; } klao@1067: ///\e klao@1067: const char* get_message() const { return message; } klao@1067: ///\e klao@1067: const char* get_file() const { return file; } klao@1067: ///\e klao@1067: const char* get_function() const { return function; } klao@1067: ///\e klao@1067: int get_line() const { return line; } klao@1067: klao@1067: klao@1067: virtual const char* what() const throw() { klao@1067: try { klao@1067: std::ostringstream ostr; klao@1067: ostr << file << ":" << line << ": "; klao@1067: if( function ) klao@1067: ostr << function << ": "; klao@1067: ostr << message; klao@1067: if( assertion ) deba@1213: ostr << " (assertion '" << assertion << "' failed)"; deba@1213: _message_holder.set(ostr.str()); klao@1209: return ostr.str().c_str(); klao@1067: } klao@1067: catch(...) {} deba@1213: if( _message_holder.valid() ) return _message_holder.get().c_str(); klao@1067: return "lemon::AssertionFailedError"; klao@1067: } alpar@2151: virtual ~AssertionFailedError() throw() {} klao@1067: }; klao@1067: klao@1056: klao@1056: /**************** Macros ****************/ klao@1056: klao@1056: deba@1785: template deba@1785: inline void assert_fail(const char *file, int line, const char *func, deba@1785: Exception exception, const char *assertion = 0, klao@1067: bool do_abort=true) klao@1067: { klao@1067: using namespace std; klao@1067: cerr << file << ":" << line << ": "; klao@1067: if( func ) klao@1067: cerr << func << ": "; deba@1785: cerr << exception.what(); deba@1785: if( assertion ) deba@1785: cerr << " (assertion '" << assertion << "' failed)"; deba@1785: cerr << endl; deba@1785: if(do_abort) deba@1785: abort(); deba@1785: } deba@1785: deba@1785: template <> deba@2150: inline void assert_fail(const char *file, int line, deba@2150: const char *func, deba@2150: const char *message, deba@2150: const char *assertion, deba@2150: bool do_abort) deba@1785: { deba@1785: using namespace std; deba@1785: cerr << file << ":" << line << ": "; deba@1785: if( func ) deba@1785: cerr << func << ": "; klao@1067: cerr << message; klao@1067: if( assertion ) klao@1067: cerr << " (assertion '" << assertion << "' failed)"; klao@1067: cerr << endl; klao@1067: if(do_abort) klao@1067: abort(); klao@1067: } klao@1056: deba@1785: template deba@1785: inline void assert_fail_failure(const char *file, int line, const char *func, deba@1785: Exception exception, deba@1785: const char *assertion = 0, deba@1785: bool = true) deba@1785: { deba@1785: throw AssertionFailedError(file, line, func, exception.what(), assertion); deba@1785: } deba@1785: deba@1785: template <> deba@1785: inline void assert_fail_failure(const char *file, int line, deba@1785: const char *func, deba@1785: const char *message, deba@1785: const char *assertion, deba@1785: bool) deba@1785: { deba@1785: throw AssertionFailedError(file, line, func, message, assertion); deba@1785: } deba@1785: deba@1785: template deba@1785: inline void assert_fail_exception(const char *file, int line, const char *func, deba@1785: Exception exception, deba@1785: const char *assertion = 0, bool = true) deba@1785: { deba@1785: throw exception; deba@1785: } deba@1785: deba@1785: template <> deba@1785: inline void assert_fail_exception(const char *file, int line, deba@1785: const char *func, deba@1785: const char *message, deba@1785: const char *assertion, deba@1785: bool) klao@1067: { klao@1067: throw AssertionFailedError(file, line, func, message, assertion); klao@1067: } klao@1056: alpar@1151: /// @} alpar@883: alpar@883: } alpar@921: #endif // LEMON_ERROR_H klao@1067: klao@1067: #undef LEMON_ASSERT klao@1067: #undef LEMON_FIXME klao@1067: deba@2150: #ifdef LEMON_ENABLE_ASSERTS deba@2150: # define LEMON_ASSERT_ABORT deba@2150: #endif deba@2150: deba@2150: #ifndef LEMON_ASSERT_DO_ABORT deba@2150: # define LEMON_ASSERT_DO_ABORT 1 klao@1067: #endif klao@1067: klao@1067: #ifndef LEMON_ASSERT_HANDLER deba@1785: # if defined LEMON_ASSERT_EXCEPTION deba@1785: # define LEMON_ASSERT_HANDLER ::lemon::assert_fail_exception deba@1785: # elif defined LEMON_ASSERT_FAILURE deba@1785: # define LEMON_ASSERT_HANDLER ::lemon::assert_fail_failure deba@2150: # elif defined LEMON_ASSERT_ABORT deba@2150: # define LEMON_ASSERT_HANDLER ::lemon::assert_fail klao@1120: # else deba@2150: # define LEMON_DISABLE_ASSERTS klao@1120: # endif klao@1067: #endif klao@1067: deba@2150: #ifdef DOXYGEN klao@1067: deba@2150: /// \brief Macro for assertions with customizable message deba@2150: /// deba@2150: /// Macro for assertions with customizable message. deba@2150: /// deba@2150: /// The assertions are disabled in the default behaviour. You can alpar@2175: /// enable the assertions with the alpar@2175: /// \code alpar@2175: /// #define LEMON_ENABLE_ASSERTS alpar@2175: /// \endcode alpar@2175: /// Then an assert deba@2150: /// provides a log on the standard error about the assertion and aborts alpar@2175: /// the program if LEMON_ASSERT_DO_ABORT is also defined (otherwise the alpar@2175: /// program keeps on running). alpar@2175: /// By defining LEMON_ASSERT_FAILURE or alpar@2175: /// LEMON_ASSERT_EXCEPTION, you can set other behaviour to the alpar@2175: /// assertions. In case LEMON_ASSERT_FAILURE is given, LEMON_ASSERT alpar@2175: /// will always throw an \c AssertionFailedError exception with alpar@2175: /// the \c msg error message. By using alpar@2175: /// LEMON_ASSERT_EXCEPTION, one can define an arbitrary exception to be thrown. deba@2150: /// deba@2150: /// The LEMON_ASSERT macro should be called with the \c exp parameter deba@2150: /// which should be an expression convertible to bool. If the given deba@2150: /// parameter is false the assertion is raised and one of the assertion deba@2150: /// behaviour will be activated. The \c msg should be either a const deba@2150: /// char* message or an exception. When the \c msg is an exception the deba@2150: /// \ref "Exception::what" what() function is called to retrieve and deba@2150: /// display the error message. deba@2150: /// deba@2150: /// \todo We should provide some way to reset to the default behaviour, deba@2150: /// shouldn't we? deba@2150: /// deba@2150: /// \todo This whole 'assert' business should be placed in a separate deba@2150: /// include file. The boost assert is not guarded by header sentries deba@2150: /// which may help to change the behaviour of the assertions in deba@2150: /// the files. deba@2150: /// deba@2150: /// \todo __PRETTY_FUNCTION__ should be replaced by something deba@2150: /// compiler-independent, like BOOST_CURRENT_FUNCTION klao@1067: klao@1067: # define LEMON_ASSERT(exp, msg) \ klao@1067: (static_cast (!!(exp) ? 0 : ( \ klao@1067: LEMON_ASSERT_HANDLER(__FILE__, __LINE__, \ klao@1067: __PRETTY_FUNCTION__, \ deba@2150: msg, #exp, LEMON_ASSERT_DO_ABORT), 0))) klao@1067: deba@2150: #else deba@2150: # if defined LEMON_DISABLE_ASSERTS deba@2150: deba@2150: # define LEMON_ASSERT(exp, msg) (static_cast (0)) deba@2150: deba@2150: # else deba@2150: # define LEMON_ASSERT(exp, msg) \ deba@2150: (static_cast (!!(exp) ? 0 : ( \ deba@2150: LEMON_ASSERT_HANDLER(__FILE__, __LINE__, \ deba@2150: __PRETTY_FUNCTION__, \ deba@2150: msg, #exp, LEMON_ASSERT_DO_ABORT), 0))) deba@2150: # endif deba@2150: #endif klao@1067: klao@1067: /** klao@1067: * \brief Macro for mark not yet implemented features. klao@1067: * klao@1067: * \todo Is this the right place for this? It should be used only in klao@1067: * modules under development. klao@1067: * klao@1067: * \todo __PRETTY_FUNCTION__ should be replaced by something zsuzska@1274: * compiler-independent, like BOOST_CURRENT_FUNCTION klao@1067: */ klao@1067: klao@1067: # define LEMON_FIXME(msg) \ klao@1067: (LEMON_ASSERT_HANDLER(__FILE__, __LINE__, __PRETTY_FUNCTION__, \ klao@1067: "FIXME: " msg))