From 770531e2645a2626a2da30e2b62e88be83cc1c3f Mon Sep 17 00:00:00 2001
From: Giovanni Bussi <giovanni.bussi@gmail.com>
Date: Tue, 3 Jan 2012 16:50:01 +0100
Subject: [PATCH] Added PlumedException class to deal with internal plumed
 errors

It is now possible to throw an Exception which is based
on standard library exception of c++.
I also added a few macros: plumed_error, plumed_merror, plumed_assert and plumed_massert.
See the documentation
---
 configurations/mac.gcc  |   2 +-
 src/PlumedException.cpp |  48 ++++++++++++++++++
 src/PlumedException.h   | 107 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 156 insertions(+), 1 deletion(-)
 create mode 100644 src/PlumedException.cpp
 create mode 100644 src/PlumedException.h

diff --git a/configurations/mac.gcc b/configurations/mac.gcc
index 74175578a..56492a0d4 100644
--- a/configurations/mac.gcc
+++ b/configurations/mac.gcc
@@ -10,7 +10,7 @@ WARNINGS=-pedantic -Wall -Wextra -Wfloat-equal -Wwrite-strings -Wpointer-arith -
          -Wredundant-decls -Wvariadic-macros # -Werror
 
 CPPFLAGS=-D__PLUMED_HAS_DLOPEN $(CHECK_BOUNDARIES) -I. $(PLUMED_INCLUDE) \
-         -I/sw/include -I/sw/include/openmpi -D__PLUMED_HAS_MATHEVAL
+         -I/sw/include -I/sw/include/openmpi -D__PLUMED_HAS_MATHEVAL  -D__PLUMED_EXCEPTIONS
 CXXFLAGS=-O -fPIC $(WARNINGS) -Wstrict-null-sentinel -Wold-style-cast
 CFLAGS=-O -fPIC $(WARNINGS) \
        -Wdeclaration-after-statement -Wbad-function-cast -Wstrict-prototypes \
diff --git a/src/PlumedException.cpp b/src/PlumedException.cpp
new file mode 100644
index 000000000..bbbd0537a
--- /dev/null
+++ b/src/PlumedException.cpp
@@ -0,0 +1,48 @@
+#include "PlumedException.h"
+#include <cstdio>
+#include <cstdlib>
+
+using namespace std;
+using namespace PLMD;
+
+std::string PlumedException::format(const std::string&msg,const std::string&file,unsigned line,const std::string&function){
+  std::string message;
+  message="\n+++ Internal PLUMED error";
+  if(file.length()>0){
+    char cline[1000];
+    sprintf(cline,"%u",line);
+    message += "\n+++ file "+file+", line "+cline;
+    if(function.length()>0) message +=", function "+function;
+  }
+  if(msg.length()>0) message +="\n+++ message: "+msg;
+  return message;
+}
+
+
+PlumedException::PlumedException()
+{
+  this->msg=format("","",0,"");
+  abortIfExceptionsAreDisabled();
+}
+
+PlumedException::PlumedException(const std::string&msg)
+{
+  this->msg=format(msg,"",0,"");
+  abortIfExceptionsAreDisabled();
+}
+
+PlumedException::PlumedException(const std::string&msg,const std::string&file,unsigned line,const std::string&function)
+{
+  this->msg=format(msg,file,line,function);
+  abortIfExceptionsAreDisabled();
+}
+
+void PlumedException::abortIfExceptionsAreDisabled(){
+#if ! defined(__PLUMED_EXCEPTIONS)
+  fprintf(stderr,"%s",what());
+  fprintf(stderr,"\n");
+  std::abort();
+#endif
+}
+
+
diff --git a/src/PlumedException.h b/src/PlumedException.h
new file mode 100644
index 000000000..42968bbc7
--- /dev/null
+++ b/src/PlumedException.h
@@ -0,0 +1,107 @@
+#ifndef __PLUMED_PlumedException_h
+#define __PLUMED_PlumedException_h
+
+#include <string>
+#include <stdexcept>
+
+namespace PLMD{
+
+/**
+Class to deal with Plumed runtime errors.
+
+This class, or better the related macros, can be used to detect programming
+errors. Typical cases are internal inconsistencies or errors in the plumed<->MD
+interface. Mistakes made by final users (i.e. in the plumed.dat file)
+should be documented in some better way (e.g. printing parts of the manual in the output).
+
+To throw an error, just throw a c++ exception
+\verbatim
+  if(something_bad) throw PlumedException();
+\endverbatim
+or better add an error message to that
+\verbatim
+  if(something_bad) throw PlumedException("describe the error here);
+\endverbatim
+
+Even better, you can use the predefined macros 
+plumed_error(), plumed_assert(), plumed_merror() and plumed_massert(),
+which add information about the exact location of the error in the file (filename, line
+and, for g++, function name). Macros ending in "error" unconditionally throw
+the exception, whereas macros ending in "assert" first perform a conditional check
+(similarly to standard assert()). The extra "m" in the name means that an
+extensive error message can be added.
+\verbatim
+// examples:
+  plumed_assert(a>0);
+  plumed_massert(a>0,"a should be larger than zero");
+  if(a<=0) plumed_error();
+  if(a<=0) plumed_merror("a should be larger than zero");
+\endverbatim
+
+By default, execution is terminated imediately and a message is printed on stderr.
+
+If PLUMED is compiled with -D__PLUMED_EXCEPTIONS execution will continue
+and the exception will be passed to c++, so that it will be possible
+to intercepted it at a higher level, even outside plumed.
+E.g., in an external c++ code using PLUMED as a library, one can type
+\verbatim
+  try{
+    plumed.cmd("setPrecision",n);
+  } catch (std::exception & e) {
+    printf("ee %s",e.what());
+    exit(1);
+  }
+\endverbatim
+This can be useful if an external code wants to exit in a controlled manner
+(e.g. flushing files, printing the error message in a specific file, etc.)
+but is anyway limited to c++ codes. Moreover,
+since these errors are expected to be unrecoverable, the MD code will
+usually not be able to do something more clever than exiting.
+
+\note
+In the future we might decide to extend the usage of exceptions to
+detect recoverable errors (e.g. optional arguments not found, etc),
+even if I am not fully convinced that this is a good idea.
+Notice that sometime people claim that code compiled with exception enabled
+is slower (GB)
+*/
+class PlumedException : public std::exception
+{
+  std::string msg;
+/// Common tool, invoked by all the constructor to build the message string
+  static std::string format(const std::string&,const std::string&,unsigned,const std::string&);
+/// Method which aborts in case exceptions are disabled
+  void abortIfExceptionsAreDisabled();
+public:
+/// Without message
+  PlumedException();
+/// With message
+  PlumedException(const std::string&);
+/// With message plus file, line and function (meant to be used through a preprocessor macro)
+  PlumedException(const std::string&,const std::string&,unsigned,const std::string&);
+/// Returns the error message
+  virtual const char* what() const throw(){return msg.c_str();}
+/// Destructor should be defined and should not throw other exceptions
+  virtual ~PlumedException() throw(){};
+};
+
+// With GNU compiler, we can use __PRETTY_FUNCTION__ to get the function name
+#if !defined(__GNUG__)
+#define __PRETTY_FUNCTION__ ""
+#endif
+
+/// \relates PLMD::PlumedException
+/// Just print file/line/function information and exit
+#define plumed_error() throw PLMD::PlumedException("",__FILE__,__LINE__,__PRETTY_FUNCTION__)
+/// \relates PLMD::PlumedException
+/// Print file/line/function information plus msg and exit
+#define plumed_merror(msg) throw PLMD::PlumedException(msg,__FILE__,__LINE__,__PRETTY_FUNCTION__)
+/// \relates PLMD::PlumedException
+/// Conditionally print file/line/function information and exit
+#define plumed_assert(test) if(!(test)) throw PLMD::PlumedException("assertion failed " #test,__FILE__,__LINE__,__PRETTY_FUNCTION__)
+/// \relates PLMD::PlumedException
+/// Conditionally print file/line/function information plus msg and exit
+#define plumed_massert(test,msg) if(!(test)) throw PLMD::PlumedException("assertion failed " #test ", " msg,__FILE__,__LINE__,__PRETTY_FUNCTION__)
+
+}
+#endif
-- 
GitLab