Fix copyright notices for new C++ code.
[alexxy/gromacs.git] / src / gromacs / utility / exceptions.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2011,2012, by the GROMACS development team, led by
5  * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6  * others, as listed in the AUTHORS file in the top-level source
7  * directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS 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 GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements classes and functions in exceptions.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_utility
41  */
42 #include "exceptions.h"
43
44 #include <cstring>
45
46 #include <new>
47 #include <stdexcept>
48 #include <typeinfo>
49
50 #include <boost/exception/get_error_info.hpp>
51 #include <boost/shared_ptr.hpp>
52
53 #include "gromacs/legacyheaders/thread_mpi/system_error.h"
54 #include "gromacs/utility/errorcodes.h"
55 #include "gromacs/utility/gmxassert.h"
56
57 #include "errorformat.h"
58
59 namespace gmx
60 {
61
62 namespace
63 {
64
65 /********************************************************************
66  * ErrorMessage
67  */
68
69 class ErrorMessage
70 {
71     public:
72         /*! \brief
73          * Creates an error message object with the specified text.
74          *
75          * \param[in] text  Text for the message.
76          */
77         explicit ErrorMessage(const std::string &text);
78
79         //! Whether this object is a context string.
80         bool isContext() const { return child_; }
81         //! Returns the text for this object.
82         const std::string &text() const { return text_; }
83         /*! \brief
84          * Returns the child object for a context object.
85          *
86          * Must not be called if isContext() returns false.
87          */
88         const ErrorMessage &child() const
89         {
90             GMX_ASSERT(isContext(),
91                        "Attempting to access nonexistent message object");
92             return *child_;
93         }
94
95         /*! \brief
96          * Creates a new message object with context prepended.
97          *
98          * \param[in] context  Context string to add.
99          * \returns   New error message object that has \p context as its text
100          *      and \c this as its child.
101          * \throws    std::bad_alloc if out of memory.
102          */
103         ErrorMessage prependContext(const std::string &context) const;
104
105     private:
106         std::string                     text_;
107         boost::shared_ptr<ErrorMessage> child_;
108 };
109
110 /*! \internal \brief
111  * Stores a reason or the top-most context string of an exception.
112  *
113  * \ingroup module_utility
114  */
115 typedef boost::error_info<struct errinfo_message_, ErrorMessage>
116     errinfo_message;
117
118 ErrorMessage::ErrorMessage(const std::string &text)
119     : text_(text)
120 {
121     size_t length = text_.find_last_not_of(" \n");
122     if (length == std::string::npos)
123     {
124         length = text_.length() - 1;
125     }
126     text_.resize(length + 1);
127 }
128
129 ErrorMessage
130 ErrorMessage::prependContext(const std::string &context) const
131 {
132     ErrorMessage newMessage(context);
133     newMessage.child_.reset(new ErrorMessage(*this));
134     return newMessage;
135 }
136
137 /*! \brief
138  * Stores list of nested exceptions for Gromacs exceptions.
139  *
140  * \ingroup module_utility
141  */
142 typedef boost::error_info<struct errinfo_message_, internal::NestedExceptionList>
143     errinfo_nested_exceptions;
144
145 }   // namespace
146
147 /********************************************************************
148  * GromacsException
149  */
150
151 GromacsException::GromacsException(const ExceptionInitializer &details)
152 {
153     *this << errinfo_message(ErrorMessage(details.reason_));
154     if (details.hasNestedExceptions())
155     {
156         *this << errinfo_nested_exceptions(details.nested_);
157     }
158 }
159
160 const char *GromacsException::what() const throw()
161 {
162     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
163     while (msg != NULL && msg->isContext())
164     {
165         msg = &msg->child();
166     }
167     return msg != NULL ? msg->text().c_str() : "No reason provided";
168 }
169
170 void GromacsException::prependContext(const std::string &context)
171 {
172     const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*this);
173     GMX_RELEASE_ASSERT(msg != NULL, "Message should always be set");
174     *this << errinfo_message(msg->prependContext(context));
175 }
176
177 /********************************************************************
178  * Derived exception classes
179  */
180
181 int FileIOError::errorCode() const
182 {
183     return eeFileIO;
184 }
185
186 int InvalidInputError::errorCode() const
187 {
188     return eeInvalidInput;
189 }
190
191 int InconsistentInputError::errorCode() const
192 {
193     return eeInconsistentInput;
194 }
195
196 int SimulationInstabilityError::errorCode() const
197 {
198     return eeInstability;
199 }
200
201 int InternalError::errorCode() const
202 {
203     return eeInternalError;
204 }
205
206 int APIError::errorCode() const
207 {
208     return eeAPIError;
209 }
210
211 int NotImplementedError::errorCode() const
212 {
213     return eeNotImplemented;
214 }
215
216
217 /********************************************************************
218  * Global functions
219  */
220
221 namespace
222 {
223
224 /*! \brief
225  * Prints error information for an exception object.
226  *
227  * \param[in] fp      File to write the information out to (typically stderr).
228  * \param[in] ex      Exception object to print.
229  * \param[in] indent  Indentation for the information.
230  *
231  * If the exception contains nested exceptions, information from them is
232  * recursively printed.
233  */
234 void printExceptionMessage(FILE *fp, const std::exception &ex, int indent)
235 {
236     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
237     if (boostEx != NULL)
238     {
239         // TODO: Remove duplicate context if present in multiple nested exceptions.
240         const ErrorMessage *msg = boost::get_error_info<errinfo_message>(*boostEx);
241         if (msg != NULL)
242         {
243             while (msg != NULL && msg->isContext())
244             {
245                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
246                 ++indent;
247                 msg = &msg->child();
248             }
249             if (msg != NULL && !msg->text().empty())
250             {
251                 internal::printFatalErrorMessageLine(fp, msg->text().c_str(), indent*2);
252             }
253         }
254         else
255         {
256             internal::printFatalErrorMessageLine(fp, ex.what(), indent);
257         }
258
259         const int *errorNumber
260             = boost::get_error_info<boost::errinfo_errno>(*boostEx);
261         if (errorNumber != NULL)
262         {
263             std::fprintf(fp, "%*sReason: %s\n", (indent+1)*2, "",
264                          std::strerror(*errorNumber));
265             const char * const *funcName
266                 = boost::get_error_info<boost::errinfo_api_function>(*boostEx);
267             if (funcName != NULL)
268             {
269                 std::fprintf(fp, "%*s(call to %s() returned error code %d)\n",
270                              (indent+1)*2, "", *funcName, *errorNumber);
271             }
272         }
273
274         // TODO: Treat also boost::nested_exception (not currently used, though)
275
276         const internal::NestedExceptionList *nested
277             = boost::get_error_info<errinfo_nested_exceptions>(*boostEx);
278         if (nested != NULL)
279         {
280             internal::NestedExceptionList::const_iterator ni;
281             for (ni = nested->begin(); ni != nested->end(); ++ni)
282             {
283                 try
284                 {
285                     rethrow_exception(*ni);
286                 }
287                 catch (const std::exception &nestedEx)
288                 {
289                     printExceptionMessage(fp, nestedEx, indent + 1);
290                 }
291             }
292         }
293     }
294     else
295     {
296         internal::printFatalErrorMessageLine(fp, ex.what(), indent);
297     }
298 }
299
300 }   // namespace
301
302 void printFatalErrorMessage(FILE *fp, const std::exception &ex)
303 {
304     const char             *title      = "Unknown exception";
305     bool                    bPrintType = false;
306     const GromacsException *gmxEx      = dynamic_cast<const GromacsException *>(&ex);
307     // TODO: Treat more of the standard exceptions
308     if (gmxEx != NULL)
309     {
310         title = getErrorCodeString(gmxEx->errorCode());
311     }
312     else if (dynamic_cast<const tMPI::system_error *>(&ex) != NULL)
313     {
314         title = "System error in thread synchronization";
315     }
316     else if (dynamic_cast<const std::bad_alloc *>(&ex) != NULL)
317     {
318         title = "Memory allocation failed";
319     }
320     else if (dynamic_cast<const std::logic_error *>(&ex) != NULL)
321     {
322         title      = "Standard library logic error (bug)";
323         bPrintType = true;
324     }
325     else if (dynamic_cast<const std::runtime_error *>(&ex) != NULL)
326     {
327         title      = "Standard library runtime error (possible bug)";
328         bPrintType = true;
329     }
330     else
331     {
332         bPrintType = true;
333     }
334     // We can't call get_error_info directly on ex since our internal boost
335     // needs to be compiled with BOOST_NO_RTTI. So we do the dynamic_cast
336     // here instead.
337     const char *const      *funcPtr = NULL;
338     const char *const      *filePtr = NULL;
339     const int              *linePtr = NULL;
340     const boost::exception *boostEx = dynamic_cast<const boost::exception *>(&ex);
341     if (boostEx != NULL)
342     {
343         funcPtr = boost::get_error_info<boost::throw_function>(*boostEx);
344         filePtr = boost::get_error_info<boost::throw_file>(*boostEx);
345         linePtr = boost::get_error_info<boost::throw_line>(*boostEx);
346     }
347     internal::printFatalErrorHeader(fp, title,
348                                     funcPtr != NULL ? *funcPtr : NULL,
349                                     filePtr != NULL ? *filePtr : NULL,
350                                     linePtr != NULL ? *linePtr : 0);
351     if (bPrintType)
352     {
353         std::fprintf(fp, "(exception type: %s)\n", typeid(ex).name());
354     }
355     printExceptionMessage(fp, ex, 0);
356     internal::printFatalErrorFooter(fp);
357 }
358
359 } // namespace gmx