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