LCOV - code coverage report
Current view: top level - tools - Exception.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 30 30 100.0 %
Date: 2024-10-11 08:09:47 Functions: 20 134 14.9 %

          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

Generated by: LCOV version 1.15