From 8f970ae672ebe8a53f7cf6b56c50ef4858fea314 Mon Sep 17 00:00:00 2001
From: Giovanni Bussi <giovanni.bussi@gmail.com>
Date: Tue, 13 Nov 2018 15:45:41 +0100
Subject: [PATCH] Completed exception mapping.

Now every exception thrown within PLUMED is remapped in the wrapper.

In particular:
- All standard C++ exceptions are remapped to equivalent ones.
- PLUMED exceptions and lepton exceptions are remapped to ad hoc classes.
- Any other exception derived from std::exception is mapped to std::exception.
- Any other exception is rethrown after printing a warning.
---
 src/core/Makefile                  |   2 +-
 src/core/PlumedMain.cpp            |  66 +++++++
 src/core/PlumedMainInitializer.cpp | 100 +++++++++--
 src/wrapper/Plumed.h               | 273 ++++++++++++++++++++++++++---
 4 files changed, 396 insertions(+), 45 deletions(-)

diff --git a/src/core/Makefile b/src/core/Makefile
index a5acc39c8..47b809e16 100644
--- a/src/core/Makefile
+++ b/src/core/Makefile
@@ -1,4 +1,4 @@
-USE=config tools
+USE=config tools lepton
 
 # generic makefile
 include ../maketools/make.module
diff --git a/src/core/PlumedMain.cpp b/src/core/PlumedMain.cpp
index 4d31ce87d..89275e293 100644
--- a/src/core/PlumedMain.cpp
+++ b/src/core/PlumedMain.cpp
@@ -40,16 +40,77 @@
 #include "tools/OpenMP.h"
 #include "tools/Tools.h"
 #include "tools/Stopwatch.h"
+#include "lepton/Exception.h"
 #include "DataFetchingObject.h"
 #include <cstdlib>
 #include <cstring>
 #include <set>
 #include <unordered_map>
+#include <ios>
+#include <future>
 
 using namespace std;
 
 namespace PLMD {
 
+/// Small utility just used in this file to throw arbitrary exceptions
+static void testThrow(const char* what) {
+  auto words=Tools::getWords(what);
+  plumed_assert(words.size()>0);
+#define __PLUMED_THROW_NOMSG(type) if(words[0]==#type) throw type()
+#define __PLUMED_THROW_MSG(type) if(words[0]==#type) throw type(what)
+  __PLUMED_THROW_MSG(PLMD::ExceptionError);
+  __PLUMED_THROW_MSG(PLMD::ExceptionDebug);
+  __PLUMED_THROW_MSG(PLMD::Exception);
+  __PLUMED_THROW_MSG(PLMD::lepton::Exception);
+  __PLUMED_THROW_NOMSG(std::bad_exception);
+#ifdef __PLUMED_LIBCXX11
+  __PLUMED_THROW_NOMSG(std::bad_array_new_length);
+#endif
+  __PLUMED_THROW_NOMSG(std::bad_alloc);
+#ifdef __PLUMED_LIBCXX11
+  __PLUMED_THROW_NOMSG(std::bad_function_call);
+  __PLUMED_THROW_NOMSG(std::bad_weak_ptr);
+#endif
+  __PLUMED_THROW_NOMSG(std::bad_cast);
+  __PLUMED_THROW_NOMSG(std::bad_typeid);
+  __PLUMED_THROW_MSG(std::underflow_error);
+  __PLUMED_THROW_MSG(std::overflow_error);
+  __PLUMED_THROW_MSG(std::range_error);
+  __PLUMED_THROW_MSG(std::runtime_error);
+  __PLUMED_THROW_MSG(std::out_of_range);
+  __PLUMED_THROW_MSG(std::length_error);
+  __PLUMED_THROW_MSG(std::domain_error);
+  __PLUMED_THROW_MSG(std::invalid_argument);
+  __PLUMED_THROW_MSG(std::logic_error);
+
+#ifdef __PLUMED_LIBCXX11
+  if(words[0]=="std::system_error") {
+    plumed_assert(words.size()>2);
+    int error_code;
+    Tools::convert(words[2],error_code);
+    if(words[1]=="std::generic_category") throw std::system_error(error_code,std::generic_category(),what);
+    if(words[1]=="std::system_category") throw std::system_error(error_code,std::system_category(),what);
+    if(words[1]=="std::iostream_category") throw std::system_error(error_code,std::iostream_category(),what);
+    if(words[1]=="std::future_category") throw std::system_error(error_code,std::future_category(),what);
+  }
+#endif
+
+  if(words[0]=="std::ios_base::failure") {
+#ifdef __PLUMED_LIBCXX11
+    int error_code=0;
+    if(words.size()>2) Tools::convert(words[2],error_code);
+    if(words.size()>1 && words[1]=="std::generic_category") throw std::ios_base::failure(what,std::error_code(error_code,std::generic_category()));
+    if(words.size()>1 && words[1]=="std::system_category") throw std::ios_base::failure(what,std::error_code(error_code,std::system_category()));
+    if(words.size()>1 && words[1]=="std::iostream_category") throw std::ios_base::failure(what,std::error_code(error_code,std::iostream_category()));
+    if(words.size()>1 && words[1]=="std::future_category") throw std::ios_base::failure(what,std::error_code(error_code,std::future_category()));
+#endif
+    throw std::ios_base::failure(what);
+  }
+
+  plumed_error() << "unknown exception" << what;
+}
+
 PlumedMain::PlumedMain():
   initialized(false),
 // automatically write on log in destructor
@@ -388,6 +449,11 @@ void PlumedMain::cmd(const std::string & word,void*val) {
         CHECK_NOTNULL(val,word);
         OpenMP::setNumThreads(*static_cast<unsigned*>(val));
         break;
+      /* ADDED WITH API==6 */
+      /* only used for testing */
+      case cmd_throw:
+        CHECK_NOTNULL(val,word);
+        testThrow((const char*) val);
       /* STOP API */
       case cmd_setMDEngine:
         CHECK_NOTINIT(initialized,word);
diff --git a/src/core/PlumedMainInitializer.cpp b/src/core/PlumedMainInitializer.cpp
index 6ce262328..76d8884e9 100644
--- a/src/core/PlumedMainInitializer.cpp
+++ b/src/core/PlumedMainInitializer.cpp
@@ -22,9 +22,12 @@
 #include "PlumedMainInitializer.h"
 #include "PlumedMain.h"
 #include "tools/Exception.h"
+#include "lepton/Exception.h"
 #include <cstdlib>
 #include <cstring>
 #include <iostream>
+#include <system_error>
+#include <future>
 #if defined __PLUMED_HAS_DLOPEN
 #include <dlfcn.h>
 #endif
@@ -48,21 +51,6 @@ extern "C" void*plumed_plumedmain_create() {
   }
 }
 
-#define __PLUMED_CATCH(e,nothrow) \
-  catch(PLMD::ExceptionError & e) { \
-    nothrow.handler(nothrow.ptr,20200,e.what(),nullptr); \
-  } catch(PLMD::ExceptionDebug & e) { \
-    nothrow.handler(nothrow.ptr,20100,e.what(),nullptr); \
-  } catch(PLMD::Exception & e) { \
-    nothrow.handler(nothrow.ptr,20000,e.what(),nullptr); \
-  } catch(std::runtime_error & e) { \
-    nothrow.handler(nothrow.ptr,10200,e.what(),nullptr); \
-  } catch(std::logic_error & e) { \
-    nothrow.handler(nothrow.ptr,10100,e.what(),nullptr); \
-  } catch(std::exception & e) { \
-    nothrow.handler(nothrow.ptr,10000,e.what(),nullptr); \
-  }
-
 extern "C" void plumed_plumedmain_cmd(void*plumed,const char*key,const void*val) {
   plumed_massert(plumed,"trying to use a plumed object which is not initialized");
   auto p=static_cast<PLMD::PlumedMain*>(plumed);
@@ -76,7 +64,87 @@ extern "C" void plumed_plumedmain_cmd_nothrow(void*plumed,const char*key,const v
   try {
     plumed_massert(plumed,"trying to use a plumed object which is not initialized");
     static_cast<PLMD::PlumedMain*>(plumed)->cmd(key,val);;
-  } __PLUMED_CATCH(e,nothrow)
+  } catch(PLMD::ExceptionError & e) {
+    nothrow.handler(nothrow.ptr,20200,e.what(),nullptr);
+  } catch(PLMD::ExceptionDebug & e) {
+    nothrow.handler(nothrow.ptr,20100,e.what(),nullptr);
+  } catch(PLMD::Exception & e) {
+    nothrow.handler(nothrow.ptr,20000,e.what(),nullptr);
+  } catch(PLMD::lepton::Exception & e) {
+    nothrow.handler(nothrow.ptr,19900,e.what(),nullptr);
+    // 11000 to 12000 are "bad exceptions". message will be copied without new allocations
+  } catch(bad_exception & e) {
+    nothrow.handler(nothrow.ptr,11500,e.what(),nullptr);
+#ifdef __PLUMED_LIBCXX11
+  } catch(bad_array_new_length & e) {
+    nothrow.handler(nothrow.ptr,11410,e.what(),nullptr);
+#endif
+  } catch(bad_alloc & e) {
+    nothrow.handler(nothrow.ptr,11400,e.what(),nullptr);
+#ifdef __PLUMED_LIBCXX11
+  } catch(bad_function_call & e) {
+    nothrow.handler(nothrow.ptr,11300,e.what(),nullptr);
+  } catch(bad_weak_ptr & e) {
+    nothrow.handler(nothrow.ptr,11200,e.what(),nullptr);
+#endif
+  } catch(bad_cast & e) {
+    nothrow.handler(nothrow.ptr,11100,e.what(),nullptr);
+  } catch(bad_typeid & e) {
+    nothrow.handler(nothrow.ptr,11000,e.what(),nullptr);
+    // not implemented yet: std::regex_error
+    // we do not allow regex yet due to portability problems with gcc 4.8
+    // as soon as we transition to using <regex> it should be straightforward to add
+  } catch(std::ios_base::failure & e) {
+#ifdef __PLUMED_LIBCXX11
+    int value=e.code().value();
+    const void* opt[3]= {"c",&value,nullptr}; // "c" passes the error code. nullptr terminates the optional part.
+    if(e.code().category()==generic_category()) nothrow.handler(nothrow.ptr,10230,e.what(),opt);
+    else if(e.code().category()==system_category()) nothrow.handler(nothrow.ptr,10231,e.what(),opt);
+    else if(e.code().category()==iostream_category()) nothrow.handler(nothrow.ptr,10232,e.what(),opt);
+    else if(e.code().category()==future_category()) nothrow.handler(nothrow.ptr,10233,e.what(),opt);
+#endif
+    // 10239 represents std::ios_base::failure with default constructur
+    nothrow.handler(nothrow.ptr,10239,e.what(),nullptr);
+#ifdef __PLUMED_LIBCXX11
+  } catch(std::system_error & e) {
+    int value=e.code().value();
+    const void* opt[3]= {"c",&value,nullptr}; // "c" passes the error code. nullptr terminates the optional part.
+    if(e.code().category()==generic_category()) nothrow.handler(nothrow.ptr,10220,e.what(),opt);
+    else if(e.code().category()==system_category()) nothrow.handler(nothrow.ptr,10221,e.what(),opt);
+    else if(e.code().category()==iostream_category()) nothrow.handler(nothrow.ptr,10222,e.what(),opt);
+    else if(e.code().category()==future_category()) nothrow.handler(nothrow.ptr,10223,e.what(),opt);
+    // fallback to generic runtime_error
+    else nothrow.handler(nothrow.ptr,10200,e.what(),nullptr);
+#endif
+  } catch(std::underflow_error &e) {
+    nothrow.handler(nothrow.ptr,10215,e.what(),nullptr);
+  } catch(std::overflow_error &e) {
+    nothrow.handler(nothrow.ptr,10210,e.what(),nullptr);
+  } catch(std::range_error &e) {
+    nothrow.handler(nothrow.ptr,10205,e.what(),nullptr);
+  } catch(std::runtime_error & e) {
+    nothrow.handler(nothrow.ptr,10200,e.what(),nullptr);
+    // not implemented yet: std::future_error
+    // not clear how useful it would be.
+  } catch(std::out_of_range & e) {
+    nothrow.handler(nothrow.ptr,10120,e.what(),nullptr);
+  } catch(std::length_error & e) {
+    nothrow.handler(nothrow.ptr,10115,e.what(),nullptr);
+  } catch(std::domain_error & e) {
+    nothrow.handler(nothrow.ptr,10110,e.what(),nullptr);
+  } catch(std::invalid_argument & e) {
+    nothrow.handler(nothrow.ptr,10105,e.what(),nullptr);
+  } catch(std::logic_error & e) {
+    nothrow.handler(nothrow.ptr,10100,e.what(),nullptr);
+    // generic exception. message will be copied without new allocations
+    // reports all non caught exceptions that are derived from std::exception
+    // for instance, boost exceptions would end up here
+  } catch(std::exception & e) {
+    nothrow.handler(nothrow.ptr,10000,e.what(),nullptr);
+  } catch(...) {
+    std::cerr<<"+++ rethrowing an unknown error happened while using a plumed cmd"<<std::endl;
+    throw;
+  }
 }
 
 extern "C" void plumed_plumedmain_finalize(void*plumed) {
diff --git a/src/wrapper/Plumed.h b/src/wrapper/Plumed.h
index a3c0cba3b..854315e4a 100644
--- a/src/wrapper/Plumed.h
+++ b/src/wrapper/Plumed.h
@@ -255,6 +255,40 @@
   When you compile the FORTRAN interface, wrapper functions are added with several possible
   name mangligs, so you should not experience problems linking the plumed library with a FORTRAN file.
 
+\paragraph ReferencePlumedH-exceptions Error handling
+
+  Bad things happen. In case an error is detected by PLUMED, either because of some user error, some internal bug,
+  or some mistake in using the library, an exception will be thrown. The behavior is different depending if you use
+  PLUMED from C/FORTRAN or from C++.
+
+  First of all, notice that access to PLUMED goes through three functions:
+  - plumed_create: this, as of PLUMED 2.5, is guaranteed not to throw any exception. If there is a problem, it will
+    just return a NULL pointer
+  - plumed_cmd: this function might throw exceptions.
+  - plumed_finalize: this is a destructor and is guaranteed not to throw any exception.
+
+  The following discussion concerns all the exceptions thrown by plumed_cmd.
+
+  If you use C/FORTRAN, you will basically have no way to intercept the exception and the program will just terminate.
+
+  If you use C++ but you are calling the C interface (e.g. \ref plumed_cmd), then you might be
+  able to catch the exceptions thrown by PLUMED. Notice that all the exceptions thrown by PLUMED inherit from std::exception,
+  so you might want to catch it by reference. Notice however that there is a C layer between your C++ code and the PLUMED
+  library. In principle, the stack unwinding performed during exception handling is undefined in C and might lead to problems
+  that are system and compiler dependent. In addition to this, there might be troubles when combining different compilers
+  or different standard libraries. E.g., if you MD code is linked against a given C++ library and PLUMED is linked against
+  another one, the two std::exception types will differ and you won't be able to catch exceptions raised by PLUMED.
+
+  If you use C++ and you are calling the C++ interface (e.g. \ref Plumed::cmd), as of PLUMED 2.5 we implemented a complete
+  remapping of the exceptions thrown by PLUMED. This solves both the problems mentioned above. In particular:
+  - Instead of throwing an exception, PLUMED will return (using a \ref plumed_nothrow_handler) the details about the occurred error.
+  - An equivalent exception will be thrown within the inline PLUMED interface compiled with your MD code.
+
+  As a consequence, you will be able to combine different compilers and avoid stack unwinding in the C layer.
+
+  The remapping of exceptions takes care of all the standard C++ exceptions plus all the exceptions raised within
+  PLUMED. Unexpected exceptions that are derived from std::exception will be rethrown as std::exception.
+
 \paragraph ReferencePlumedH-2-5 New in PLUMED 2.5
 
   The wrappers in PLUMED 2.5 have been completely rewritten with several improvements.
@@ -433,6 +467,24 @@
 #define __PLUMED_WRAPPER_CXX_DEFAULT_INVALID 0
 #endif
 
+/*
+  Size of a buffer used to store message for exceptions with noexcept constructor.
+  Should typically hold short messages. Anyway, as long as the stack size stays within the correct
+  limits it does not seem to affect efficiency. Notice that there cannot be recursive calls of
+  PLMD::Plumed::cmd, so that it should be in practice irrelevant.
+*/
+#ifndef __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER
+#define __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER 512
+#endif
+
+
+/*
+ By default, assume C++11 compliant library is not available.
+*/
+
+#ifndef __PLUMED_WRAPPER_LIBCXX11
+#define __PLUMED_WRAPPER_LIBCXX11 0
+#endif
 
 /* The following macros are just to define shortcuts */
 
@@ -929,17 +981,22 @@ __PLUMED_WRAPPER_EXTERN_C_END /*}*/
 
 #if defined( __cplusplus) && __PLUMED_WRAPPER_CXX /*{*/
 
-/* this is to include the NULL pointer */
 #if __PLUMED_WRAPPER_CXX_STD
-#include <cstdlib>
+#include <cstdlib> /* NULL getenv */
+#include <cstring> /* strncat strlen */
 #else
 #include <stdlib.h>
+#include <string.h>
 #endif
 
-/* these are to include standard exceptions */
-#include <exception>
-#include <stdexcept>
-#include <string>
+#include <exception> /* exception */
+#include <stdexcept> /* runtime_error logic_error invalid_argument domain_error length_error out_of_range range_error overflow_error underflow_error */
+#include <string> /* string */
+#include <ios> /* iostream_category (C++11) ios_base::failure (C++11 and C++<11) */
+#if __cplusplus > 199711L
+#include <system_error> /* generic_category system_category */
+#include <future> /* future_category */
+#endif
 
 /* C++ interface is hidden in PLMD namespace (same as plumed library) */
 namespace PLMD {
@@ -968,18 +1025,126 @@ class Plumed {
   */
 
   struct NothrowHandler {
+    /** code used for translating messages */
     int code;
+    /** short message buffer for non-throwing exceptions */
+    char exception_buffer[__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER];
+    /** if exception_buffer='\0', message stored as an allocatable string */
     ::std::string what;
+    /** error code for system_error */
+    int error_code;
   };
 
   /**
     Callback function that sets the error handler.
+
+    opt argument is interpreted as the pointer to a null terminated array of void*.
+    The number of non-null element is expected to be even, and there should be a null element
+    that follows. Every pair of pointers should point
+    to a char, identifying the type of argument passed, and an arbitrary object.
+    Currently used to (optionally) pass error_code.
   */
   static void nothrow_handler(void*ptr,int code,const char*what,const void* opt) {
     NothrowHandler* h=(NothrowHandler*) ptr;
     h->code=code;
-    h->what=what;
-    (void) opt; /* not used yet */
+    h->exception_buffer[0]='\0';
+    h->what.clear();
+    h->error_code=0;
+    /*
+       These codes correspond to exceptions that should not allocate a separate buffer but use the fixed one.
+       Notice that a mismatch between the exceptions using the stack buffer here and those implementing
+       the stack buffer would be in practice harmless. However, it makes sense to be consistent.
+    */
+    if(code==10000 || (code>=11000 && code<12000)) {
+      __PLUMED_WRAPPER_STD strncat(h->exception_buffer,what,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1);
+    } else {
+      h->what=what;
+    }
+
+    /* interpret optional arguments */
+    const void** options=(const void**)opt;
+    if(options) while(*options) {
+        if(*((char*)*options)=='c') h->error_code=*((int*)*(options+1));
+        options+=2;
+      }
+
+    static const char* debug=__PLUMED_WRAPPER_STD getenv("PLUMED_EXCEPTIONS_DEBUG");
+
+    if(debug) {
+      fprintf(stderr,"+++ PLUMED_EXCEPTIONS_DEBUG\n");
+      fprintf(stderr,"+++ code: %d error_code: %d message:\n%s\n",h->code,h->error_code,what);
+      if(__PLUMED_WRAPPER_STD strlen(what) > __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1) fprintf(stderr,"+++ WARNING: message will be truncated\n");
+      fprintf(stderr,"+++ END PLUMED_EXCEPTIONS_DEBUG\n");
+    }
+
+  }
+
+  /**
+    Rethrow the exception.
+  */
+
+  static void rethrow(const NothrowHandler&h) {
+    /* The interpretation of the codes should be kept in sync with core/PlumedMainInitializer.cpp */
+    /* check if we are using a full string or a fixes size buffer */
+    const char* msg=(h.exception_buffer[0]?h.exception_buffer:h.what.c_str());
+    if(h.code==1) throw Plumed::Invalid(msg);
+    /* logic errors */
+    if(h.code>=10100 && h.code<10200) {
+      if(h.code>=10105 && h.code<10110) throw ::std::invalid_argument(msg);
+      if(h.code>=10110 && h.code<10115) throw ::std::domain_error(msg);
+      if(h.code>=10115 && h.code<10120) throw ::std::length_error(msg);
+      if(h.code>=10120 && h.code<10125) throw ::std::out_of_range(msg);
+      throw ::std::logic_error(msg);
+    }
+    /* runtime errors */
+    if(h.code>=10200 && h.code<10300) {
+      if(h.code>=10205 && h.code<10210) throw ::std::range_error(msg);
+      if(h.code>=10210 && h.code<10215) throw ::std::overflow_error(msg);
+      if(h.code>=10215 && h.code<10220) throw ::std::underflow_error(msg);
+#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11
+      if(h.code==10220) throw ::std::system_error(h.error_code,::std::generic_category(),msg);
+      if(h.code==10221) throw ::std::system_error(h.error_code,::std::system_category(),msg);
+      if(h.code==10222) throw ::std::system_error(h.error_code,::std::iostream_category(),msg);
+      if(h.code==10223) throw ::std::system_error(h.error_code,::std::future_category(),msg);
+#endif
+      if(h.code>=10230 && h.code<10240) {
+#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11
+// These cases are probably useless as it looks like this should always be std::iostream_category
+        if(h.code==10230) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::generic_category()));
+        if(h.code==10231) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::system_category()));
+        if(h.code==10232) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::iostream_category()));
+        if(h.code==10233) throw ::std::ios_base::failure(msg,std::error_code(h.error_code,::std::future_category()));
+#endif
+        throw ::std::ios_base::failure(msg);
+      }
+      throw ::std::runtime_error(msg);
+    }
+    /* "bad" errors */
+    if(h.code>=11000 && h.code<11100) throw Plumed::std_bad_typeid(msg);
+    if(h.code>=11100 && h.code<11200) throw Plumed::std_bad_cast(msg);
+#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11
+    if(h.code>=11200 && h.code<11300) throw Plumed::std_bad_weak_ptr(msg);
+    if(h.code>=11300 && h.code<11400) throw Plumed::std_bad_function_call(msg);
+#endif
+    if(h.code>=11400 && h.code<11500) {
+#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11
+      if(h.code>=11410 && h.code<11420) throw Plumed::std_bad_array_new_length(msg);
+#endif
+      throw Plumed::std_bad_alloc(msg);
+    }
+    if(h.code>=11500 && h.code<11600) throw Plumed::std_bad_exception(msg);
+    /* lepton error */
+    if(h.code>=19900 && h.code<20000) throw Plumed::LeptonException(msg);
+    /* plumed exceptions */
+    if(h.code>=20000 && h.code<30000) {
+      /* debug - only raised with debug options */
+      if(h.code>=20100 && h.code<20200) throw Plumed::ExceptionDebug(msg);
+      /* error - runtime check */
+      if(h.code>=20200 && h.code<20300) throw Plumed::ExceptionError(msg);
+      throw Plumed::Exception(msg);
+    }
+    /* fallback for any other exception */
+    throw Plumed::std_exception(msg);
   }
 
 public:
@@ -1035,6 +1200,70 @@ public:
     ~Invalid() __PLUMED_WRAPPER_CXX_NOEXCEPT {}
   };
 
+  /**
+    Class used to rethrow Lepton exceptions.
+  */
+
+  class LeptonException :
+    public ::std::exception
+  {
+    ::std::string msg;
+  public:
+    LeptonException(const char* msg): msg(msg) {}
+    LeptonException(const LeptonException & other): msg(other.what()) {}
+    const char* what() const __PLUMED_WRAPPER_CXX_NOEXCEPT {return msg.c_str();}
+    ~LeptonException() __PLUMED_WRAPPER_CXX_NOEXCEPT {}
+  };
+
+private:
+  /*
+    These exceptions are declared as private as they are not supposed to be
+    catched by value. they only exist to allow a buffer to be attached to
+    the std::exceptions that do not contain it already.
+    Notice that these exceptions are those whose constructor should never throw, and as
+    such they use a fixed size buffer.
+  */
+
+#define __PLUMED_WRAPPER_NOSTRING_EXCEPTION(name) \
+  class std_ ## name : \
+    public ::std::name \
+  { \
+    char msg[__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER]; \
+  public: \
+    std_ ## name(const char * msg) __PLUMED_WRAPPER_CXX_NOEXCEPT { \
+      this->msg[0]='\0'; \
+      __PLUMED_WRAPPER_STD strncat(this->msg,msg,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1); \
+      static const char* debug=__PLUMED_WRAPPER_STD getenv("PLUMED_EXCEPTIONS_DEBUG"); \
+      if(debug && __PLUMED_WRAPPER_STD strlen(msg) > __PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1) fprintf(stderr,"+++ WARNING: message will be truncated\n"); \
+    } \
+    std_ ## name(const std_ ## name & other) __PLUMED_WRAPPER_CXX_NOEXCEPT { \
+      msg[0]='\0'; \
+      __PLUMED_WRAPPER_STD strncat(msg,other.msg,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1); \
+    } \
+    std_ ## name & operator=(const std_ ## name & other) __PLUMED_WRAPPER_CXX_NOEXCEPT { \
+      if(this==&other) return *this;\
+      msg[0]='\0'; \
+      __PLUMED_WRAPPER_STD strncat(msg,other.msg,__PLUMED_WRAPPER_CXX_EXCEPTION_BUFFER-1); \
+      return *this; \
+    } \
+    const char* what() const __PLUMED_WRAPPER_CXX_NOEXCEPT {return msg;} \
+    ~std_ ## name() __PLUMED_WRAPPER_CXX_NOEXCEPT {} \
+  };
+
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_typeid)
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_cast)
+#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_weak_ptr)
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_function_call)
+#endif
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_alloc)
+#if __cplusplus > 199711L && __PLUMED_WRAPPER_LIBCXX11
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_array_new_length)
+#endif
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(bad_exception)
+  __PLUMED_WRAPPER_NOSTRING_EXCEPTION(exception)
+
+public:
 
   /**
      Check if plumed is installed (for runtime binding)
@@ -1411,21 +1640,9 @@ Plumed(Plumed&&p)__PLUMED_WRAPPER_CXX_NOEXCEPT :
     h.code=0;
     plumed_nothrow_handler nothrow= {&h,nothrow_handler};
     plumed_cmd_nothrow(main,key,val,nothrow);
-    /* The interpretation of the codes should be kept in sync with core/PlumedMainInitializer.cpp */
-    if(h.code==0) return;
-    if(h.code==1) throw Plumed::Invalid(h.what.c_str());
-    if(h.code>=10000 && h.code<20000) {
-      if(h.code>=10100 && h.code<10200) throw ::std::logic_error(h.what.c_str());
-      if(h.code>=10200 && h.code<10300) throw ::std::runtime_error(h.what.c_str());
-    }
-    if(h.code>=20000 && h.code<30000) {
-      if(h.code>=20100 && h.code<20200) throw Plumed::ExceptionDebug(h.what.c_str());
-      if(h.code>=20200 && h.code<20300) throw Plumed::ExceptionError(h.what.c_str());
-      throw Plumed::Exception(h.what.c_str());
-    }
-    /* all other exception types are mapped to runtime_error */
-    throw ::std::runtime_error(h.what.c_str());
+    if(h.code!=0) rethrow(h);
   }
+
   /**
      Destructor
 
@@ -1600,15 +1817,15 @@ __PLUMED_WRAPPER_ANONYMOUS_END /*}*/
 #endif /*}*/
 
 #ifdef __PLUMED_HAS_DLOPEN
-#include <dlfcn.h>
+#include <dlfcn.h> /* dlopen dlerror dlsym */
 #endif
 
 #if __PLUMED_WRAPPER_CXX_STD
-#include <cstdio>
-#include <cstring>
-#include <cassert>
-#include <cstdlib>
-#include <climits>
+#include <cstdio>  /* fprintf */
+#include <cstring> /* memcpy strlen strncpy memcmp memmove strcmp memcpy */
+#include <cassert> /* assert */
+#include <cstdlib> /* getenv malloc free abort exit */
+#include <climits> /* CHAR_BIT */
 #else
 #include <stdio.h>
 #include <string.h>
-- 
GitLab