1 /* 2 * Scythe Statistical Library Copyright (C) 2000-2002 Andrew D. Martin 3 * and Kevin M. Quinn; 2002-present Andrew D. Martin, Kevin M. Quinn, 4 * and Daniel Pemstein. All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify under the terms of the GNU General Public License as 8 * published by Free Software Foundation; either version 2 of the 9 * License, or (at your option) any later version. See the text files 10 * COPYING and LICENSE, distributed with this source code, for further 11 * information. 12 * -------------------------------------------------------------------- 13 * scythestat/error.h 14 */ 15 16 /*! \file error.h 17 * 18 * \brief Definitions of Scythe exception classes. 19 * 20 * This file contains the class definitions for 21 * scythe::scythe_exception and its children. These exception classes 22 * describe all of the error conditions generated by Scythe library 23 * routines. 24 * 25 * Furthermore, error.h contains a series of macro definitions that 26 * regulate the inclusion of the library's error checking code in 27 * compiled code. These macros are controlled by the compiler flag 28 * SCYTHE_DEBUG and define four levels of scythe debug 29 * info, SCYTHE_DEBUG = 0, 1, 2, or 3. The library uses these macros to 30 * specify the debug level of thrown exceptions. If we are at level 31 * three, all throws are expanded into actual code, at level 2 only 32 * SCYTHE_THROW_10 AND SCYTHE_THROW_20 calls are expanded, and so on. 33 * Scythe developers should balance exception importance and 34 * efficiency costs when making exception level choices. For example, 35 * bounds checking in matrices is done at level three primarily 36 * because the added branch results in high performance penalties and 37 * out-of-bounds errors shouldn't occur in well-written code, while 38 * conformance checks in matrix multiplication are level 1 because the 39 * checks result in little overhead relative to the cost of matrix 40 * multiplication and conformation errors are easy to introduce by 41 * accident. At level 0, the library performs virtually no error 42 * checking. 43 * 44 * While the various SCYTHE_THROW, SCYTHE_CHECK, and SCYTHE_WARN 45 * macros will only typically be used by library developers, users 46 * should make extensive use the tiered error reporting in Scythe by 47 * setting the compiler flag SCYTHE_DEBUG. If not explicitly set by 48 * the user, the SCYTHE_DEBUG level is automatically set to 3. 49 */ 50 51 #ifndef SCYTHE_ERROR_H 52 #define SCYTHE_ERROR_H 53 54 #include <exception> 55 #include <string> 56 #include <sstream> 57 #include <iostream> 58 #include <vector> 59 #include <cstring> 60 61 #ifdef SCYTHE_RPACK 62 #include <R.h> // needed to use Rprintf() 63 #include <R_ext/Utils.h> // needed to allow user interrupts 64 #endif 65 66 /*! @cond */ 67 #ifdef SCYTHE_DEBUG_LIB 68 #define SCYTHE_DEBUG_MSG(MSG) \ 69 { std::cout << "SCYTHE_DEBUG_LIB: " << MSG << std::endl; } 70 #else 71 #define SCYTHE_DEBUG_MSG(MSG) 72 #endif 73 /*! @endcond */ 74 75 #define SCYTHE_THROW(EXCEP,MSG) \ 76 { \ 77 std::stringstream _SCYTHE_DEBUG_ss; \ 78 _SCYTHE_DEBUG_ss << MSG; \ 79 throw EXCEP(__FILE__, __func__, __LINE__, \ 80 _SCYTHE_DEBUG_ss.str()); \ 81 } 82 83 #define SCYTHE_CHECK(CHECK,EXCEP,MSG) \ 84 { \ 85 if (CHECK) \ 86 SCYTHE_THROW(EXCEP,MSG) \ 87 } 88 89 #define SCYTHE_WARN_RPACK(MSG) \ 90 { \ 91 std::stringstream _SCYTHE_WARN_ss; \ 92 _SCYTHE_WARN_ss << "WARNING in " << __FILE__ << ", " \ 93 << __func__ << ", " << __LINE__ << ": " \ 94 << MSG << "\n"; \ 95 Rprintf(_SCYTHE_WARN_ss.str().c_str()); \ 96 } 97 98 #define SCYTHE_WARN_STD(MSG) \ 99 std::cerr << "WARNING in " << __FILE__ << ", " \ 100 << __func__ << ", " << __LINE__ << ": " \ 101 << MSG << "\n"; 102 103 #ifdef SCYTHE_RPACK 104 #define SCYTHE_WARN SCYTHE_WARN_RPACK 105 #else 106 #define SCYTHE_WARN SCYTHE_WARN_STD 107 #endif 108 109 #define SCYTHE_CHECK_WARN(CHECK,MSG) \ 110 { \ 111 if (CHECK) \ 112 SCYTHE_WARN(MSG) \ 113 } 114 115 /*! @cond */ 116 #ifndef SCYTHE_DEBUG 117 #define SCYTHE_DEBUG 3 118 #endif 119 /*! @endcond */ 120 121 #if SCYTHE_DEBUG > 0 122 #define SCYTHE_CHECK_10(CHECK,EXCEP,MSG) SCYTHE_CHECK(CHECK,EXCEP,MSG) 123 #else 124 #define SCYTHE_CHECK_10(CHECK, EXCEP, MSG) 125 #endif 126 127 #if SCYTHE_DEBUG > 1 128 #define SCYTHE_CHECK_20(CHECK,EXCEP,MSG) SCYTHE_CHECK(CHECK,EXCEP,MSG) 129 #else 130 #define SCYTHE_CHECK_20(CHECK, EXCEP, MSG) 131 #endif 132 133 #if SCYTHE_DEBUG > 2 134 #define SCYTHE_CHECK_30(CHECK,EXCEP,MSG) SCYTHE_CHECK(CHECK,EXCEP,MSG) 135 #else 136 #define SCYTHE_CHECK_30(CHECK, EXCEP, MSG) 137 #endif 138 139 #if SCYTHE_DEBUG > 0 140 #define SCYTHE_THROW_10(EXCEP,MSG) SCYTHE_THROW(EXCEP,MSG) 141 #else 142 #define SCYTHE_THROW_10(EXCEP,MSG) 143 #endif 144 145 #if SCYTHE_DEBUG > 1 146 #define SCYTHE_THROW_20(EXCEP,MSG) SCYTHE_THROW(EXCEP,MSG) 147 #else 148 #define SCYTHE_THROW_20(EXCEP,MSG) 149 #endif 150 151 #if SCYTHE_DEBUG > 2 152 #define SCYTHE_THROW_30(EXCEP,MSG) SCYTHE_THROW(EXCEP,MSG) 153 #else 154 #define SCYTHE_THROW_30(EXCEP,MSG) 155 #endif 156 157 namespace scythe 158 { 159 /* Forward declaration for serr */ 160 class scythe_exception; 161 162 /**** This file-local variable holds the output of the last 163 * scythe_exception constructed. 164 ****/ 165 #ifdef __MINGW32__ 166 static scythe_exception *serr; 167 #else 168 namespace 169 { 170 scythe_exception *serr; 171 } 172 #endif 173 174 /**** A replacement for the default terminate handler. This outputs 175 * the string held in serr before calling abort, thereby notifying 176 * the user of why the program crashed. 177 ****/ 178 inline void scythe_terminate (); 179 180 /**** The scythe exception abstract base class ****/ 181 /*! 182 * \brief The Scythe exception abstract base class. 183 * 184 * The is the base class in Scythe's error handling class tree. 185 * This class extends std::exception and provides fields for 186 * information about the exception, including where the exception 187 * occurred in the library and a message describing the error. 188 */ 189 class scythe_exception:public std::exception 190 { 191 public: 192 scythe_exception (const std::string & head, 193 const std::string & file, 194 const std::string & function, 195 const unsigned int &line, 196 const std::string & message = "", throw()197 const bool & halt = false) throw () 198 : exception (), 199 head_ (head), 200 file_ (file), 201 function_ (function), 202 line_ (line), 203 message_ (message), 204 call_files_ (), 205 call_funcs_ (), 206 call_lines_ () 207 { 208 std::ostringstream os; 209 os << head_ << " in " << file_ << ", " << function_ << ", " 210 << line_ << ": " << message_ << "!\n\n"; 211 212 serr = this; 213 std::set_terminate (scythe_terminate); 214 if (halt) { 215 #ifdef SCYTHE_RPACK 216 error("Aborting Scythe C++ execution"); 217 #else 218 std::terminate (); 219 #endif 220 } 221 } 222 throw()223 scythe_exception (const scythe_exception & e) throw () 224 : exception (), 225 head_ (e.head_), 226 file_ (e.file_), 227 function_ (e.function_), 228 line_ (e.line_), 229 message_ (e.message_), 230 call_files_ (e.call_files_), 231 call_funcs_ (e.call_funcs_), 232 call_lines_ (e.call_lines_) 233 { 234 } 235 throw()236 scythe_exception & operator= (const scythe_exception & e) throw () 237 { 238 head_ = e.head_; 239 file_ = e.file_; 240 function_ = e.function_; 241 line_ = e.line_; 242 message_ = e.message_; 243 244 return *this; 245 } 246 ~scythe_exception()247 virtual ~ scythe_exception () throw () 248 { 249 } 250 251 /* This function is only called from scythe_terminate, and only 252 * once, so this memory leak is not an issue. We can't just return 253 * os.str().c_str() because that is a dangling pointer after the 254 * function returns... 255 * TODO: Deal with memory leak issue that might affect R packages 256 */ what()257 virtual const char *what () const throw () 258 { 259 std::ostringstream os; 260 for (int i = call_files_.size() - 1; i > -1; ++i) { 261 os << "Called from " << call_files_[i] << ", " 262 << call_funcs_[i] << ", " << call_lines_[i] << std::endl; 263 } 264 os << head_ << " in " << file_ << ", " << function_ << ", " 265 << line_ << ": " << message_ << "!"; 266 char *retval = new char[os.str().length()]; 267 std::strcpy(retval, os.str().c_str()); 268 return retval; 269 } 270 message()271 virtual std::string message () const throw () 272 { 273 return message_; 274 } 275 add_caller(const std::string & file,const std::string & function,const unsigned int & line)276 virtual void add_caller (const std::string &file, 277 const std::string &function, const unsigned int &line) throw () 278 { 279 280 /* This if allows one to catch and rethrow an error in the same 281 * function w/out messing things up. Nice to keep try-catch 282 * blocks to a minimum 283 */ 284 285 if (file != file_ && function != function_) { 286 call_files_.push_back(file); 287 call_funcs_.push_back(function); 288 call_lines_.push_back(line); 289 } 290 } 291 292 private: 293 std::string head_; 294 std::string file_; 295 std::string function_; 296 unsigned int line_; 297 std::string message_; 298 std::vector<std::string> call_files_; 299 std::vector<std::string> call_funcs_; 300 std::vector<unsigned int> call_lines_; 301 }; 302 303 304 /**** Exception class types, added as needed ****/ 305 306 /*! 307 * \brief Memory allocation error. 308 * 309 * Library members throw this exception in response to insufficient 310 * memory conditions, such as when one attempts to create a Matrix 311 * object that is bigger than available memory. 312 */ 313 class scythe_alloc_error:public scythe_exception 314 { 315 public: 316 scythe_alloc_error (const std::string & file, 317 const std::string & function, 318 const unsigned int &line, 319 const std::string & message = "", throw()320 const bool & halt = false) throw () 321 : scythe_exception ("SCYTHE_ALLOCATION_ERROR", file, function, 322 line, message, halt) 323 { 324 } 325 }; 326 327 /*! 328 * \brief Invalid function argument. 329 * 330 * Library members throw this exception when callers pass incorrect 331 * arguments to a function, such as when one calls the factorial 332 * method with an argument less than 0. 333 */ 334 class scythe_invalid_arg:public scythe_exception 335 { 336 public: 337 scythe_invalid_arg (const std::string & file, 338 const std::string & function, 339 const unsigned int &line, 340 const std::string & message = "", throw()341 const bool & halt = false) throw () 342 : scythe_exception ("SCYTHE_INVALID ARGUMENT", file, function, 343 line, message, halt) 344 { 345 } 346 }; 347 348 /*! 349 * \brief File i/o error. 350 * 351 * Library members throw this exception when errors occur during 352 * file reading, writing, or creation, such as when one passes an 353 * invalid file name to the Matrix class's save method. 354 */ 355 class scythe_file_error:public scythe_exception 356 { 357 public: 358 scythe_file_error(const std::string & file, 359 const std::string & function, 360 const unsigned int &line, 361 const std::string & message = "", throw()362 const bool & halt = false) throw () 363 : scythe_exception ("SCYTHE FILE ERROR", file, function, line, 364 message, halt) 365 { 366 } 367 }; 368 369 /*! \brief Matrix conformation error. 370 * 371 * Library members throw this exception when a caller passes 372 * non-conforming matrices (matrices of incompatible dimensions) to 373 * a routine, such as when one attempt two row vectors. 374 */ 375 class scythe_conformation_error:public scythe_exception 376 { 377 public: 378 scythe_conformation_error(const std::string & file, 379 const std::string & function, 380 const unsigned int &line, 381 const std::string & message = "", throw()382 const bool & halt = false) throw () 383 : scythe_exception ("SCYTHE CONFORMATION ERROR", file, function, 384 line, message, halt) 385 { 386 } 387 }; 388 389 /*! \brief Matrix dimension error. 390 * 391 * Library members throw this exception when a caller passes a 392 * Matrix of the wrong size or shape to a routine. For example, 393 * trying to take the Cholesky decomposition of a non-square Matrix 394 * causes this error. 395 */ 396 397 class scythe_dimension_error:public scythe_exception 398 { 399 public: 400 scythe_dimension_error (const std::string & file, 401 const std::string & function, 402 const unsigned int &line, 403 const std::string & message = "", throw()404 const bool & halt = false) throw () 405 : scythe_exception ("SCYTHE DIMENSION ERROR", file, function, 406 line, message, halt) 407 { 408 } 409 }; 410 411 /*! \brief Null Matrix error. 412 * 413 * Library members throw this exception when a caller passes a null 414 * Matrix to a routine when it expects a non-null argument. For 415 * example, taking the inverse of a null Matrix is impossible, 416 * resulting in this exception. 417 */ 418 class scythe_null_error:public scythe_exception 419 { 420 public: 421 scythe_null_error(const std::string & file, 422 const std::string & function, 423 const unsigned int &line, 424 const std::string & message = "", throw()425 const bool & halt = false) throw () 426 : scythe_exception ("SCYTHE NULL ERROR", file, function, line, 427 message, halt) 428 { 429 } 430 }; 431 432 /*! \brief Matrix type error. 433 * 434 * Library members throw this exception when a caller passes a 435 * Matrix that does not satisfy some required property to a routine. 436 * For example, Cholesky decomposition is designed to work on 437 * positive definite matrices; trying to perform Cholesky 438 * decomposition on a Matrix that does not satisfy this requirement 439 * causes this exception. 440 */ 441 class scythe_type_error:public scythe_exception 442 { 443 public: 444 scythe_type_error(const std::string & file, 445 const std::string & function, 446 const unsigned int &line, 447 const std::string & message = "", throw()448 const bool & halt = false) throw () 449 : scythe_exception ("SCYTHE TYPE ERROR", file, function, line, 450 message, halt) 451 { 452 } 453 }; 454 455 /*! \brief Element out of bounds error. 456 * 457 * Library members throw this exception when a caller attempts to 458 * access an element outside the bounds of a data structure, such as 459 * when one tries to access the 1000th element of a 200-element 460 * Matrix. 461 */ 462 class scythe_bounds_error:public scythe_exception 463 { 464 public: 465 scythe_bounds_error(const std::string & file, 466 const std::string & function, 467 const unsigned int &line, 468 const std::string & message = "", throw()469 const bool & halt = false) throw () 470 : scythe_exception ("SCYTHE BOUNDS ERROR", file, function, 471 line, message, halt) 472 { 473 } 474 }; 475 476 /*! \brief Numerical convergence error. 477 * 478 * Library members throw this exception when a numerical algorithm 479 * fails to converge to a stable value. For example, the BFGS 480 * optimization routine throws this exception when it cannot locate 481 * the minimum of a function to a given tolerance. 482 */ 483 class scythe_convergence_error:public scythe_exception 484 { 485 public: 486 scythe_convergence_error (const std::string & file, 487 const std::string & function, 488 const unsigned int &line, 489 const std::string & message = "", throw()490 const bool & halt = false) throw () 491 : scythe_exception ("SCYTHE CONVERGENCE ERROR", file, function, 492 line, message, halt) 493 { 494 } 495 }; 496 497 /*! \brief Numerical underflow or overflow error. 498 * 499 * Library members throw this exception when the result of a 500 * calculation, assignment, or other operation is to small or large 501 * for the data type holding the value. For example, passing 502 * certain values to the gammafn function can result in underflow or 503 * overflow conditions in the resulting calculations. 504 */ 505 class scythe_range_error:public scythe_exception 506 { 507 public: 508 scythe_range_error (const std::string & file, 509 const std::string & function, 510 const unsigned int &line, 511 const std::string & message = "", throw()512 const bool & halt = false) throw () 513 : scythe_exception ("SCYTHE RANGE ERROR", file, function, line, 514 message, halt) 515 { 516 } 517 }; 518 519 /*! \brief Numerical precision error. 520 * 521 * Library members throw this exception when a routine cannot 522 * complete a computation effectively and will sacrifice reasonable 523 * precision as a consequence. For example, passing a value too 524 * close to a negative integer to the gammafn function renders the 525 * function incapable of returning an accurate result and thus 526 * generates this exception. 527 */ 528 class scythe_precision_error:public scythe_exception 529 { 530 public: 531 scythe_precision_error (const std::string & file, 532 const std::string & function, 533 const unsigned int &line, 534 const std::string & message = "", throw()535 const bool & halt = false) throw () 536 : scythe_exception ("SCYTHE PRECISION ERROR", file, function, 537 line, message, halt) 538 { 539 } 540 }; 541 542 /*! \brief Random number seed error. 543 * 544 * Library members throw this exception when a random number 545 * generator is provided with an illegitimate starting seed value. 546 * For example, the lecuyer class requires seeds within a certain 547 * range to operate properly and will throw this exception when 548 * seeded with a number outside of that range. 549 */ 550 class scythe_randseed_error:public scythe_exception 551 { 552 public: 553 scythe_randseed_error(const std::string & file, 554 const std::string & function, 555 const unsigned int &line, 556 const std::string & message = "", throw()557 const bool & halt = false) throw () 558 : scythe_exception ("SCYTHE RANDOM SEED ERROR", file, function, 559 line, message, halt) 560 { 561 } 562 }; 563 564 /*! \brief Matrix style error. 565 * 566 * Library members throw this exception when they are asked to 567 * operate on a Matrix of the incorrect style. Some routines 568 * require specifically a concrete Matrix or view to work correctly. 569 * For example, only views may reference other matrices; invoking 570 * the reference function on a concrete Matrix will generate this 571 * exception. 572 */ 573 class scythe_style_error:public scythe_exception 574 { 575 public: 576 scythe_style_error(const std::string& file, 577 const std::string& function, 578 const unsigned int& line, 579 const std::string& message = "", throw()580 const bool& halt = false) throw () 581 : scythe_exception("SCYTHE STYLE ERROR", file, function, 582 line, message, halt) 583 {} 584 }; 585 586 /*! \brief LAPACK Internal Error 587 * 588 * Library members throw this exception when an underlying LAPACK or 589 * BLAS routine indicates that an internal error has occurred. 590 * 591 */ 592 class scythe_lapack_internal_error:public scythe_exception 593 { 594 public: 595 scythe_lapack_internal_error(const std::string& file, 596 const std::string& function, 597 const unsigned int& line, 598 const std::string& message = "", throw()599 const bool& halt = false) throw () 600 : scythe_exception("SCYTHE LAPACK/BLAS INTERNAL ERROR", file, 601 function, line, message, halt) 602 {} 603 }; 604 605 /*! \brief Unexpected call to default error. 606 * 607 * This error should not occur. If it occurs in your code, please 608 * contact the Scythe developers to report the problem. 609 * 610 */ 611 class scythe_unexpected_default_error:public scythe_exception 612 { 613 public: 614 scythe_unexpected_default_error(const std::string& file, 615 const std::string& function, 616 const unsigned int& line, 617 const std::string& message = "", throw()618 const bool& halt = false) throw () 619 : scythe_exception("SCYTHE UNEXPECTED DEFAULT ERROR", file, 620 function, line, message, halt) 621 {} 622 }; 623 624 // The definition of our terminate handler described above scythe_terminate()625 inline void scythe_terminate () 626 { 627 #ifdef SCYTHE_RPACK 628 Rprintf(serr->what()); 629 error("Aborting Scythe C++ execution"); 630 #else 631 std::cerr << serr->what() << std::endl; 632 std::cerr << std::endl; 633 abort (); 634 #endif 635 } 636 637 } // end namspace SCYTHE 638 639 #endif /* SCYTHE_ERROR_H */ 640