src/lemon/error.h
author marci
Thu, 24 Feb 2005 17:42:11 +0000
changeset 1176 1ba2b4c0c970
parent 1157 3996d2098090
child 1207 8117169c9049
permissions -rw-r--r--
glpk is able to search 5x5 magic square, let's celebrate the free software
alpar@906
     1
/* -*- C++ -*-
klao@1157
     2
 *
alpar@921
     3
 * src/lemon/error.h - Part of LEMON, a generic C++ optimization library
alpar@906
     4
 *
alpar@1164
     5
 * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi
klao@1157
     6
 * Kutatocsoport (Egervary Combinatorial Optimization Research Group,
klao@1157
     7
 * EGRES).
alpar@906
     8
 *
alpar@906
     9
 * Permission to use, modify and distribute this software is granted
alpar@906
    10
 * provided that this copyright notice appears in all copies. For
alpar@906
    11
 * precise terms see the accompanying LICENSE file.
alpar@906
    12
 *
alpar@906
    13
 * This software is provided "AS IS" with no warranty of any kind,
alpar@906
    14
 * express or implied, and with no claim as to its suitability for any
alpar@906
    15
 * purpose.
alpar@906
    16
 *
alpar@906
    17
 */
alpar@883
    18
alpar@921
    19
#ifndef LEMON_ERROR_H
alpar@921
    20
#define LEMON_ERROR_H
alpar@883
    21
alpar@1151
    22
//! \ingroup exceptions
alpar@883
    23
//! \file
klao@1067
    24
//! \brief Basic exception classes and error handling.
alpar@883
    25
alpar@883
    26
#include <exception>
alpar@883
    27
#include <string>
alpar@883
    28
#include <sstream>
klao@1067
    29
#include <iostream>
klao@1067
    30
#include <cstdlib>
klao@1157
    31
#include <memory>
alpar@883
    32
alpar@921
    33
namespace lemon {
alpar@883
    34
alpar@1151
    35
/// \addtogroup exceptions
alpar@1151
    36
/// @{
alpar@1151
    37
klao@1056
    38
  /// Exception-safe convenient "error message" class.
klao@1067
    39
klao@1067
    40
  /// Helper class which provides a convenient ostream-like (operator <<
klao@1067
    41
  /// based) interface to create a string message. Mostly useful in
klao@1067
    42
  /// exception classes (therefore the name).
klao@1056
    43
  class ErrorMessage {
klao@1056
    44
  protected:
klao@1067
    45
    ///\e 
klao@1157
    46
    ///\todo The good solution is boost:shared_ptr...
klao@1157
    47
    mutable
klao@1157
    48
    std::auto_ptr<std::ostringstream> buf;
klao@1056
    49
    
klao@1067
    50
    ///\e 
klao@1056
    51
    bool init() throw() {
klao@1056
    52
      try {
klao@1056
    53
	buf.reset(new std::ostringstream);
klao@1056
    54
      }
klao@1056
    55
      catch(...) {
klao@1056
    56
	buf.reset();
klao@1056
    57
      }
klao@1157
    58
      return buf.get();
klao@1056
    59
    }
klao@1056
    60
klao@1056
    61
  public:
klao@1056
    62
klao@1067
    63
    ///\e 
klao@1056
    64
    ErrorMessage() throw() { init(); }
klao@1056
    65
klao@1157
    66
    ErrorMessage(const ErrorMessage& em) throw() : buf(em.buf) { }
klao@1157
    67
klao@1067
    68
    ///\e 
klao@1056
    69
    ErrorMessage(const char *message) throw() {
klao@1056
    70
      init();
klao@1056
    71
      *this << message;
klao@1056
    72
    }
klao@1056
    73
klao@1067
    74
    ///\e 
klao@1056
    75
    ErrorMessage(const std::string &message) throw() {
klao@1056
    76
      init();
klao@1056
    77
      *this << message;
klao@1056
    78
    }
klao@1056
    79
klao@1067
    80
    ///\e 
klao@1056
    81
    template <typename T>
klao@1056
    82
    ErrorMessage& operator<<(const T &t) throw() {
klao@1157
    83
      if( ! buf.get() ) return *this;
klao@1056
    84
klao@1056
    85
      try {
klao@1056
    86
	*buf << t;
klao@1056
    87
      }
klao@1056
    88
      catch(...) {
klao@1056
    89
	buf.reset();
klao@1056
    90
      }
klao@1056
    91
    }
klao@1056
    92
klao@1067
    93
    ///\e 
klao@1056
    94
    const char* message() throw() {
klao@1157
    95
      if( ! buf.get() ) return 0;
klao@1056
    96
klao@1056
    97
      const char* mes = 0;
klao@1056
    98
      try {
klao@1056
    99
	mes = buf->str().c_str();
klao@1056
   100
      }
klao@1056
   101
      catch(...) {}
klao@1056
   102
      return mes;
klao@1056
   103
    }
klao@1056
   104
    
klao@1056
   105
  };
klao@1056
   106
alpar@883
   107
  /**
alpar@883
   108
   * \brief Generic exception class.
alpar@883
   109
   *
klao@1056
   110
   * Base class for exceptions used in LEMON.
alpar@883
   111
   */
klao@1067
   112
  class Exception : public std::exception {
alpar@883
   113
  public:
klao@1067
   114
    ///\e 
klao@1120
   115
    Exception() {}
klao@1067
   116
    ///\e 
alpar@883
   117
    virtual ~Exception() throw() {}
klao@1120
   118
klao@1120
   119
    ///\e
klao@1120
   120
    virtual const char* exceptionName() const {
klao@1120
   121
      return "lemon::Exception";
klao@1120
   122
    }
alpar@883
   123
    
klao@1067
   124
    ///\e 
alpar@883
   125
    virtual const char* what() const throw() {
klao@1120
   126
      return exceptionName();
klao@1056
   127
    }
klao@1056
   128
  };
klao@1056
   129
klao@1120
   130
  /**
klao@1120
   131
   * \brief One of the two main subclasses of \ref Exception.
klao@1120
   132
   *
klao@1120
   133
   * Logic errors represent problems in the internal logic of a program;
klao@1120
   134
   * in theory, these are preventable, and even detectable before the
klao@1120
   135
   * program runs (e.g., violations of class invariants).
klao@1120
   136
   *
alpar@1125
   137
   * A typical example for this is \ref UninitializedParameter.
klao@1120
   138
   */
klao@1056
   139
  class LogicError : public Exception {
klao@1067
   140
  public:
klao@1120
   141
    virtual const char* exceptionName() const {
klao@1120
   142
      return "lemon::LogicError";
klao@1120
   143
    }
klao@1056
   144
  };
klao@1056
   145
alpar@1125
   146
  /**
alpar@1125
   147
   * \brief \ref Exception for uninitialized parameters.
alpar@1125
   148
   *
alpar@1125
   149
   * This error represents problems in the initialization
alpar@1125
   150
   * of the parameters of the algorithms.
alpar@1125
   151
   */
alpar@1125
   152
  class UninitializedParameter : public LogicError {
alpar@1125
   153
  public:
alpar@1125
   154
    virtual const char* exceptionName() const {
alpar@1125
   155
      return "lemon::UninitializedParameter";
alpar@1125
   156
    }
alpar@1125
   157
  };
alpar@1125
   158
klao@1120
   159
  
klao@1120
   160
  /**
klao@1120
   161
   * \brief One of the two main subclasses of \ref Exception.
klao@1120
   162
   *
klao@1120
   163
   * Runtime errors represent problems outside the scope of a program;
klao@1120
   164
   * they cannot be easily predicted and can generally only be caught as
klao@1120
   165
   * the program executes.
klao@1120
   166
   */
klao@1056
   167
  class RuntimeError : public Exception {
klao@1067
   168
  public:
klao@1120
   169
    virtual const char* exceptionName() const {
klao@1120
   170
      return "lemon::RuntimeError";
klao@1120
   171
    }
klao@1056
   172
  };
klao@1056
   173
klao@1067
   174
  ///\e
klao@1056
   175
  class RangeError : public RuntimeError {
klao@1067
   176
  public:
klao@1120
   177
    virtual const char* exceptionName() const {
klao@1120
   178
      return "lemon::RangeError";
klao@1120
   179
    }
klao@1056
   180
  };
klao@1056
   181
alpar@1061
   182
  ///\e 
klao@1056
   183
  class IOError : public RuntimeError {
klao@1067
   184
  public:
klao@1120
   185
    virtual const char* exceptionName() const {
klao@1120
   186
      return "lemon::IOError";
klao@1120
   187
    }
klao@1056
   188
  };
klao@1056
   189
alpar@1061
   190
  ///\e 
klao@1056
   191
  class DataFormatError : public IOError {
klao@1067
   192
  protected:
klao@1120
   193
    const char *_message;
klao@1120
   194
    int _line;
klao@1157
   195
klao@1157
   196
    ///\todo Much better solution is boost::shared_ptr
klao@1157
   197
    mutable
klao@1157
   198
    std::auto_ptr<std::string> _file;
klao@1067
   199
klao@1067
   200
  public:
klao@1157
   201
klao@1157
   202
    DataFormatError(const DataFormatError &dfe) : 
klao@1157
   203
      IOError(dfe), _message(dfe._message), _line(dfe._line),
klao@1157
   204
      _file(dfe._file) {}
klao@1157
   205
klao@1067
   206
    ///\e 
klao@1120
   207
    explicit DataFormatError(const char *the_message)
klao@1120
   208
      : _message(the_message), _line(0) {}
klao@1067
   209
    ///\e 
klao@1056
   210
    DataFormatError(const std::string &file_name, int line_num,
klao@1120
   211
		    const char *the_message)
klao@1120
   212
      : _message(the_message), _line(line_num) { file(file_name); }
klao@1056
   213
klao@1067
   214
    ///\e 
klao@1120
   215
    void line(int line_num) { _line=line_num; }
klao@1067
   216
    ///\e 
klao@1120
   217
    void message(char *the_message) { _message=the_message; }
klao@1120
   218
    ///\e 
klao@1120
   219
    void file(const std::string &file_name) {
klao@1056
   220
      try {
klao@1120
   221
	_file.reset(new std::string);
klao@1120
   222
	*_file = file_name;
klao@1056
   223
      }
klao@1056
   224
      catch(...) {
klao@1120
   225
	_file.reset();
klao@1056
   226
      }
alpar@883
   227
    }
alpar@883
   228
klao@1067
   229
    ///\e
klao@1120
   230
    int line() const { return _line; }
klao@1120
   231
    ///\e
klao@1120
   232
    const char* message() const { return _message; }
klao@1067
   233
klao@1067
   234
    /// \brief Returns the filename.
klao@1067
   235
    ///
klao@1068
   236
    /// Returns \e "(unknown)" if the filename was not specified.
klao@1120
   237
    const char* file() const {
klao@1157
   238
      if( _file.get() )
klao@1120
   239
	return _file->c_str();
klao@1067
   240
      else
klao@1067
   241
	return "(unknown)";
klao@1067
   242
    }
klao@1067
   243
klao@1067
   244
    ///\e 
klao@1067
   245
    virtual const char* what() const throw() {
klao@1056
   246
      const char *mes = 0;
klao@1056
   247
      try {
klao@1056
   248
	std::ostringstream ostr;
klao@1120
   249
	ostr << _message;
klao@1157
   250
	if( _file.get() || _line ) {
klao@1056
   251
	  ostr << " (";
klao@1157
   252
	  if( _file.get() ) ostr << "in file '" << *_file << "'";
klao@1157
   253
	  if( _file.get() && _line ) ostr << " ";
klao@1120
   254
	  if( _line ) ostr << "at line " << _line;
klao@1067
   255
	  ostr << ")";
klao@1056
   256
	}
klao@1056
   257
	mes = ostr.str().c_str();
klao@1056
   258
      }
klao@1056
   259
      catch(...) {}
klao@1056
   260
      if( mes ) return mes;
klao@1120
   261
      return exceptionName();
klao@1120
   262
    }
klao@1120
   263
klao@1120
   264
    virtual const char* exceptionName() const {
klao@1056
   265
      return "lemon::DataFormatError";
klao@1056
   266
    }
klao@1067
   267
klao@1067
   268
    virtual ~DataFormatError() throw() {}
alpar@883
   269
  };
alpar@883
   270
klao@1056
   271
klao@1068
   272
  ///\e
klao@1067
   273
  class AssertionFailedError : public LogicError {
klao@1067
   274
  protected:
klao@1067
   275
    const char *assertion;
klao@1067
   276
    const char *file;
klao@1067
   277
    int line;
klao@1067
   278
    const char *function;
klao@1067
   279
    const char *message;
klao@1067
   280
  public:
klao@1067
   281
    ///\e
klao@1067
   282
    AssertionFailedError(const char *_file, int _line, const char *func,
klao@1067
   283
			 const char *msg, const char *_assertion = 0) :
klao@1067
   284
      assertion(_assertion), file(_file), line(_line), function(func),
klao@1067
   285
      message(msg) {}
klao@1067
   286
klao@1067
   287
    ///\e
klao@1067
   288
    const char* get_assertion() const { return assertion; }
klao@1067
   289
    ///\e
klao@1067
   290
    const char* get_message() const { return message; }
klao@1067
   291
    ///\e
klao@1067
   292
    const char* get_file() const { return file; }
klao@1067
   293
    ///\e
klao@1067
   294
    const char* get_function() const { return function; }
klao@1067
   295
    ///\e
klao@1067
   296
    int get_line() const { return line; }
klao@1067
   297
klao@1067
   298
klao@1067
   299
    virtual const char* what() const throw() {
klao@1067
   300
      const char *mes = 0;
klao@1067
   301
      try {
klao@1067
   302
	std::ostringstream ostr;
klao@1067
   303
	ostr << file << ":" << line << ": ";
klao@1067
   304
	if( function )
klao@1067
   305
	  ostr << function << ": ";
klao@1067
   306
	ostr << message;
klao@1067
   307
	if( assertion )
klao@1067
   308
	  ostr << " (assertion '" << assertion << "' failed)";
klao@1067
   309
	mes = ostr.str().c_str();
klao@1067
   310
      }
klao@1067
   311
      catch(...) {}
klao@1067
   312
      if( mes ) return mes;
klao@1120
   313
      return exceptionName();
klao@1120
   314
    }
klao@1120
   315
klao@1120
   316
    virtual const char* exceptionName() const {
klao@1067
   317
      return "lemon::AssertionFailedError";
klao@1067
   318
    }
klao@1067
   319
klao@1067
   320
    virtual ~AssertionFailedError() throw() {}
klao@1067
   321
  };
klao@1067
   322
klao@1056
   323
klao@1056
   324
  /****************  Macros  ****************/
klao@1056
   325
klao@1056
   326
klao@1067
   327
  inline
klao@1067
   328
  void assert_fail(const char *file, int line, const char *func,
klao@1067
   329
		   const char *message, const char *assertion = 0,
klao@1067
   330
		   bool do_abort=true)
klao@1067
   331
  {
klao@1067
   332
    using namespace std;
klao@1067
   333
    cerr << file << ":" << line << ": ";
klao@1067
   334
    if( func )
klao@1067
   335
      cerr << func << ": ";
klao@1067
   336
    cerr << message;
klao@1067
   337
    if( assertion )
klao@1067
   338
      cerr << " (assertion '" << assertion << "' failed)";
klao@1067
   339
    cerr << endl;
klao@1067
   340
    if(do_abort)
klao@1067
   341
      abort();
klao@1067
   342
  }
klao@1056
   343
klao@1067
   344
  inline
klao@1067
   345
  void assert_fail_throw(const char *file, int line, const char *func,
klao@1067
   346
		   const char *message, const char *assertion = 0,
klao@1067
   347
		   bool = true)
klao@1067
   348
  {
klao@1067
   349
    throw AssertionFailedError(file, line, func, message, assertion);
klao@1067
   350
  }
klao@1056
   351
alpar@1151
   352
/// @}
alpar@883
   353
alpar@883
   354
}
alpar@921
   355
#endif // LEMON_ERROR_H
klao@1067
   356
klao@1067
   357
#undef LEMON_ASSERT
klao@1067
   358
#undef LEMON_FIXME
klao@1067
   359
klao@1067
   360
#ifndef LEMON_ASSERT_ABORT
klao@1067
   361
#  define LEMON_ASSERT_ABORT 1
klao@1067
   362
#endif
klao@1067
   363
klao@1067
   364
#ifndef LEMON_ASSERT_HANDLER
klao@1120
   365
#  ifdef LEMON_ASSERT_EXCEPTION
klao@1120
   366
#    define LEMON_ASSERT_HANDLER ::lemon::assert_fail_throw
klao@1120
   367
#  else
klao@1120
   368
#    define LEMON_ASSERT_HANDLER ::lemon::assert_fail
klao@1120
   369
#  endif
klao@1067
   370
#endif
klao@1067
   371
klao@1067
   372
#if defined(NDEBUG) || defined(LEMON_DISABLE_ASSERTS)
klao@1067
   373
klao@1067
   374
#  define LEMON_ASSERT(exp, msg)  (static_cast<void> (0))
klao@1067
   375
klao@1067
   376
#else
klao@1067
   377
klao@1067
   378
/**
klao@1067
   379
 * \brief Macro for assertions with customizable message
klao@1067
   380
 *
klao@1120
   381
 * Macro for assertions with customizable message.
klao@1120
   382
 *
klao@1120
   383
 * The behaviour can be customized with LEMON_ASSERT_HANDLER,
klao@1120
   384
 * LEMON_ASSERT_EXCEPTION and LEMON_ASSERT_ABORT defines. Asserts can be
klao@1120
   385
 * disabled by defining either NDEBUG or LEMON_DISABLE_ASSERTS macros.
klao@1120
   386
 *
klao@1120
   387
 * \todo We should provide some way to reset to the default behaviour,
klao@1120
   388
 * shouldn't we?
klao@1120
   389
 *
klao@1120
   390
 * \todo This whole 'assert' business should be placed in a separate
klao@1120
   391
 * include file.
klao@1120
   392
 *
klao@1067
   393
 * \todo __PRETTY_FUNCTION__ should be replaced by something
klao@1067
   394
 * compiler-independant, like BOOST_CURRENT_FUNCTION
klao@1067
   395
 */
klao@1067
   396
klao@1067
   397
#  define LEMON_ASSERT(exp, msg)                 \
klao@1067
   398
     (static_cast<void> (!!(exp) ? 0 : (         \
klao@1067
   399
       LEMON_ASSERT_HANDLER(__FILE__, __LINE__,  \
klao@1067
   400
                            __PRETTY_FUNCTION__, \
klao@1067
   401
			    (msg), #exp, LEMON_ASSERT_ABORT), 0)))
klao@1067
   402
klao@1120
   403
#endif // NDEBUG || LEMON_DISABLE_ASSERTS
klao@1067
   404
klao@1067
   405
/**
klao@1067
   406
 * \brief Macro for mark not yet implemented features.
klao@1067
   407
 *
klao@1067
   408
 * \todo Is this the right place for this? It should be used only in
klao@1067
   409
 * modules under development.
klao@1067
   410
 *
klao@1067
   411
 * \todo __PRETTY_FUNCTION__ should be replaced by something
klao@1067
   412
 * compiler-independant, like BOOST_CURRENT_FUNCTION
klao@1067
   413
 */
klao@1067
   414
klao@1067
   415
# define LEMON_FIXME(msg) \
klao@1067
   416
    (LEMON_ASSERT_HANDLER(__FILE__, __LINE__, __PRETTY_FUNCTION__, \
klao@1067
   417
			  "FIXME: " msg))