diff --git a/lemon/Makefile.am b/lemon/Makefile.am --- a/lemon/Makefile.am +++ b/lemon/Makefile.am @@ -17,6 +17,7 @@ lemon_HEADERS += \ lemon/arg_parser.h \ + lemon/assert.h \ lemon/bfs.h \ lemon/bin_heap.h \ lemon/dfs.h \ diff --git a/lemon/arg_parser.h b/lemon/arg_parser.h --- a/lemon/arg_parser.h +++ b/lemon/arg_parser.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include ///\ingroup misc ///\file diff --git a/lemon/error.h b/lemon/assert.h copy from lemon/error.h copy to lemon/assert.h --- a/lemon/error.h +++ b/lemon/assert.h @@ -16,442 +16,57 @@ * */ -#ifndef LEMON_ERROR_H -#define LEMON_ERROR_H +#ifndef LEMON_ASSERT_H +#define LEMON_ASSERT_H /// \ingroup exceptions /// \file -/// \brief Basic exception classes and error handling. +/// \brief Extended assertion handling -#include -#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) throw() { - if (ptr.get() == 0) return; - try { - if (!copy.valid()) return; - *ptr = copy.get(); - } catch (...) {} - } - - void set(const Type& type) throw() { - if (ptr.get() == 0) return; - try { - *ptr = type; - } catch (...) {} - } - - const Type& get() const { - return *ptr; - } - - bool valid() const throw() { - 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 *msg) throw() { - init(); - *this << msg; - } - - ///\e - ErrorMessage(const std::string &msg) throw() { - init(); - *this << msg; - } - - ///\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; - } - - }; - - /// 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* what() const throw() { - return "lemon::Exception"; - } - }; - - /// 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* what() const throw() { - return "lemon::LogicError"; - } - }; - - /// \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* what() const throw() { - return "lemon::UninitializedParameter"; - } - }; - - - /// 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* what() const throw() { - return "lemon::RuntimeError"; - } - }; - - ///\e - class RangeError : public RuntimeError { - public: - virtual const char* what() const throw() { - return "lemon::RangeError"; - } - }; - - ///\e - class IoError : public RuntimeError { - public: - virtual const char* what() const throw() { - 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 ln) { _line = ln; } - ///\e - void message(const std::string& msg) { _message.set(msg); } - ///\e - void file(const std::string &fl) { _file.set(fl); } - - ///\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 << "lemon:DataFormatError" << ": "; - 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 "lemon:DataFormatError"; - } - - virtual ~DataFormatError() throw() {} - }; - - ///\e - class FileOpenError : public IoError { - protected: - ExceptionMember _file; - - mutable ExceptionMember _message_holder; - public: - - FileOpenError(const FileOpenError &foe) : - IoError(foe), _file(foe._file) {} - - ///\e - explicit FileOpenError(const std::string& fl) - : _file(fl) {} - - - ///\e - void file(const std::string &fl) { _file.set(fl); } - - /// \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 << "lemon::FileOpenError" << ": "; - ostr << "Cannot open file - " << file(); - _message_holder.set(ostr.str()); - } - catch (...) {} - if( _message_holder.valid()) return _message_holder.get().c_str(); - return "lemon::FileOpenError"; - } - virtual ~FileOpenError() throw() {} - }; - - class IoParameterError : public IoError { - protected: - ExceptionMember _message; - ExceptionMember _file; - - mutable ExceptionMember _message_holder; - public: - - IoParameterError(const IoParameterError &ile) : - IoError(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& msg) { _message.set(msg); } - ///\e - void file(const std::string &fl) { _file.set(fl); } - - ///\e - const char* message() const { - if (_message.valid()) { - return _message.get().c_str(); - } else { - return 0; - } - } - - /// \brief Returns the filename. - /// - /// Returns \c 0 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 "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; + 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) {} + AssertionFailedError(const char *file, int line, const char *function, + const char *msg, const char *assertion = 0) : + _assertion(assertion), _file(file), _line(line), + _function(function), _message(msg) {} ///\e - const char* get_assertion() const { return assertion; } + const char* assertion() const { return _assertion; } ///\e - const char* get_message() const { return message; } + const char* message() const { return _message; } ///\e - const char* get_file() const { return file; } + const char* file() const { return _file; } ///\e - const char* get_function() const { return function; } + const char* function() const { return _function; } ///\e - int get_line() const { return line; } + int 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)"; + 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(); } @@ -459,217 +74,291 @@ if( _message_holder.valid() ) return _message_holder.get().c_str(); return "lemon::AssertionFailedError"; } - virtual ~AssertionFailedError() throw() {} + virtual ~AssertionFailedError() throw() {} }; - /**************** Macros ****************/ - - - template - inline void assert_fail(const char *file, int line, - const char *func, - Exception exception, - const char *assertion = 0, - bool do_abort=true) + inline void assert_fail_log(const char *file, int line, + const char *function, + const std::exception& exception, + const char *assertion) { - using namespace std; - cerr << file << ":" << line << ": "; - if (func) - cerr << func << ": "; - cerr << exception.what(); + std::cerr << file << ":" << line << ": "; + if (function) + std::cerr << function << ": "; + std::cerr << exception.what(); if (assertion) - cerr << " (assertion '" << assertion << "' failed)"; - cerr << endl; - if (do_abort) - abort(); + std::cerr << " (assertion '" << assertion << "' failed)"; + std::cerr << std::endl; } - template <> - inline void assert_fail(const char *file, int line, - const char *func, - const char *message, - const char *assertion, - bool do_abort) + inline void assert_fail_log(const char *file, int line, const char *function, + const char *message, const char *assertion) { - using namespace std; - cerr << file << ":" << line << ": "; - if (func) - cerr << func << ": "; - cerr << message; + std::cerr << file << ":" << line << ": "; + if (function) + std::cerr << function << ": "; + std::cerr << message; if (assertion) - cerr << " (assertion '" << assertion << "' failed)"; - cerr << endl; - if (do_abort) - abort(); + std::cerr << " (assertion '" << assertion << "' failed)"; + std::cerr << std::endl; } - template <> - inline void assert_fail(const char *file, int line, - const char *func, - std::string message, - const char *assertion, - bool do_abort) + inline void assert_fail_log(const char *file, int line, const char *function, + const std::string& message, const char *assertion) { - assert_fail(file, line, func, message.c_str(), assertion, do_abort); + assert_fail_log(file, line, function, message.c_str(), assertion); + } + + inline void assert_fail_abort(const char *file, int line, + const char *function, + const std::exception& exception, + const char *assertion) + { + std::cerr << file << ":" << line << ": "; + if (function) + std::cerr << function << ": "; + std::cerr << exception.what(); + if (assertion) + std::cerr << " (assertion '" << assertion << "' failed)"; + std::cerr << std::endl; + std::abort(); + } + + inline void assert_fail_abort(const char *file, int line, + const char *function, const char* message, + const char *assertion) + { + std::cerr << file << ":" << line << ": "; + if (function) + std::cerr << function << ": "; + std::cerr << message; + if (assertion) + std::cerr << " (assertion '" << assertion << "' failed)"; + std::cerr << std::endl; + std::abort(); + } + + inline void assert_fail_abort(const char *file, int line, + const char *function, + const std::string& message, + const char *assertion) + { + assert_fail_abort(file, line, function, message.c_str(), assertion); + } + + inline void assert_fail_error(const char *file, int line, + const char *function, + const std::exception& exception, + const char *assertion) + { + throw AssertionFailedError(file, line, function, + exception.what(), assertion); + } + + inline void assert_fail_error(const char *file, int line, + const char *function, const char *message, + const char *assertion) + { + throw AssertionFailedError(file, line, function, message, assertion); + } + + inline void assert_fail_error(const char *file, int line, + const char *function, + const std::string& message, + const char *assertion) + { + assert_fail_error(file, line, function, message.c_str(), assertion); } template - inline void assert_fail_failure(const char *file, int line, const char *func, - Exception exception, - const char *assertion = 0, - bool = true) - { - throw AssertionFailedError(file, line, func, exception.what(), assertion); - } - - template <> - inline void assert_fail_failure(const char *file, int line, - const char *func, - const char *message, - const char *assertion, - bool) - { - throw AssertionFailedError(file, line, func, message, assertion); - } - - template <> - inline void assert_fail_failure(const char *file, int line, - const char *func, - std::string message, - const char *assertion, - bool) - { - assert_fail_failure(file, line, func, message.c_str(), assertion, true); - } - - template - inline void assert_fail_exception(const char *file, int line, const char *func, - Exception exception, - const char *assertion = 0, bool = true) + inline void assert_fail_exception(const char *, int, const char *, + const Exception& exception, + const char *, const std::exception* = + static_cast(0)) { throw exception; } - template <> - inline void assert_fail_exception(const char *file, int line, - const char *func, - const char *message, - const char *assertion, - bool) + inline void assert_fail_exception(const char *file, int line, + const char *function, const char *message, + const char *assertion) { - throw AssertionFailedError(file, line, func, message, assertion); + throw AssertionFailedError(file, line, function, message, assertion); } - template <> - inline void assert_fail_exception(const char *file, int line, - const char *func, - std::string message, - const char *assertion, - bool) + inline void assert_fail_exception(const char *file, int line, + const char *function, + const std::string& message, + const char *assertion) { - assert_fail_exception(file, line, func, message.c_str(), assertion, true); + assert_fail_exception(file, line, function, message.c_str(), assertion); } /// @} } -#endif // LEMON_ERROR_H +#endif // LEMON_ASSERT_H #undef LEMON_ASSERT #undef LEMON_FIXME -#ifdef LEMON_ENABLE_ASSERTS -# define LEMON_ASSERT_ABORT +#if (defined(LEMON_ASSERT_LOG) ? 1 : 0) + \ + (defined(LEMON_ASSERT_ABORT) ? 1 : 0) + \ + (defined(LEMON_ASSERT_ERROR) ? 1 : 0) + \ + (defined(LEMON_ASSERT_EXCEPTION) ? 1 : 0) + \ + (defined(LEMON_ASSERT_CUSTOM) ? 1 : 0) > 1 +#error "Lemon assertion system is not set properly" #endif -#ifndef LEMON_ASSERT_DO_ABORT -# define LEMON_ASSERT_DO_ABORT 1 +#if ((defined(LEMON_ASSERT_LOG) ? 1 : 0) + \ + (defined(LEMON_ASSERT_ABORT) ? 1 : 0) + \ + (defined(LEMON_ASSERT_ERROR) ? 1 : 0) + \ + (defined(LEMON_ASSERT_EXCEPTION) ? 1 : 0) + \ + (defined(LEMON_ASSERT_CUSTOM) ? 1 : 0) == 1 || \ + defined(LEMON_ENABLE_ASSERT)) && \ + defined(LEMON_DISABLE_ASSERTS) +#error "Lemon assertion system is not set properly" #endif -#ifndef LEMON_ASSERT_HANDLER -# if defined LEMON_ASSERT_EXCEPTION -# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_exception -# elif defined LEMON_ASSERT_FAILURE -# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_failure -# elif defined LEMON_ASSERT_ABORT -# define LEMON_ASSERT_HANDLER ::lemon::assert_fail -# else -# define LEMON_DISABLE_ASSERTS + +#if defined LEMON_ASSERT_LOG +# undef LEMON_ASSERT_HANDLER +# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_log +#elif defined LEMON_ASSERT_ABORT +# undef LEMON_ASSERT_HANDLER +# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_abort +#elif defined LEMON_ASSERT_ERROR +# undef LEMON_ASSERT_HANDLER +# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_error +#elif defined LEMON_ASSERT_EXCEPTION +# undef LEMON_ASSERT_HANDLER +# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_exception +#elif defined LEMON_ASSERT_CUSTOM +# undef LEMON_ASSERT_HANDLER +# ifndef LEMON_CUSTOM_ASSERT_HANDLER +# error "LEMON_CUSTOM_ASSERT_HANDLER is not set" # endif +# define LEMON_ASSERT_HANDLER LEMON_CUSTOM_ASSERT_HANDLER +#elif defined LEMON_ENABLE_ASSERTS +# undef LEMON_ASSERT_HANDLER +# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_abort +#else +# undef LEMON_ASSERT_HANDLER +#endif + + +#ifndef LEMON_FUNCTION_NAME +# define LEMON_FUNCTION_NAME (__PRETTY_FUNCTION__) #endif #ifdef DOXYGEN +/// \ingroup exceptions +/// /// \brief Macro for assertions with customizable message /// -/// Macro for assertions with customizable message. +/// Macro for assertions with customizable message. +/// \param exp An expression convertible to bool. If the expression is +/// false, then an assertion is raised. The concrete behaviour depends +/// on the settings of the assertion system. +/// \param msg A \e const \e char*, a \e const std::string& or a \e +/// const \e std::exception& parameter. The variable can be used to +/// provide information about the circumstances of failed assertion. /// /// The assertions are disabled in the default behaviour. You can -/// enable the assertions with the +/// enable the assertions with the following code: /// \code /// #define LEMON_ENABLE_ASSERTS /// \endcode -/// Then an assert -/// provides a log on the standard error about the assertion and aborts -/// the program if LEMON_ASSERT_DO_ABORT is also defined (otherwise the -/// program keeps on running). -/// By defining LEMON_ASSERT_FAILURE or -/// LEMON_ASSERT_EXCEPTION, you can set other behaviour to the -/// assertions. In case LEMON_ASSERT_FAILURE is given, LEMON_ASSERT -/// will always throw an \c AssertionFailedError exception with -/// the \c msg error message. By using -/// LEMON_ASSERT_EXCEPTION, one can define an arbitrary exception to be thrown. +/// or with compilation parameters: +/// \code +/// g++ -DLEMON_ENABLE_ASSERTS +/// make CXXFLAGS='-DLEMON_ENABLE_ASSERTS' +/// \endcode +/// +/// The %lemon assertion system has a wide range of customization +/// properties. As default behaviour the failed assertion prints a +/// short log message to the standard ouput and aborts the execution. /// -/// The LEMON_ASSERT macro should be called with the \c exp parameter -/// which should be an expression convertible to bool. If the given -/// parameter is false the assertion is raised and one of the assertion -/// behaviour will be activated. The \c msg should be either a const -/// char* message or an exception. When the \c msg is an exception the -/// \ref lemon::Exception::what() "what()" function is called to retrieve and -/// display the error message. +/// The following modes can be used in the assertion system: /// -/// \todo We should provide some way to reset to the default behaviour, -/// shouldn't we? +/// - \e LEMON_ASSERT_LOG The failed assert print a short convenient +/// error message to the standard error and continues the +/// execution. +/// - \e LEMON_ASSERT_ABORT This mode is similar to the \e +/// LEMON_ASSERT_LOG, but it aborts the program. It is the default +/// operation mode when the asserts are enabled with \e +/// LEMON_ENABLE_ASSERTS. +/// - \e LEMON_ASSERT_ERROR The assert throws an \ref +/// lemon::AssertionFailedError "AssertionFailedError". If the \c +/// msg parameter is an exception, then the result of the \ref +/// lemon::Exception::what() "what()" member function is passed as +/// error message. +/// - \e LEMON_ASSERT_EXCEPTION If the specified \c msg is an +/// exception then it raised directly (solving that the exception +/// can not be thrown polymorphically), otherwise an \ref +/// lemon::AssertionFailedError "AssertionFailedError" is thrown with +/// the given parameter. +/// - \e LEMON_ASSERT_CUSTOM The user can define an own assertion +/// handler functions. Three overloaded functions should be defined +/// with the following parameter lists: +/// \code +/// void custom_assert_handler(const char* file, int line, +/// const char* function, const char* message, const char* expression); +/// void custom_assert_handler(const char* file, int line, +/// const char* function, const std::string& message, const char* expression); +/// void custom_assert_handler(const char* file, int line, +/// const char* function, const std::exception& message, const char* expression); +/// \endcode +/// The name of the functions should be defined as the \c +/// LEMON_CUSTOM_ASSERT_HANDLER macro name. +/// \code +/// #define LEMON_CUSTOM_ASSERT_HANDLER custom_assert_handler +/// \endcode +/// Whenever an assertion is occured, one of the custom assertion +/// handler is called with appropiate parameters. /// -/// \todo This whole 'assert' business should be placed in a separate -/// include file. The boost assert is not guarded by header sentries -/// which may help to change the behaviour of the assertions in -/// the files. +/// The assertion mode can be changed within one compilation unit, if +/// the macros are redefined with other settings and the +/// lemon/assert.h file is reincluded then the behaviour is changed +/// appropiately to the new settings. +# define LEMON_ASSERT(exp, msg) \ + (static_cast (!!(exp) ? 0 : ( \ + LEMON_ASSERT_HANDLER(__FILE__, __LINE__, \ + LEMON_FUNCTION_NAME, \ + msg, #exp), 0))) + + +/// \ingroup exceptions /// -/// \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_DO_ABORT), 0))) +/// \brief Macro for mark not yet implemented features. +/// +/// Macro for mark not yet implemented features and outstanding bugs. +/// It is close to be the shortcut of the following code: +/// \code +/// LEMON_ASSERT(false, msg); +/// \endcode +# define LEMON_FIXME(msg) \ + (LEMON_ASSERT_HANDLER(__FILE__, __LINE__, LEMON_FUNCTION_NAME, \ + "FIXME: " msg, static_cast(0))) #else -# if defined LEMON_DISABLE_ASSERTS +# ifndef LEMON_ASSERT_HANDLER # define LEMON_ASSERT(exp, msg) (static_cast (0)) - +# define LEMON_FIXME(msg) (static_cast(0)) # else # define LEMON_ASSERT(exp, msg) \ (static_cast (!!(exp) ? 0 : ( \ LEMON_ASSERT_HANDLER(__FILE__, __LINE__, \ - __PRETTY_FUNCTION__, \ - msg, #exp, LEMON_ASSERT_DO_ABORT), 0))) + LEMON_FUNCTION_NAME, \ + msg, #exp), 0))) +# define LEMON_FIXME(msg) \ + (LEMON_ASSERT_HANDLER(__FILE__, __LINE__, LEMON_FUNCTION_NAME, \ + "FIXME: " msg, static_cast(0))) # endif + #endif -/** - * \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)) diff --git a/lemon/error.h b/lemon/error.h --- a/lemon/error.h +++ b/lemon/error.h @@ -93,7 +93,7 @@ std::auto_ptr<_Type> ptr; }; - /// Exception-safe convenient "error message" class. + /// Exception-safe convenient error message builder class. /// Helper class which provides a convenient ostream-like (operator << /// based) interface to create a string message. Mostly useful in @@ -413,263 +413,8 @@ 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 "lemon::AssertionFailedError"; - } - virtual ~AssertionFailedError() throw() {} - }; - - - /**************** Macros ****************/ - - - template - inline void assert_fail(const char *file, int line, - const char *func, - Exception exception, - const char *assertion = 0, - bool do_abort=true) - { - using namespace std; - cerr << file << ":" << line << ": "; - if (func) - cerr << func << ": "; - cerr << exception.what(); - if (assertion) - cerr << " (assertion '" << assertion << "' failed)"; - cerr << endl; - if (do_abort) - abort(); - } - - template <> - inline void assert_fail(const char *file, int line, - const char *func, - const char *message, - const char *assertion, - bool do_abort) - { - using namespace std; - cerr << file << ":" << line << ": "; - if (func) - cerr << func << ": "; - cerr << message; - if (assertion) - cerr << " (assertion '" << assertion << "' failed)"; - cerr << endl; - if (do_abort) - abort(); - } - - template <> - inline void assert_fail(const char *file, int line, - const char *func, - std::string message, - const char *assertion, - bool do_abort) - { - assert_fail(file, line, func, message.c_str(), assertion, do_abort); - } - - template - inline void assert_fail_failure(const char *file, int line, const char *func, - Exception exception, - const char *assertion = 0, - bool = true) - { - throw AssertionFailedError(file, line, func, exception.what(), assertion); - } - - template <> - inline void assert_fail_failure(const char *file, int line, - const char *func, - const char *message, - const char *assertion, - bool) - { - throw AssertionFailedError(file, line, func, message, assertion); - } - - template <> - inline void assert_fail_failure(const char *file, int line, - const char *func, - std::string message, - const char *assertion, - bool) - { - assert_fail_failure(file, line, func, message.c_str(), assertion, true); - } - - template - inline void assert_fail_exception(const char *file, int line, const char *func, - Exception exception, - const char *assertion = 0, bool = true) - { - throw exception; - } - - template <> - inline void assert_fail_exception(const char *file, int line, - const char *func, - const char *message, - const char *assertion, - bool) - { - throw AssertionFailedError(file, line, func, message, assertion); - } - - template <> - inline void assert_fail_exception(const char *file, int line, - const char *func, - std::string message, - const char *assertion, - bool) - { - assert_fail_exception(file, line, func, message.c_str(), assertion, true); - } - -/// @} + /// @} } + #endif // LEMON_ERROR_H - -#undef LEMON_ASSERT -#undef LEMON_FIXME - -#ifdef LEMON_ENABLE_ASSERTS -# define LEMON_ASSERT_ABORT -#endif - -#ifndef LEMON_ASSERT_DO_ABORT -# define LEMON_ASSERT_DO_ABORT 1 -#endif - -#ifndef LEMON_ASSERT_HANDLER -# if defined LEMON_ASSERT_EXCEPTION -# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_exception -# elif defined LEMON_ASSERT_FAILURE -# define LEMON_ASSERT_HANDLER ::lemon::assert_fail_failure -# elif defined LEMON_ASSERT_ABORT -# define LEMON_ASSERT_HANDLER ::lemon::assert_fail -# else -# define LEMON_DISABLE_ASSERTS -# endif -#endif - -#ifdef DOXYGEN - -/// \brief Macro for assertions with customizable message -/// -/// Macro for assertions with customizable message. -/// -/// The assertions are disabled in the default behaviour. You can -/// enable the assertions with the -/// \code -/// #define LEMON_ENABLE_ASSERTS -/// \endcode -/// Then an assert -/// provides a log on the standard error about the assertion and aborts -/// the program if LEMON_ASSERT_DO_ABORT is also defined (otherwise the -/// program keeps on running). -/// By defining LEMON_ASSERT_FAILURE or -/// LEMON_ASSERT_EXCEPTION, you can set other behaviour to the -/// assertions. In case LEMON_ASSERT_FAILURE is given, LEMON_ASSERT -/// will always throw an \c AssertionFailedError exception with -/// the \c msg error message. By using -/// LEMON_ASSERT_EXCEPTION, one can define an arbitrary exception to be thrown. -/// -/// The LEMON_ASSERT macro should be called with the \c exp parameter -/// which should be an expression convertible to bool. If the given -/// parameter is false the assertion is raised and one of the assertion -/// behaviour will be activated. The \c msg should be either a const -/// char* message or an exception. When the \c msg is an exception the -/// \ref lemon::Exception::what() "what()" function is called to retrieve and -/// display the error message. -/// -/// \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. The boost assert is not guarded by header sentries -/// which may help to change the behaviour of the assertions in -/// the files. -/// -/// \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_DO_ABORT), 0))) - -#else -# if defined LEMON_DISABLE_ASSERTS - -# define LEMON_ASSERT(exp, msg) (static_cast (0)) - -# else -# define LEMON_ASSERT(exp, msg) \ - (static_cast (!!(exp) ? 0 : ( \ - LEMON_ASSERT_HANDLER(__FILE__, __LINE__, \ - __PRETTY_FUNCTION__, \ - msg, #exp, LEMON_ASSERT_DO_ABORT), 0))) -# endif -#endif - -/** - * \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)) diff --git a/test/Makefile.am b/test/Makefile.am --- a/test/Makefile.am +++ b/test/Makefile.am @@ -12,6 +12,7 @@ test/dfs_test \ test/digraph_test \ test/dim_test \ + test/error_test \ test/graph_test \ test/kruskal_test \ test/maps_test \ @@ -28,7 +29,7 @@ test_dfs_test_SOURCES = test/dfs_test.cc test_digraph_test_SOURCES = test/digraph_test.cc test_dim_test_SOURCES = test/dim_test.cc -#test_error_test_SOURCES = test/error_test.cc +test_error_test_SOURCES = test/error_test.cc test_graph_test_SOURCES = test/graph_test.cc # test_heap_test_SOURCES = test/heap_test.cc test_kruskal_test_SOURCES = test/kruskal_test.cc diff --git a/test/error_test.cc b/test/error_test.cc --- a/test/error_test.cc +++ b/test/error_test.cc @@ -22,47 +22,242 @@ #include "test_tools.h" using namespace lemon; -using std::cout; -using std::endl; -void faulty_fn() { - fault("This is a fault message"); +#ifdef LEMON_ENABLE_ASSERTS +#undef LEMON_ENABLE_ASSERTS +#endif + +#ifdef LEMON_DISABLE_ASSERTS +#undef LEMON_DISABLE_ASSERTS +#endif + +//checking disabled asserts +#define LEMON_DISABLE_ASSERTS +#include + +void no_assertion_text_disable() { + LEMON_ASSERT(true, "This is a fault message"); } -void exception_fn() { - throw Exception("This is a function throwing exception with some args: ") - << 5 << ", " << 18; +void no_assertion_exception_disable() { + LEMON_ASSERT(true, Exception()); } -void unfinished_fn() { - LEMON_FIXME("unfinished_fn() is unfinished!"); +void assertion_text_disable() { + LEMON_ASSERT(false, "This is a fault message"); } +void assertion_exception_disable() { + LEMON_ASSERT(false, Exception()); +} -int main() { +void fixme_disable() { + LEMON_FIXME("fixme_disable() is fixme!"); +} + +void check_assertion_disable() { + no_assertion_text_disable(); + no_assertion_exception_disable(); + assertion_exception_disable(); + assertion_text_disable(); + fixme_disable(); +} +#undef LEMON_DISABLE_ASSERTS + + +#define LEMON_ASSERT_ERROR +#include + +void no_assertion_text_error() { + LEMON_ASSERT(true, "This is a fault message"); +} + +void no_assertion_exception_error() { + LEMON_ASSERT(true, Exception()); +} + +void assertion_text_error() { + LEMON_ASSERT(false, "This is a fault message"); +} + +void assertion_exception_error() { + LEMON_ASSERT(false, Exception()); +} + +void fixme_error() { + LEMON_FIXME("fixme_error() is fixme!"); +} + +void check_assertion_error() { + no_assertion_text_error(); + no_assertion_exception_error(); try { - faulty_fn(); - check(false, "A faulty function did not fail."); - } - catch(const Exception &e) { - cout << "Exeption = \"" << e.what() << "\" (Right behaviour)" << endl; + assertion_exception_error(); + check(false, "Assertion error"); + } catch (const AssertionFailedError& e) { } try { - exception_fn(); - check(false, "The function did not throw Exception."); - } - catch(const Exception &e) { - cout << "Exeption = \"" << e.what() << "\" (Right behaviour)" << endl; + assertion_text_error(); + check(false, "Assertion error"); + } catch (const AssertionFailedError& e) { } try { - unfinished_fn(); - check(false, "FIXME macro does not work."); + fixme_error(); + check(false, "Assertion error"); + } catch (const AssertionFailedError& e) { } - catch(const Exception &e) { - cout << "Exeption = \"" << e.what() << "\" (Right behaviour)" << endl; +} +#undef LEMON_ASSERT_ERROR + +#define LEMON_ASSERT_EXCEPTION +#include + +void no_assertion_text_exception() { + LEMON_ASSERT(true, "This is a fault message"); +} + +void no_assertion_exception_exception() { + LEMON_ASSERT(true, Exception()); +} + +void assertion_text_exception() { + LEMON_ASSERT(false, "This is a fault message"); +} + +void assertion_exception_exception() { + LEMON_ASSERT(false, Exception()); +} + +void fixme_exception() { + LEMON_FIXME("fixme_exception() is fixme!"); +} + +void check_assertion_exception() { + no_assertion_text_exception(); + no_assertion_exception_exception(); + try { + assertion_exception_exception(); + check(false, "Assertion error"); + } catch (const Exception& e) { } + try { + assertion_text_exception(); + check(false, "Assertion error"); + } catch (const AssertionFailedError& e) { + } + + try { + assertion_text_exception(); + check(false, "Assertion error"); + } catch (const AssertionFailedError& e) { + } + + try { + fixme_exception(); + check(false, "Assertion error"); + } catch (const AssertionFailedError& e) { + } +} +#undef LEMON_ASSERT_EXCEPTION + +#define LEMON_ASSERT_LOG + +#include + +void no_assertion_text_log() { + LEMON_ASSERT(true, "This is a fault message"); +} + +void no_assertion_exception_log() { + LEMON_ASSERT(true, Exception()); +} + +void assertion_text_log() { + LEMON_ASSERT(false, "This is a fault message"); +} + +void assertion_exception_log() { + LEMON_ASSERT(false, Exception()); +} + +void fixme_log() { + LEMON_FIXME("fixme_log() is fixme!"); +} + +void check_assertion_log() { + no_assertion_text_log(); + no_assertion_exception_log(); + std::cerr << "The next 3 failure messages are expected: " << std::endl; + assertion_exception_log(); + assertion_text_log(); + fixme_log(); + std::cerr << "End of expected error messages" << std::endl; +} +#undef LEMON_ASSERT_LOG + +#define LEMON_ASSERT_CUSTOM + +static int cnt = 0; +void my_assert_handler(const char*, int, const char*, + const char*, const char*) { + ++cnt; +} + +void my_assert_handler(const char*, int, const char*, + const std::exception&, const char*) { + ++cnt; +} + +void my_assert_handler(const char*, int, const char*, + const std::string&, const char*) { + ++cnt; +} + + +#define LEMON_CUSTOM_ASSERT_HANDLER my_assert_handler +#include + +void no_assertion_text_custom() { + LEMON_ASSERT(true, "This is a fault message"); +} + +void no_assertion_exception_custom() { + LEMON_ASSERT(true, Exception()); +} + +void assertion_text_custom() { + LEMON_ASSERT(false, "This is a fault message"); +} + +void assertion_exception_custom() { + LEMON_ASSERT(false, Exception()); +} + +void fixme_custom() { + LEMON_FIXME("fixme_custom() is fixme!"); +} + +void check_assertion_custom() { + no_assertion_text_custom(); + no_assertion_exception_custom(); + assertion_exception_custom(); + assertion_text_custom(); + fixme_custom(); + check(cnt == 3, "The custom assert handler does not work"); +} + +#undef LEMON_ASSERT_CUSTOM + + +int main() { + check_assertion_disable(); + check_assertion_error(); + check_assertion_exception(); + check_assertion_log(); + check_assertion_custom(); + return 0; }