alpar@906: /* -*- C++ -*-
klao@1157:  *
alpar@921:  * src/lemon/error.h - Part of LEMON, a generic C++ optimization library
alpar@906:  *
alpar@1164:  * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi
klao@1157:  * Kutatocsoport (Egervary Combinatorial Optimization Research Group,
klao@1157:  * 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 <exception>
alpar@883: #include <string>
alpar@883: #include <sstream>
klao@1067: #include <iostream>
klao@1067: #include <cstdlib>
klao@1157: #include <memory>
alpar@883: 
alpar@921: namespace lemon {
alpar@883: 
alpar@1151: /// \addtogroup exceptions
alpar@1151: /// @{
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 
klao@1157:     ///\todo The good solution is boost:shared_ptr...
klao@1157:     mutable
klao@1157:     std::auto_ptr<std::ostringstream> 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 
klao@1056:     ErrorMessage(const char *message) throw() {
klao@1056:       init();
klao@1056:       *this << message;
klao@1056:     }
klao@1056: 
klao@1067:     ///\e 
klao@1056:     ErrorMessage(const std::string &message) throw() {
klao@1056:       init();
klao@1056:       *this << message;
klao@1056:     }
klao@1056: 
klao@1067:     ///\e 
klao@1056:     template <typename T>
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:       }
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@1120: 
klao@1120:     ///\e
klao@1120:     virtual const char* exceptionName() const {
klao@1120:       return "lemon::Exception";
klao@1120:     }
alpar@883:     
klao@1067:     ///\e 
alpar@883:     virtual const char* what() const throw() {
klao@1120:       return exceptionName();
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:
klao@1120:     virtual const char* exceptionName() const {
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@1125:     virtual const char* exceptionName() const {
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:
klao@1120:     virtual const char* exceptionName() const {
klao@1120:       return "lemon::RuntimeError";
klao@1120:     }
klao@1056:   };
klao@1056: 
klao@1067:   ///\e
klao@1056:   class RangeError : public RuntimeError {
klao@1067:   public:
klao@1120:     virtual const char* exceptionName() const {
klao@1120:       return "lemon::RangeError";
klao@1120:     }
klao@1056:   };
klao@1056: 
alpar@1061:   ///\e 
klao@1056:   class IOError : public RuntimeError {
klao@1067:   public:
klao@1120:     virtual const char* exceptionName() const {
klao@1120:       return "lemon::IOError";
klao@1120:     }
klao@1056:   };
klao@1056: 
alpar@1061:   ///\e 
klao@1056:   class DataFormatError : public IOError {
klao@1067:   protected:
klao@1120:     const char *_message;
klao@1120:     int _line;
klao@1157: 
klao@1157:     ///\todo Much better solution is boost::shared_ptr
klao@1157:     mutable
klao@1157:     std::auto_ptr<std::string> _file;
klao@1067: 
klao@1067:   public:
klao@1157: 
klao@1157:     DataFormatError(const DataFormatError &dfe) : 
klao@1157:       IOError(dfe), _message(dfe._message), _line(dfe._line),
klao@1157:       _file(dfe._file) {}
klao@1157: 
klao@1067:     ///\e 
klao@1120:     explicit DataFormatError(const char *the_message)
klao@1120:       : _message(the_message), _line(0) {}
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 
klao@1120:     void line(int line_num) { _line=line_num; }
klao@1067:     ///\e 
klao@1120:     void message(char *the_message) { _message=the_message; }
klao@1120:     ///\e 
klao@1120:     void file(const std::string &file_name) {
klao@1056:       try {
klao@1120: 	_file.reset(new std::string);
klao@1120: 	*_file = file_name;
klao@1056:       }
klao@1056:       catch(...) {
klao@1120: 	_file.reset();
klao@1056:       }
alpar@883:     }
alpar@883: 
klao@1067:     ///\e
klao@1120:     int line() const { return _line; }
klao@1120:     ///\e
klao@1120:     const char* message() const { return _message; }
klao@1067: 
klao@1067:     /// \brief Returns the filename.
klao@1067:     ///
klao@1068:     /// Returns \e "(unknown)" if the filename was not specified.
klao@1120:     const char* file() const {
klao@1157:       if( _file.get() )
klao@1120: 	return _file->c_str();
klao@1067:       else
klao@1067: 	return "(unknown)";
klao@1067:     }
klao@1067: 
klao@1067:     ///\e 
klao@1067:     virtual const char* what() const throw() {
klao@1056:       const char *mes = 0;
klao@1056:       try {
klao@1056: 	std::ostringstream ostr;
klao@1120: 	ostr << _message;
klao@1157: 	if( _file.get() || _line ) {
klao@1056: 	  ostr << " (";
klao@1157: 	  if( _file.get() ) ostr << "in file '" << *_file << "'";
klao@1157: 	  if( _file.get() && _line ) ostr << " ";
klao@1120: 	  if( _line ) ostr << "at line " << _line;
klao@1067: 	  ostr << ")";
klao@1056: 	}
klao@1056: 	mes = ostr.str().c_str();
klao@1056:       }
klao@1056:       catch(...) {}
klao@1056:       if( mes ) return mes;
klao@1120:       return exceptionName();
klao@1120:     }
klao@1120: 
klao@1120:     virtual const char* exceptionName() const {
klao@1056:       return "lemon::DataFormatError";
klao@1056:     }
klao@1067: 
klao@1067:     virtual ~DataFormatError() throw() {}
alpar@883:   };
alpar@883: 
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;
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:       const char *mes = 0;
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 )
klao@1067: 	  ostr << " (assertion '" << assertion << "' failed)";
klao@1067: 	mes = ostr.str().c_str();
klao@1067:       }
klao@1067:       catch(...) {}
klao@1067:       if( mes ) return mes;
klao@1120:       return exceptionName();
klao@1120:     }
klao@1120: 
klao@1120:     virtual const char* exceptionName() const {
klao@1067:       return "lemon::AssertionFailedError";
klao@1067:     }
klao@1067: 
klao@1067:     virtual ~AssertionFailedError() throw() {}
klao@1067:   };
klao@1067: 
klao@1056: 
klao@1056:   /****************  Macros  ****************/
klao@1056: 
klao@1056: 
klao@1067:   inline
klao@1067:   void assert_fail(const char *file, int line, const char *func,
klao@1067: 		   const char *message, 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 << ": ";
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: 
klao@1067:   inline
klao@1067:   void assert_fail_throw(const char *file, int line, const char *func,
klao@1067: 		   const char *message, const char *assertion = 0,
klao@1067: 		   bool = true)
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: 
klao@1067: #ifndef LEMON_ASSERT_ABORT
klao@1067: #  define LEMON_ASSERT_ABORT 1
klao@1067: #endif
klao@1067: 
klao@1067: #ifndef LEMON_ASSERT_HANDLER
klao@1120: #  ifdef LEMON_ASSERT_EXCEPTION
klao@1120: #    define LEMON_ASSERT_HANDLER ::lemon::assert_fail_throw
klao@1120: #  else
klao@1120: #    define LEMON_ASSERT_HANDLER ::lemon::assert_fail
klao@1120: #  endif
klao@1067: #endif
klao@1067: 
klao@1067: #if defined(NDEBUG) || defined(LEMON_DISABLE_ASSERTS)
klao@1067: 
klao@1067: #  define LEMON_ASSERT(exp, msg)  (static_cast<void> (0))
klao@1067: 
klao@1067: #else
klao@1067: 
klao@1067: /**
klao@1067:  * \brief Macro for assertions with customizable message
klao@1067:  *
klao@1120:  * Macro for assertions with customizable message.
klao@1120:  *
klao@1120:  * The behaviour can be customized with LEMON_ASSERT_HANDLER,
klao@1120:  * LEMON_ASSERT_EXCEPTION and LEMON_ASSERT_ABORT defines. Asserts can be
klao@1120:  * disabled by defining either NDEBUG or LEMON_DISABLE_ASSERTS macros.
klao@1120:  *
klao@1120:  * \todo We should provide some way to reset to the default behaviour,
klao@1120:  * shouldn't we?
klao@1120:  *
klao@1120:  * \todo This whole 'assert' business should be placed in a separate
klao@1120:  * include file.
klao@1120:  *
klao@1067:  * \todo __PRETTY_FUNCTION__ should be replaced by something
klao@1067:  * compiler-independant, like BOOST_CURRENT_FUNCTION
klao@1067:  */
klao@1067: 
klao@1067: #  define LEMON_ASSERT(exp, msg)                 \
klao@1067:      (static_cast<void> (!!(exp) ? 0 : (         \
klao@1067:        LEMON_ASSERT_HANDLER(__FILE__, __LINE__,  \
klao@1067:                             __PRETTY_FUNCTION__, \
klao@1067: 			    (msg), #exp, LEMON_ASSERT_ABORT), 0)))
klao@1067: 
klao@1120: #endif // NDEBUG || LEMON_DISABLE_ASSERTS
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
klao@1067:  * compiler-independant, 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))