blocxx

Exception.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Vintela, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00039 #include "blocxx/BLOCXX_config.h"
00040 #include "blocxx/Exception.hpp"
00041 #include "blocxx/StackTrace.hpp"
00042 #include "blocxx/Format.hpp"
00043 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
00044 #include "blocxx/Mutex.hpp"
00045 #endif
00046 #include <string.h>
00047 // Not <cstring>, because strerror_r is not part of C or C++ standard lib,
00048 // but is a POSIX function defined to be in <string.h>.
00049 #include <cstdlib>
00050 #if defined(BLOCXX_HAVE_ISTREAM) && defined(BLOCXX_HAVE_OSTREAM)
00051 #include <istream>
00052 #include <ostream>
00053 #else
00054 #include <iostream>
00055 #endif
00056 #include <algorithm> // for std::swap
00057 
00058 namespace BLOCXX_NAMESPACE
00059 {
00060 
00061 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
00062 Mutex* Exception::m_mutex = new Mutex();
00063 #endif
00064 
00065 static void freeBuf(char** ptr)
00066 {
00067    delete [] *ptr;
00068    *ptr = NULL;
00069 }
00071 char* Exception::dupString(const char* str)
00072 {
00073    if (!str)
00074    {
00075       return 0;
00076    }
00077    char* rv = new (std::nothrow) char[strlen(str)+1];
00078    if (!rv)
00079    {
00080       return 0;
00081    }
00082    strcpy(rv, str);
00083    return rv;
00084 }
00086 Exception::Exception(const char* file, int line, const char* msg)
00087    : std::exception()
00088    , m_file(dupString(file))
00089    , m_line(line)
00090    , m_msg(dupString(msg))
00091    , m_subClassId(UNKNOWN_SUBCLASS_ID)
00092    , m_subException(0)
00093    , m_errorCode(UNKNOWN_ERROR_CODE)
00094 {
00095 #ifdef BLOCXX_ENABLE_STACK_TRACE_ON_EXCEPTIONS
00096    StackTrace::printStackTrace();
00097 #endif
00098 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
00099    m_mutex->acquire();
00100 #endif
00101 }
00103 Exception::Exception(int subClassId, const char* file, int line, const char* msg, int errorCode, const Exception* subException)
00104    : std::exception()
00105    , m_file(dupString(file))
00106    , m_line(line)
00107    , m_msg(dupString(msg))
00108    , m_subClassId(subClassId)
00109    , m_subException(subException ? subException->clone() : 0)
00110    , m_errorCode(errorCode)
00111 {
00112 #ifdef BLOCXX_ENABLE_STACK_TRACE_ON_EXCEPTIONS
00113    StackTrace::printStackTrace();
00114 #endif
00115 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
00116    m_mutex->acquire();
00117 #endif
00118 }
00120 Exception::Exception(const char* file, int line, const char* msg, int errorCode, const Exception* subException, int subClassId)
00121    : std::exception()
00122    , m_file(dupString(file))
00123    , m_line(line)
00124    , m_msg(dupString(msg))
00125    , m_subClassId(subClassId)
00126    , m_subException(subException ? subException->clone() : 0)
00127    , m_errorCode(errorCode)
00128 {
00129 #ifdef BLOCXX_ENABLE_STACK_TRACE_ON_EXCEPTIONS
00130    StackTrace::printStackTrace();
00131 #endif
00132 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
00133    m_mutex->acquire();
00134 #endif
00135 }
00137 Exception::Exception( const Exception& e )
00138     : std::exception(e)
00139     , m_file(dupString(e.m_file))
00140     , m_line(e.m_line)
00141     , m_msg(dupString(e.m_msg))
00142    , m_subClassId(e.m_subClassId)
00143     , m_subException(e.m_subException ? e.m_subException->clone() : 0)
00144    , m_errorCode(e.m_errorCode)
00145 {
00146 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
00147     m_mutex->acquire();
00148 #endif
00149 }
00151 Exception::~Exception() throw()
00152 {
00153    try
00154    {
00155       delete m_subException;
00156       freeBuf(&m_file);
00157       freeBuf(&m_msg);
00158 #if defined(BLOCXX_NON_THREAD_SAFE_EXCEPTION_HANDLING)
00159       m_mutex->release();
00160 #endif
00161    }
00162    catch (...)
00163    {
00164       // don't let exceptions escape
00165    }
00166 }
00168 Exception&
00169 Exception::operator=(const Exception& rhs)
00170 {
00171     Exception(rhs).swap(*this);
00172     return *this;
00173 }
00175 void
00176 Exception::swap(Exception& rhs)
00177 {
00178    std::swap(static_cast<std::exception&>(*this), static_cast<std::exception&>(rhs));
00179    std::swap(m_file, rhs.m_file);
00180    std::swap(m_line, rhs.m_line);
00181    std::swap(m_msg, rhs.m_msg);
00182    std::swap(m_subClassId, rhs.m_subClassId);
00183    std::swap(m_subException, rhs.m_subException);
00184    std::swap(m_errorCode, rhs.m_errorCode);
00185 }
00186       
00188 const char*
00189 Exception::type() const
00190 {
00191    return "Exception";
00192 }
00193 
00195 int
00196 Exception::getLine() const
00197 {
00198    return m_line;
00199 }
00200 
00202 const char*
00203 Exception::getMessage() const
00204 {
00205    return (m_msg != NULL) ? m_msg : "";
00206 }
00208 const char*
00209 Exception::getFile() const
00210 {
00211    return (m_file != NULL) ? m_file : "";
00212 }
00214 std::ostream&
00215 operator<<(std::ostream& os, const Exception& e)
00216 {
00217    if (*e.getFile() == '\0')
00218    {
00219       os << "[no file]: ";
00220    }
00221    else
00222    {
00223       os << e.getFile() << ": ";
00224    }
00225    
00226    if (e.getLine() == 0)
00227    {
00228       os << "[no line] ";
00229    }
00230    else
00231    {
00232       os << e.getLine() << ' ';
00233    }
00234    
00235    os << e.type() << ": ";
00236 
00237    if (e.getErrorCode() != Exception::UNKNOWN_ERROR_CODE)
00238    {
00239       os << e.getErrorCode() << ": ";
00240    }
00241    
00242    if (*e.getMessage() == '\0')
00243    {
00244       os << "[no message]";
00245    }
00246    else
00247    {
00248       os << e.getMessage();
00249    }
00250 
00251    const Exception* subEx = e.getSubException();
00252    if (subEx)
00253    {
00254       os << " <" << *subEx << '>';
00255    }
00256    return os;
00257 }
00259 const char*
00260 Exception::what() const throw()
00261 {
00262    return getMessage();
00263 }
00264 
00266 int
00267 Exception::getSubClassId() const
00268 {
00269    return m_subClassId;
00270 }
00271 
00273 void
00274 Exception::setSubClassId(int subClassId)
00275 {
00276    m_subClassId = subClassId;
00277 }
00278 
00280 Exception*
00281 Exception::clone() const
00282 {
00283    return new(std::nothrow) Exception(*this);
00284 }
00285 
00287 void Exception::rethrow() const
00288 {
00289    throw *this;
00290 }
00291 
00293 const Exception*
00294 Exception::getSubException() const
00295 {
00296    return m_subException;
00297 }
00298 
00300 int
00301 Exception::getErrorCode() const
00302 {
00303    return m_errorCode;
00304 }
00305 
00307 void
00308 Exception::setErrorCode(int errorCode)
00309 {
00310    m_errorCode = errorCode;
00311 }
00312 
00313 namespace ExceptionDetail
00314 {
00315 
00316 // HPUX, solaris have a thread safe strerror(), windows doesn't have strerror_r(), and doesn't document whether strerror() is thread safe or not.
00317 #if defined(BLOCXX_HPUX) || defined(BLOCXX_SOLARIS) || defined(BLOCXX_WIN32) || defined(BLOCXX_NCR)
00318 
00319    void portable_strerror_r(int errnum, char * buf, unsigned n)
00320    {
00321       ::strncpy(buf, strerror(errnum), n);
00322       buf[n-1] = '\0'; // just in case...
00323    }
00324 
00325 #else
00326    typedef int (*posix_fct)(int, char *, ::std::size_t);
00327    typedef char * (*gnu_fct)(int, char *, ::std::size_t);
00328    typedef int (*aix_fct)(int, char *, int);
00329 
00330    struct dummy
00331    {
00332    };
00333 
00334    // We make the strerror_r_wrap functions into templates so that
00335    // code is generated only for the one that gets used.
00336 
00337    template <typename Dummy>
00338    inline int
00339    strerror_r_wrap(posix_fct strerror_r, int errnum, char * buf, unsigned n,
00340                    Dummy)
00341    {
00342       return strerror_r(errnum, buf, n);
00343    }
00344 
00345    template <typename Dummy>
00346    inline int
00347    strerror_r_wrap(aix_fct strerror_r, int errnum, char * buf, unsigned n,
00348                    Dummy)
00349    {
00350       return strerror_r(errnum, buf, n);
00351    }
00352 
00353    template <typename Dummy>
00354    inline int
00355    strerror_r_wrap(gnu_fct strerror_r, int errnum, char * buf, unsigned n,
00356                    Dummy)
00357    {
00358       char * errstr = strerror_r(errnum, buf, n);
00359       if (errstr != buf)
00360       {
00361          if (errstr)
00362          {
00363             ::strncpy(buf, errstr, n);
00364          }
00365          else
00366          {
00367             return -1;
00368          }
00369       }
00370       return 0;
00371    }
00372 
00373    void portable_strerror_r(int errnum, char * buf, unsigned n)
00374    {
00375       int errc = strerror_r_wrap(&::strerror_r, errnum, buf, n, dummy());
00376       if (errc != 0)
00377       {
00378          ::strncpy(buf, "[Could not create error message for error code]", n);
00379       }
00380       buf[n-1] = '\0'; // just in case...
00381    }
00382 #endif
00383 
00384    struct BLOCXX_COMMON_API FormatMsgImpl
00385    {
00386       String fm;
00387    };
00388 
00389    FormatMsg::FormatMsg(char const * msg, int errnum)
00390       : pImpl(new FormatMsgImpl)
00391    {
00392       char arr[BUFSZ];
00393       portable_strerror_r(errnum, arr, BUFSZ);
00394       char const * sarr = static_cast<char const *>(arr);
00395       pImpl->fm = Format("%1: %2(%3)", msg, errnum, sarr).toString();
00396    }
00397 
00398    FormatMsg::~FormatMsg()
00399    {
00400    }
00401 
00402    char const * FormatMsg::get() const
00403    {
00404       return pImpl->fm.c_str();
00405    }
00406 
00407 } // namespace ExceptionDetail
00408 
00409 } // end namespace BLOCXX_NAMESPACE
00410