1 // -*- C++ -*-
2 // Exception.h: Exceptions used by Asap
3 //
4 // Copyright (C) 2001-2011 Jakob Schiotz and Center for Individual
5 // Nanoparticle Functionality, Department of Physics, Technical
6 // University of Denmark.  Email: schiotz@fysik.dtu.dk
7 //
8 // This file is part of Asap version 3.
9 // Asap is released under the GNU Lesser Public License (LGPL) version 3.
10 // However, the parts of Asap distributed within the OpenKIM project
11 // (including this file) are also released under the Common Development
12 // and Distribution License (CDDL) version 1.0.
13 //
14 // This program is free software: you can redistribute it and/or
15 // modify it under the terms of the GNU Lesser General Public License
16 // version 3 as published by the Free Software Foundation.  Permission
17 // to use other versions of the GNU Lesser General Public License may
18 // granted by Jakob Schiotz or the head of department of the
19 // Department of Physics, Technical University of Denmark, as
20 // described in section 14 of the GNU General Public License.
21 //
22 // This program is distributed in the hope that it will be useful,
23 // but WITHOUT ANY WARRANTY; without even the implied warranty of
24 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 // GNU General Public License for more details.
26 //
27 // You should have received a copy of the GNU General Public License
28 // and the GNU Lesser Public License along with this program.  If not,
29 // see <http://www.gnu.org/licenses/>.
30 
31 
32 // DESIGN NOTE:
33 // ============
34 //
35 // Error conditions in Asap are usually handled by throwing an AsapError
36 // exception.  This exception is caught by the Python Interface methods,
37 // and converted to a Python exception.  If errors are generated by Python
38 // in Python calls from Asap, the Python error condition is left in place,
39 // and the C++ code throws an AsapPythonError.  This error is also caught
40 // by the Python Interface methods, but in this case the preexisting Python
41 // error condition is left unchanged, and the error condition is reported
42 // back to Python.  This results in slightly strange tracebacks, as the
43 // path through the C++ code will be missing.  A subclass of AsapPythonError
44 // is AsapNotImplementedError, which is thrown without a corresponding
45 // Python error, and converted into Python's NotImplementedError.  Finally,
46 // assert statements can be changed to emit an AssertionFailed error that is
47 // also turned into a Python AssertionFailed error.  This latter behaviour
48 // is normally disabled, and is totally incompatible with OpenMP.
49 //
50 // OpenMP note:  OpenMP place so severe restrictions on throwing exceptions
51 // inside parallel sections that exceptions become almost useless, even if
52 // parallelism is disabled at runtime.  Asap handles this by replacing the
53 // throw statement with a THROW() macro inside OpenMP enabled regions.  If
54 // compiled without OpenMP support, this macro just expands to the throw
55 // statement.  If OpenMP is enabled, it instead expands to a helper function
56 // that just stores the exception in a global variable.  Subsequent function
57 // calls are then supposed to return immediately (thanks to the
58 // RETURNIFASAPERROR macro in the beginning of the functions) until control
59 // leaves the parallel section, and the exception is thrown by the
60 // PROPAGATEASAPERROR macro.  These latter macros are empty if OpenMP is
61 // not enabled at compile time.
62 
63 
64 #ifndef _EXCEPTION_H
65 #define _EXCEPTION_H
66 
67 #include "Asap.h"
68 #include <sstream>
69 #include <iostream>
70 #ifndef ASAPASSERT
71 #include <assert.h>
72 #include <exception>
73 #endif // ASAPASSERT
74 
75 using std::stringstream;
76 using std::string;
77 
78 #ifndef _NOEXCEPT
79 #define _NOEXCEPT throw()
80 #endif
81 
82 namespace ASAPSPACE {
83 
84 // this AsapError class can be used as follows:
85 //
86 // throw AsapError("Error");
87 //
88 // throw AsapError("Error ") << n + 7 << " while opening file: " << filename;
89 
90 class ASAP_PUBLIC AsapErrorBase : std::exception
91 {
92 public:
~AsapErrorBase()93   virtual ~AsapErrorBase() _NOEXCEPT {};
94 };
95 
96 class ASAP_PUBLIC AsapError : public AsapErrorBase
97 {
98 public:
99   AsapError(const char *m);
100   AsapError(const AsapError& ex);
~AsapError()101   virtual ~AsapError() _NOEXCEPT {};
102   template<class T> AsapError& operator<<(const T& x)
103     {
104       message << x;
105       return *this;
106     }
107   string GetMessage() const;
108 private:
109   stringstream message;
110 };
111 
112 /// An exception for indicating that a Python error has occurred and
113 /// should be propagated.
114 class ASAP_PUBLIC AsapPythonError : public AsapErrorBase
115 {
116 public:
AsapPythonError()117   AsapPythonError() {};
~AsapPythonError()118   virtual ~AsapPythonError() _NOEXCEPT {};
119 };
120 
121 #ifdef ASAP_FOR_KIM
122 // This definition is used when an ASAP potential is exported to OpenKIM,
123 // where there is no Python.
124 class ASAP_PUBLIC AsapNotImplementedError : public AsapError
125 {
126 public:
AsapNotImplementedError(const char * m)127   AsapNotImplementedError(const char *m) : AsapError(m) {};
128 };
129 #else // ASAP_FOR_KIM
130 // The NORMAL version is here !
131 /// An exception mapping to a Python NotImplementedError
132 class ASAP_PUBLIC AsapNotImplementedError : public AsapPythonError
133 {
134 public:
135   AsapNotImplementedError(const char *m);
136 };
137 #endif //ASAP_FOR_KIM
138 
139 class ASAP_PUBLIC AssertionFailed : public AsapErrorBase
140 {
141 public:
142   AssertionFailed(const char *expression, const char *file, int line,
143 		  const char *func = NULL);
144   AssertionFailed(const AssertionFailed& ex);
~AssertionFailed()145   ~AssertionFailed() _NOEXCEPT {};
146   string GetMessage() const;
147 private:
148   stringstream message;
149 };
150 
151 /// A helper define
152 #define NOTHREADSERROR throw AsapError("Threads not supported. ") << __FILE__ << ":" << __LINE__
153 
154 /// Defining our own assert macro that throws an exception
155 #ifdef ASAPASSERT
156 #ifdef __GNUC__
157 #define ASSERT(EX) if (!(EX)) \
158     throw AssertionFailed(#EX, __FILE__, __LINE__, __PRETTY_FUNCTION__)
159 #else // __GNUC__
160 #define ASSERT(EX) if (!(EX)) \
161     throw AssertionFailed(#EX, __FILE__, __LINE__, __func__)
162 #endif // __GNUC__
163 #else // ASAPASSERT
164 #define ASSERT(EX) assert(EX)
165 #endif // ASAPASSERT
166 
167 #ifdef SLOWASSERT
168 // We need the semicolon below, although it looks wrong, to allow SASSERT to vanish altogether.
169 #define SASSERT(x) ASSERT(x);
170 #else  // SLOWASSERT
171 #define SASSERT(x)
172 #endif // SLOWASSERT
173 
174 
175 // Define macros and helper functions for throwing exceptions within OpenMP constructs.
176 #ifdef _OPENMP
177 
178 extern AsapErrorBase *AsapGlobalException;
179 void AsapThrowHelper(const AsapError &err);
180 void AsapThrowHelper(const AsapPythonError &err);
181 
182 #define THROW(x) AsapThrowHelper(x)
183 #define THROW_RETURN(x) {AsapThrowHelper(x); return;}
184 #define CHECKNOASAPERROR ASSERT(AsapGlobalException == NULL)
185 #define RETURNIFASAPERROR if (AsapGlobalException != NULL) return
186 #define RETURNIFASAPERROR2(x) if (AsapGlobalException != NULL) return (x)
187 #define PROPAGATEASAPERROR if (AsapGlobalException != NULL) { \
188     AsapError *err = dynamic_cast<AsapError*>(AsapGlobalException); \
189     AsapPythonError *perr = dynamic_cast<AsapPythonError*>(AsapGlobalException); \
190     AsapGlobalException = NULL; \
191     std::cerr << "Propagating ASAP error [" << __FILE__ << ":" << __LINE__ << "] ..." << std::endl; \
192     if (err != NULL) throw *err; \
193     if (perr != NULL) throw *perr; \
194     throw AsapError("Error throwing exception."); }
195 
196 #else // _OPENMP
197 
198 #define THROW(x) throw x
199 #define THROW_RETURN(x) throw x
200 #define CHECKNOASAPERROR
201 #define RETURNIFASAPERROR
202 #define RETURNIFASAPERROR2(x)
203 #define PROPAGATEASAPERROR
204 
205 #endif // _OPENMP
206 
207 } // end namespace
208 
209 #endif // _EXCEPTION_H
210