1 /**
2  * @file ctexceptions.h
3  *   Definitions for the classes that are
4  *   thrown when %Cantera experiences an error condition
5  *   (also contains errorhandling module text - see \ref errorhandling).
6  */
7 
8 // This file is part of Cantera. See License.txt in the top-level directory or
9 // at https://cantera.org/license.txt for license and copyright information.
10 
11 #ifndef CT_CTEXCEPTIONS_H
12 #define CT_CTEXCEPTIONS_H
13 
14 #include "cantera/base/fmt.h"
15 #include <exception>
16 
17 namespace Cantera
18 {
19 
20 /*!
21  * @defgroup errorhandling Error Handling
22  *
23  * \brief These classes and related functions are used to handle errors and
24  *        unknown events within Cantera.
25  *
26  * The general idea is that exceptions are thrown using the common base class
27  * called CanteraError. Derived types of CanteraError characterize what type of
28  * error is thrown. A list of all of the thrown errors is kept in the
29  * Application class.
30  *
31  * Any exceptions which are not caught cause a fatal error exit from the
32  * program.
33  *
34  * A group of defines may be used during debugging to assert conditions which
35  * should be true. These are named AssertTrace(), AssertThrow(), and
36  * AssertThrowMsg(). Examples of their usage is given below.
37  *
38  * @code
39  * AssertTrace(p == OneAtm);
40  * AssertThrow(p == OneAtm, "Kinetics::update");
41  * AssertThrowMsg(p == OneAtm, "Kinetics::update",
42  *              "Algorithm limited to atmospheric pressure");
43  * @endcode
44  *
45  * Their first argument is a boolean. If the boolean is not true, a CanteraError
46  * is thrown, with descriptive information indicating where the error occurred.
47  * The Assert* checks are skipped if the NDEBUG preprocessor symbol is defined,
48  * e.g. with the compiler option -DNDEBUG.
49  */
50 
51 
52 //! Base class for exceptions thrown by Cantera classes.
53 /*!
54  * This class is the base class for exceptions thrown by Cantera. It inherits
55  * from std::exception so that normal error handling operations from
56  * applications may automatically handle the errors in their own way.
57  *
58  * @ingroup errorhandling
59  */
60 class CanteraError : public std::exception
61 {
62 public:
63     //! Normal Constructor for the CanteraError base class
64     /*!
65      * @param procedure Name of the function within which the error was
66      *             generated. For member functions, this should be written as
67      *             `ClassName::functionName`. For constructors, this should be
68      *             `ClassName::ClassName`. Arguments can be specified to
69      *             disambiguate overloaded functions, e.g.
70      *             `ClassName::functionName(int, int)`.
71      * @param msg  Descriptive string describing the type of error message. This
72      *     can be a fmt-style format string (i.e. using curly braces to indicate
73      *     fields), which will be used with additional arguments to generate a
74      *     formatted error message
75      * @param args Arguments which will be used to interpolate the format string
76      */
77     template <typename... Args>
CanteraError(const std::string & procedure,const std::string & msg,const Args &...args)78     CanteraError(const std::string& procedure, const std::string& msg,
79                  const Args&... args)
80         : procedure_(procedure)
81     {
82         if (sizeof...(args) == 0) {
83             msg_ = msg;
84         } else {
85             msg_ = fmt::format(msg, args...);
86         }
87     }
88 
89     //! Destructor for base class does nothing
~CanteraError()90     virtual ~CanteraError() throw() {};
91 
92     //! Get a description of the error
93     const char* what() const throw();
94 
95     //! Method overridden by derived classes to format the error message
96     virtual std::string getMessage() const;
97 
98     //! Method overridden by derived classes to indicate their type
getClass()99     virtual std::string getClass() const {
100         return "CanteraError";
101     }
102 
103 protected:
104     //! Protected default constructor discourages throwing errors containing no
105     //! information.
CanteraError()106     CanteraError() {};
107 
108     //! Constructor used by derived classes that override getMessage()
109     explicit CanteraError(const std::string& procedure);
110 
111     //! The name of the procedure where the exception occurred
112     std::string procedure_;
113     mutable std::string formattedMessage_; //!< Formatted message returned by what()
114 
115 private:
116     std::string msg_; //!< Message associated with the exception
117 };
118 
119 
120 //! Array size error.
121 /*!
122  * This error is thrown if a supplied length to a vector supplied to Cantera is
123  * too small.
124  *
125  * @ingroup errorhandling
126  */
127 class ArraySizeError : public CanteraError
128 {
129 public:
130     //! Constructor
131     /*!
132      * The length needed is supplied by the argument, reqd, and the
133      * length supplied is given by the argument sz.
134      *
135      * @param procedure String name for the function within which the error was
136      *             generated.
137      * @param sz   This is the length supplied to Cantera.
138      * @param reqd This is the required length needed by Cantera
139      */
ArraySizeError(const std::string & procedure,size_t sz,size_t reqd)140     ArraySizeError(const std::string& procedure, size_t sz, size_t reqd) :
141         CanteraError(procedure), sz_(sz), reqd_(reqd) {}
142 
143     virtual std::string getMessage() const;
getClass()144     virtual std::string getClass() const {
145         return "ArraySizeError";
146     }
147 
148 private:
149     size_t sz_, reqd_;
150 };
151 
152 
153 //! An array index is out of range.
154 /*!
155  *  @ingroup errorhandling
156  */
157 class IndexError : public CanteraError
158 {
159 public:
160     //! Constructor
161     /*!
162      * This class indicates an out-of-bounds array index.
163      *
164      * @param func String name for the function within which the error was
165      *             generated.
166      * @param arrayName name of the corresponding array
167      * @param m   This is the value of the out-of-bounds index.
168      * @param mmax This is the maximum allowed value of the index. The
169      *             minimum allowed value is assumed to be 0.
170      */
IndexError(const std::string & func,const std::string & arrayName,size_t m,size_t mmax)171     IndexError(const std::string& func, const std::string& arrayName, size_t m, size_t mmax) :
172         CanteraError(func), arrayName_(arrayName), m_(m), mmax_(mmax) {}
173 
~IndexError()174     virtual ~IndexError() throw() {};
175     virtual std::string getMessage() const;
getClass()176     virtual std::string getClass() const {
177         return "IndexError";
178     }
179 
180 private:
181     std::string arrayName_;
182     size_t m_, mmax_;
183 };
184 
185 //! An error indicating that an unimplemented function has been called
186 class NotImplementedError : public CanteraError
187 {
188 public:
189     //! @param func Name of the unimplemented function, e.g.
190     //!     `ClassName::functionName`
NotImplementedError(const std::string & func)191     NotImplementedError(const std::string& func) :
192         CanteraError(func, "Not implemented.") {}
193 
194     //! Alternative constructor taking same arguments as @see CanteraError
195     template <typename... Args>
NotImplementedError(const std::string & func,const std::string & msg,const Args &...args)196     NotImplementedError(const std::string& func, const std::string& msg,
197                         const Args&... args) :
198         CanteraError(func, msg, args...) {}
199 
getClass()200     virtual std::string getClass() const {
201         return "NotImplementedError";
202     }
203 };
204 
205 //! Provides a line number
206 #define XSTR_TRACE_LINE(s) STR_TRACE_LINE(s)
207 
208 //! Provides a line number
209 #define STR_TRACE_LINE(s) #s
210 
211 //! Provides a std::string variable containing the file and line number
212 /*!
213  * This is a std:string containing the file name and the line number
214  */
215 #define STR_TRACE (std::string(__FILE__) + ":" + XSTR_TRACE_LINE(__LINE__))
216 
217 #ifdef NDEBUG
218 #ifndef AssertTrace
219 #  define AssertTrace(expr)                        ((void) (0))
220 #endif
221 #ifndef AssertThrow
222 #  define AssertThrow(expr, procedure)             ((void) (0))
223 #endif
224 #ifndef AssertThrowMsg
225 #  define AssertThrowMsg(expr,procedure, ...)  ((void) (0))
226 #endif
227 #else
228 
229 //! Assertion must be true or an error is thrown
230 /*!
231  * Assertion must be true or else a CanteraError is thrown. A diagnostic string
232  * containing the file and line number, indicating where the error occurred is
233  * added to the thrown object.
234  *
235  * @param expr  Boolean expression that must be true
236  *
237  * @ingroup errorhandling
238  */
239 #ifndef AssertTrace
240 #  define AssertTrace(expr)  ((expr) ? (void) 0 : throw CanteraError(STR_TRACE, std::string("failed assert: ") + #expr))
241 #endif
242 
243 //! Assertion must be true or an error is thrown
244 /*!
245  * Assertion must be true or else a CanteraError is thrown. A diagnostic string
246  * indicating where the error occurred is added to the thrown object.
247  *
248  * @param expr  Boolean expression that must be true
249  * @param procedure  Character string or std:string expression indicating the
250  *     procedure where the assertion failed
251  * @ingroup errorhandling
252  */
253 #ifndef AssertThrow
254 #  define AssertThrow(expr, procedure)   ((expr) ? (void) 0 : throw CanteraError(procedure, std::string("failed assert: ") + #expr))
255 #endif
256 
257 //! Assertion must be true or an error is thrown
258 /*!
259  * Assertion must be true or else a CanteraError is thrown. A diagnostic string
260  * indicating where the error occurred is added to the thrown object.
261  *
262  * @param expr  Boolean expression that must be true
263  * @param procedure  Character string or std:string expression indicating
264  *                   the procedure where the assertion failed
265  * Additional arguments are passed on to the constructor for CanteraError to
266  * generate a formatted error message.
267  *
268  * @ingroup errorhandling
269  */
270 #ifndef AssertThrowMsg
271 #  define AssertThrowMsg(expr, procedure, ...)  ((expr) ? (void) 0 : throw CanteraError(procedure + std::string(":\nfailed assert: \"") + std::string(#expr) + std::string("\""), __VA_ARGS__))
272 #endif
273 
274 #endif
275 
276 //! Throw an exception if the specified exception is not a finite number.
277 #ifndef AssertFinite
278 #  define AssertFinite(expr, procedure, ...) AssertThrowMsg(expr < BigNumber && expr > -BigNumber, procedure, __VA_ARGS__)
279 #endif
280 
281 }
282 
283 #endif
284