LCOV - code coverage report
Current view: top level - tools - Exception.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 33 36 91.7 %
Date: 2024-10-18 14:00:25 Functions: 34 139 24.5 %

          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

Generated by: LCOV version 1.16