Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #ifndef __PLUMED_tools_Exception_h
23 : #define __PLUMED_tools_Exception_h
24 :
25 : #include <exception>
26 : #include <string>
27 : #include <stdexcept>
28 : #include <sstream>
29 : #include <array>
30 :
31 : namespace PLMD {
32 :
33 : /**
34 : \ingroup TOOLBOX
35 : Class to deal with Plumed runtime errors.
36 :
37 : This class and the related macros can be used to detect programming
38 : errors. Typical cases are internal inconsistencies or errors in the plumed<->MD
39 : interface. Mistakes made by final users (i.e. in the `plumed.dat` file)
40 : should probably be documented in some better way (e.g. printing parts of the manual in the output).
41 : However, also this class allows for significant information to be attached.
42 : Let's try to make error messages as informative as possible!
43 :
44 : \note This class has been rewritten in PLUMED 2.5. It works in a backward compatible manner,
45 : but is much more flexible. The main novelty is that we can use insertion operators to
46 : add arbitrary messages, as in `plumed_error()<<"check this vector "<<v;`
47 : See below for more details.
48 :
49 : To throw an error, just throw a c++ exception
50 : \verbatim
51 : if(something_bad) throw Exception();
52 : \endverbatim
53 : or better add an error message to that
54 : \verbatim
55 : if(something_bad) throw Exception("describe the error here");
56 : \endverbatim
57 :
58 : As of PLUMED 2.5 you can add multiple messages, they will just be concatenated,
59 : but to do se you should use the insertion operator. Notice that anything that
60 : can be formatted with an insertion operator can go to the exception, even a \ref Vector
61 : \verbatim
62 : Vector v;
63 : if(something_bad) throw Exception()<<"problem with this "<<v;
64 : \endverbatim
65 : In principle you can mix the two syntax (add a message as an argument and insert others with `<<`),
66 : however it is not very clear and should be avoided.
67 : We only allow using arguments in parenthesis in order to keep backward compatibility.
68 :
69 : \par Using macros
70 :
71 : In order to provide more context, especially for debugging, it might be useful to know where the exception
72 : originated from. The macros below add information about the exact location of the error in the file (filename, line
73 : and, when available, function name). Macros ending in "error" unconditionally throw
74 : the exception, whereas macros ending in "assert" first perform a conditional check
75 : (similarly to standard assert()).
76 : An extra `m` in the name (e.g. `plumed_merror`) indicates a macro that provides a message as its argument.
77 : However, as of PLUMED 2.5 we should prefer adding messages using insertion operators.
78 : \verbatim
79 : // this is correct but not recommended. add a message please!
80 : plumed_assert(a>0);
81 :
82 : // this is the old syntax (with argument).
83 : // this syntax is basically available for backward compatibility.
84 : plumed_massert(a>0,"a should be larger than zero);
85 :
86 : // this is the recommended syntax, with insertion operators.
87 : // it allows to easily insert multiple objects
88 : plumed_assert(a>0)<<"a should be larger than zero. a="<<a;
89 :
90 : // same as above, but the test is made explicitly:
91 : if(a<=0) plumed_error();
92 : if(a<=0) plumed_error("a should be larger than zero);
93 : if(a<=0) plumed_error()<<"a should be larger than zero. a="<<a;
94 : \endverbatim
95 :
96 : The additional macros
97 : plumed_dbg_assert() and plumed_dbg_massert() are similar
98 : to plumed_assert() and plumed_massert() respectively, but the corresponding
99 : check is only performed when NDEBUG macro is not defined. They should
100 : be used when the check is expensive and should be skipped in production
101 : code. So, for instance, in the following case:
102 : \verbatim
103 : plumed_dbg_assert(expensive_function(i)>0)<<"message";
104 : \endverbatim
105 : `expensive_function()` is not called in the production code.
106 : Notice that the compiler should be able to completely optimize away the
107 : whole statement including functions used to produce the message as in this example:
108 : \verbatim
109 : plumed_dbg_assert(expensive_function(i)>0)<<"I did this check "<<other_expensive_function(i);
110 : \endverbatim
111 :
112 : Finally, notice that there is another macro available, \ref plumed_here.
113 : In can be used in order to create an exception with information about the
114 : line/file coordinates without trowing it. That is, the two following syntaxes
115 : are equivalent
116 : \verbatim
117 : // First way, all at once
118 : plumed_error()<<"some message";
119 : /////////////////////////////////
120 : // Second way, one step at a time
121 : // Create exception
122 : Exception e;
123 : // Append information about line and file
124 : e<<plumed_here;
125 : // Append some other message
126 : e<<"some message";
127 : // Throw the resulting exception
128 : throw e;
129 : \endverbatim
130 :
131 : Exceptions can be caught within plumed or outside of it.
132 : E.g., in an external c++ code using PLUMED as a library, one can type
133 : \verbatim
134 : try{
135 : plumed.cmd("setPrecision",n);
136 : } catch (const std::exception & e) {
137 : std::printf("ee %s",e.what());
138 : exit(1);
139 : }
140 : \endverbatim
141 : This can be useful if an external code wants to exit in a controlled manner
142 : (e.g. flushing files, printing the error message in a specific file, etc.)
143 : but is anyway limited to c++ codes. Moreover,
144 : since these errors are expected to be unrecoverable, the MD code will
145 : usually not be able to do something more clever than exiting.
146 :
147 : \note
148 : We store message and stack trace in growing strings. This is in
149 : principle not recommended, since copying the exception might fail if
150 : copying the string throw another exception. However, this has been like
151 : this in all previous PLUMED versions. In case it is necessary, we can replace
152 : it later with a fixed size array placed on the stack.
153 :
154 : */
155 : class Exception : public std::exception
156 : {
157 : /// Reported message. Can be updated.
158 : std::string msg;
159 : /// Flag to remember if we have to write the `+++ message follows +++` string.
160 : /// Needed so that the string appears only at the beginning of the message.
161 : bool note=true;
162 : /// Stream used to insert objects.
163 : /// It is not copied when the Exception is copied.
164 : std::stringstream stream;
165 : /// Stack trace, computed at construction
166 : std::array<void*,128> callstack;
167 : /// Number of frames in stack, computed at construction
168 : int callstack_n=0;
169 : /// Parsed stack trace. Built at first use, thus mutable.
170 : mutable std::string stackTrace;
171 :
172 : public:
173 :
174 : /// Auxiliary containing the location of the exception in the file.
175 : /// Typically used from the macros below.
176 : class Location {
177 : public:
178 : const char*file;
179 : const unsigned line;
180 : const char* pretty;
181 54 : explicit Location(const char*file,unsigned line,const char* pretty=nullptr):
182 54 : file(file),
183 54 : line(line),
184 54 : pretty(pretty)
185 : {}
186 : };
187 :
188 : /// Auxiliary containing the failed assertion.
189 : /// Typically used from the macros below.
190 : class Assertion {
191 : public:
192 : const char*assertion;
193 10 : explicit Assertion(const char*assertion=nullptr):
194 10 : assertion(assertion)
195 : {}
196 : };
197 :
198 : /// Auxiliary class used to throw exceptions.
199 : /// It just defines <<= operator so that:
200 : /// - exceptions can be thrown calling std::throw_with_nested
201 : /// with a "throw like" syntax
202 : /// - precedence is the same as the throw operator
203 : /// (see https://en.cppreference.com/w/cpp/language/operator_precedence)
204 : class Throw {
205 : public:
206 : template <typename E>
207 2 : [[noreturn]] void operator <<=(E&&e) {
208 4 : if(std::current_exception()) {
209 2 : std::throw_with_nested(e);
210 : } else {
211 : // if not nested, avoid modifying the exception type
212 0 : throw e;
213 : }
214 : }
215 : };
216 :
217 : /// Default constructor with no message.
218 : /// Only records the stack trace.
219 : Exception();
220 :
221 : /// Constructor compatible with PLUMED <=2.4.
222 31 : explicit Exception(const std::string & msg):
223 31 : Exception()
224 : {
225 31 : *this << msg;
226 31 : }
227 :
228 : /// Copy constructor.
229 : /// Needed to make sure stream is not copied
230 70 : Exception(const Exception & e):
231 70 : msg(e.msg),
232 70 : note(e.note),
233 70 : callstack(e.callstack),
234 70 : callstack_n(e.callstack_n),
235 70 : stackTrace(e.stackTrace)
236 : {
237 70 : }
238 :
239 : /// Assignment.
240 : /// Needed to make sure stream is not copied
241 : Exception & operator=(const Exception & e) {
242 : msg=e.msg;
243 : note=e.note;
244 : callstack=e.callstack;
245 : callstack_n=e.callstack_n;
246 : stackTrace=e.stackTrace;
247 : stream.str("");
248 : return *this;
249 : }
250 :
251 : /// Returns the error message.
252 : /// In case the environment variable PLUMED_STACK_TRACE was defined
253 : /// and equal to `yes` when the exception was raised,
254 : /// the error message will contain the stack trace as well.
255 121 : const char* what() const noexcept override {return msg.c_str();}
256 :
257 : /// Returns the stack trace as a string.
258 : /// This function is slow as it requires building a parsed string.
259 : /// If storing the stack for later usage, you might prefer to use trace().
260 : const char* stack() const;
261 :
262 : /// Returns the callstack.
263 : const std::array<void*,128> & trace() const noexcept {return callstack;}
264 :
265 : /// Returns the number of elements in the trace array
266 : int trace_n() const noexcept {return callstack_n;}
267 :
268 : /// Destructor should be defined and should not throw other exceptions
269 144 : ~Exception() noexcept override {}
270 :
271 : /// Insert location.
272 : /// Format the location properly.
273 : Exception& operator<<(const Location&);
274 :
275 : /// Insert assertion.
276 : /// Format the assertion properly
277 : Exception& operator<<(const Assertion&);
278 :
279 : /// Insert string.
280 : /// Append this string to the message.
281 : Exception& operator<<(const std::string&);
282 :
283 : /// Insert anything else.
284 : /// This allows to dump also other types (e.g. double, or even Vector).
285 : /// Anything that can be written on a stream can go here.
286 : template<typename T>
287 267 : Exception& operator<<(const T & x) {
288 267 : stream<<x;
289 267 : (*this)<<stream.str();
290 267 : stream.str("");
291 267 : return *this;
292 : }
293 : };
294 :
295 : /// Class representing a generic error
296 143 : class ExceptionError :
297 : public Exception {
298 : public:
299 1 : using Exception::Exception;
300 : template<typename T>
301 : ExceptionError& operator<<(const T & x) {
302 89 : *static_cast<Exception*>(this) <<x;
303 : return *this;
304 : }
305 : };
306 :
307 : /// Class representing a debug error (can only be thrown when using debug options)
308 0 : class ExceptionDebug :
309 : public Exception {
310 : public:
311 1 : using Exception::Exception;
312 : template<typename T>
313 : ExceptionDebug& operator<<(const T & x) {
314 : *static_cast<Exception*>(this) <<x;
315 : return *this;
316 : }
317 : };
318 :
319 : /// Class representing a type error in the PLMD::Plumed interface
320 111 : class ExceptionTypeError :
321 : public Exception {
322 : public:
323 0 : using Exception::Exception;
324 : template<typename T>
325 : ExceptionTypeError& operator<<(const T & x) {
326 107 : *static_cast<Exception*>(this) <<x;
327 : return *this;
328 : }
329 : };
330 :
331 : #ifdef __GNUG__
332 : // With GNU compiler, we can use __PRETTY_FUNCTION__ to get the function name
333 : #define __PLUMED_FUNCNAME __PRETTY_FUNCTION__
334 : #else
335 : // Otherwise, we use the standard C++11 variable
336 : #define __PLUMED_FUNCNAME __func__
337 : #endif
338 :
339 : #ifndef PLUMED_MODULE_DIR
340 : #define PLUMED_MODULE_DIR ""
341 : #endif
342 :
343 : /// \relates PLMD::Exception
344 : /// Auxiliary macro that generates a PLMD::Exception::Location object.
345 : /// Might be useful if we want to use derived exceptions that could
346 : /// be thrown using `throw DerivedException()<<plumed_here<<" "<<other stuff"`.
347 : /// It is used in the macros below to throw PLMD::Exception.
348 : #define plumed_here PLMD::Exception::Location(PLUMED_MODULE_DIR __FILE__,__LINE__,__PLUMED_FUNCNAME)
349 :
350 : /// \relates PLMD::Exception
351 : /// Throw an exception with information about the position in the file.
352 : /// Messages can be inserted with `plumed_error()<<"message"`.
353 : #define plumed_error() throw PLMD::ExceptionError() << plumed_here
354 :
355 : /// \relates PLMD::Exception
356 : /// Throw a nested exception with information about the position in the file.
357 : /// It preliminary checks if we are in a catch block. If so, the caught exception
358 : /// is rethrown as nested. If not, it throws a normal ExceptionError.
359 : /// NB in theory we could have just redefined plumed_error to this, but
360 : /// for some reason cppcheck does not understand that the <<= operator used here is
361 : /// [[noreturn]] and thus gives many false warnings
362 : #define plumed_error_nested() PLMD::Exception::Throw() <<= PLMD::ExceptionError() << plumed_here
363 :
364 : /// \relates PLMD::Exception
365 : /// Throw an exception with information about the position in the file
366 : /// and a message. Mostly available for backward compatibility
367 : #define plumed_merror(msg) plumed_error() << msg
368 :
369 : /// \relates PLMD::Exception
370 : /// Launches plumed_merror only if test evaluates to false.
371 : /// The string describing the test is also reported.
372 : /// Further messages can be inserted with `<<`.
373 : #define plumed_assert(test) if(!(test)) plumed_error() << PLMD::Exception::Assertion(#test)
374 :
375 : /// \relates PLMD::Exception
376 : /// Launches plumed_merror only if test evaluates to false.
377 : /// The string describing the test is also reported, in addition to
378 : /// messages reported in the extra argument. Mostly available for backward compatibility.
379 : #define plumed_massert(test,msg) plumed_assert(test) << msg
380 :
381 : #ifdef NDEBUG
382 :
383 : // These are the versions used when compiling with NDEBUG flag.
384 : // The if constexpr(false) gurarantees that the compiler will optimize away the assertion
385 : // We are not using an empty macro becasue the user may want to use the << operator
386 :
387 : #define plumed_dbg_assert(test) if constexpr(false) plumed_assert(true)
388 : #define plumed_dbg_massert(test,msg) if constexpr(false) plumed_massert(true,msg)
389 :
390 : #else
391 :
392 : /// \relates PLMD::Exception
393 : /// Same as \ref plumed_assert, but only evaluates the condition if NDEBUG is not defined.
394 : #define plumed_dbg_assert(test) if(!(test)) PLMD::Exception::Throw() <<= PLMD::ExceptionDebug() << plumed_here << PLMD::Exception::Assertion(#test) << "(this check is enabled only in debug builds)\n"
395 :
396 : /// \relates PLMD::Exception
397 : /// Same as \ref plumed_massert, but only evaluates the condition if NDEBUG is not defined.
398 : #define plumed_dbg_massert(test,msg) plumed_dbg_assert(test) << msg
399 :
400 : #endif
401 :
402 : }
403 : #endif
|