blocxx
Exception.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of
14 * Vintela, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
39 #include "blocxx/BLOCXX_config.h"
40 #include "blocxx/Exception.hpp"
41 #include "blocxx/StackTrace.hpp"
42 #include "blocxx/Format.hpp"
43 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
44 #include "blocxx/Mutex.hpp"
45 #endif
46 #include <string.h>
47 // Not <cstring>, because strerror_r is not part of C or C++ standard lib,
48 // but is a POSIX function defined to be in <string.h>.
49 #include <cstdlib>
50 #if defined(BLOCXX_HAVE_ISTREAM) && defined(BLOCXX_HAVE_OSTREAM)
51 #include <istream>
52 #include <ostream>
53 #else
54 #include <iostream>
55 #endif
56 #include <algorithm> // for std::swap
57 
58 namespace BLOCXX_NAMESPACE
59 {
60 
61 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
62 Mutex* Exception::m_mutex = new Mutex();
63 #endif
64 
65 static void freeBuf(char** ptr)
66 {
67  delete [] *ptr;
68  *ptr = NULL;
69 }
71 char* Exception::dupString(const char* str)
72 {
73  if (!str)
74  {
75  return 0;
76  }
77  char* rv = new (std::nothrow) char[strlen(str)+1];
78  if (!rv)
79  {
80  return 0;
81  }
82  strcpy(rv, str);
83  return rv;
84 }
86 Exception::Exception(const char* file, int line, const char* msg)
87  : std::exception()
88  , m_file(dupString(file))
89  , m_line(line)
90  , m_msg(dupString(msg))
91  , m_subClassId(UNKNOWN_SUBCLASS_ID)
92  , m_subException(0)
93  , m_errorCode(UNKNOWN_ERROR_CODE)
94 {
95 #ifdef BLOCXX_ENABLE_STACK_TRACE_ON_EXCEPTIONS
97 #endif
98 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
99  m_mutex->acquire();
100 #endif
101 }
103 Exception::Exception(int subClassId, const char* file, int line, const char* msg, int errorCode, const Exception* subException)
104  : std::exception()
105  , m_file(dupString(file))
106  , m_line(line)
107  , m_msg(dupString(msg))
108  , m_subClassId(subClassId)
109  , m_subException(subException ? subException->clone() : 0)
110  , m_errorCode(errorCode)
111 {
112 #ifdef BLOCXX_ENABLE_STACK_TRACE_ON_EXCEPTIONS
114 #endif
115 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
116  m_mutex->acquire();
117 #endif
118 }
120 Exception::Exception(const char* file, int line, const char* msg, int errorCode, const Exception* subException, int subClassId)
121  : std::exception()
122  , m_file(dupString(file))
123  , m_line(line)
124  , m_msg(dupString(msg))
125  , m_subClassId(subClassId)
126  , m_subException(subException ? subException->clone() : 0)
127  , m_errorCode(errorCode)
128 {
129 #ifdef BLOCXX_ENABLE_STACK_TRACE_ON_EXCEPTIONS
131 #endif
132 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
133  m_mutex->acquire();
134 #endif
135 }
138  : std::exception(e)
139  , m_file(dupString(e.m_file))
140  , m_line(e.m_line)
141  , m_msg(dupString(e.m_msg))
142  , m_subClassId(e.m_subClassId)
143  , m_subException(e.m_subException ? e.m_subException->clone() : 0)
144  , m_errorCode(e.m_errorCode)
145 {
146 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
147  m_mutex->acquire();
148 #endif
149 }
152 {
153  try
154  {
155  delete m_subException;
156  freeBuf(&m_file);
157  freeBuf(&m_msg);
158 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
159  m_mutex->release();
160 #endif
161  }
162  catch (...)
163  {
164  // don't let exceptions escape
165  }
166 }
168 Exception&
170 {
171  Exception(rhs).swap(*this);
172  return *this;
173 }
175 void
177 {
178  std::swap(static_cast<std::exception&>(*this), static_cast<std::exception&>(rhs));
179  std::swap(m_file, rhs.m_file);
180  std::swap(m_line, rhs.m_line);
181  std::swap(m_msg, rhs.m_msg);
185 }
186 
188 const char*
190 {
191  return "Exception";
192 }
193 
195 int
197 {
198  return m_line;
199 }
200 
202 const char*
204 {
205  return (m_msg != NULL) ? m_msg : "";
206 }
208 const char*
210 {
211  return (m_file != NULL) ? m_file : "";
212 }
214 std::ostream&
215 operator<<(std::ostream& os, const Exception& e)
216 {
217  if (*e.getFile() == '\0')
218  {
219  os << "[no file]: ";
220  }
221  else
222  {
223  os << e.getFile() << ": ";
224  }
225 
226  if (e.getLine() == 0)
227  {
228  os << "[no line] ";
229  }
230  else
231  {
232  os << e.getLine() << ' ';
233  }
234 
235  os << e.type() << ": ";
236 
238  {
239  os << e.getErrorCode() << ": ";
240  }
241 
242  if (*e.getMessage() == '\0')
243  {
244  os << "[no message]";
245  }
246  else
247  {
248  os << e.getMessage();
249  }
250 
251  const Exception* subEx = e.getSubException();
252  if (subEx)
253  {
254  os << " <" << *subEx << '>';
255  }
256  return os;
257 }
259 const char*
260 Exception::what() const throw()
261 {
262  return getMessage();
263 }
264 
266 int
267 Exception::getSubClassId() const
268 {
269  return m_subClassId;
270 }
271 
273 void
274 Exception::setSubClassId(int subClassId)
275 {
276  m_subClassId = subClassId;
277 }
278 
280 Exception*
281 Exception::clone() const
282 {
283  return new(std::nothrow) Exception(*this);
284 }
285 
287 void Exception::rethrow() const
288 {
289  throw *this;
290 }
291 
293 const Exception*
294 Exception::getSubException() const
295 {
296  return m_subException;
297 }
298 
300 int
301 Exception::getErrorCode() const
302 {
303  return m_errorCode;
304 }
305 
307 void
308 Exception::setErrorCode(int errorCode)
309 {
310  m_errorCode = errorCode;
311 }
312 
313 namespace ExceptionDetail
314 {
315 
316 // HPUX, solaris have a thread safe strerror(), windows doesn't have strerror_r(), and doesn't document whether strerror() is thread safe or not.
317 #if defined(BLOCXX_HPUX) || defined(BLOCXX_SOLARIS) || defined(BLOCXX_WIN32) || defined(BLOCXX_NCR)
318 
319  void portable_strerror_r(int errnum, char * buf, unsigned n)
320  {
321  ::strncpy(buf, strerror(errnum), n);
322  buf[n-1] = '\0'; // just in case...
323  }
324 
325 #else
326  typedef int (*posix_fct)(int, char *, ::std::size_t);
327  typedef char * (*gnu_fct)(int, char *, ::std::size_t);
328  typedef int (*aix_fct)(int, char *, int);
329 
330  struct dummy
331  {
332  };
333 
334  // We make the strerror_r_wrap functions into templates so that
335  // code is generated only for the one that gets used.
336 
337  template <typename Dummy>
338  inline int
339  strerror_r_wrap(posix_fct strerror_r, int errnum, char * buf, unsigned n,
340  Dummy)
341  {
342  return strerror_r(errnum, buf, n);
343  }
344 
345  template <typename Dummy>
346  inline int
347  strerror_r_wrap(aix_fct strerror_r, int errnum, char * buf, unsigned n,
348  Dummy)
349  {
350  return strerror_r(errnum, buf, n);
351  }
352 
353  template <typename Dummy>
354  inline int
355  strerror_r_wrap(gnu_fct strerror_r, int errnum, char * buf, unsigned n,
356  Dummy)
357  {
358  char * errstr = strerror_r(errnum, buf, n);
359  if (errstr != buf)
360  {
361  if (errstr)
362  {
363  ::strncpy(buf, errstr, n);
364  }
365  else
366  {
367  return -1;
368  }
369  }
370  return 0;
371  }
372 
373  void portable_strerror_r(int errnum, char * buf, unsigned n)
374  {
375  int errc = strerror_r_wrap(&::strerror_r, errnum, buf, n, dummy());
376  if (errc != 0)
377  {
378  ::strncpy(buf, "[Could not create error message for error code]", n);
379  }
380  buf[n-1] = '\0'; // just in case...
381  }
382 #endif
383 
384  struct BLOCXX_COMMON_API FormatMsgImpl
385  {
386  String fm;
387  };
388 
389  FormatMsg::FormatMsg(char const * msg, int errnum)
390  : pImpl(new FormatMsgImpl)
391  {
392  char arr[BUFSZ];
393  portable_strerror_r(errnum, arr, BUFSZ);
394  char const * sarr = static_cast<char const *>(arr);
395  pImpl->fm = Format("%1: %2(%3)", msg, errnum, sarr).toString();
396  }
397 
398  FormatMsg::~FormatMsg()
399  {
400  }
401 
402  char const * FormatMsg::get() const
403  {
404  return pImpl->fm.c_str();
405  }
406 
407 } // namespace ExceptionDetail
408 
409 } // end namespace BLOCXX_NAMESPACE
410