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