c3020032e85cbc1ee34e450d8cb42628895ede32
[senf.git] / senf / Utils / Exception.hh
1 // $Id$
2 //
3 // Copyright (C) 2006
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Exception public header */
25
26 #ifndef HH_SENF_Utils_Exception_
27 #define HH_SENF_Utils_Exception_ 1
28
29 // Custom includes
30 #include <exception>
31 #include <string>
32 #include <iostream>
33 #include <sstream>
34 #include <boost/preprocessor/repeat.hpp>
35 #include <boost/preprocessor/cat.hpp>
36 #include <boost/utility.hpp>
37 #include <boost/type_traits/is_convertible.hpp>
38
39 //#include "Exception.mpp"
40 //-/////////////////////////////////////////////////////////////////////////////////////////////////
41
42 /** \defgroup exception Exception classes
43
44     All exceptions in %senf are derived from senf::Exception. This class adds the possibility to
45     extend the exception description while it is processed. The functionality is provided by a mixin
46     class senf::ExceptionMixin:
47
48     \code
49     try {
50
51         // Some code which might raise an arbitrary senf exception
52
53     }
54     catch (senf::ExceptionMixin & e) {
55         e << "\handling user " << user;
56         throw;
57     }
58     \endcode
59
60     This will add the user information to any %senf exception thrown. The Exception is however not a
61     stream. If you need to do more extensive formating, either use an intermediate string-stream or
62     use <a href="http://www.boost.org/doc/libs/release/libs/format/index.html">Boost.Format</a>:
63
64     \code
65     try {
66         // ...
67     }
68     catch (senf::ExceptionMixin & e) {
69         e << boost::format("\n" "call id 0x%04x@%s") % id % address;
70     }
71     \endcode
72
73     senf::SystemException is thrown for all operating system errors (failures which result in the
74     operating system setting the errno value). It is also derived from senf::Exception and can
75     therefore be extended as well.
76
77     Defining your own exception classes derived from senf::Exception is very simple:
78
79     \code
80     struct FooException : public senf::Exception
81     { FooException() : senf::Exception("Foo hit the fan") {} };
82     \endcode
83
84     If SENF is compiled in debug mode (SENF_DEBUG is defined), the exception message will
85     automatically include a stack backtrace. For this to work, you need to add the
86     <tt>-rdynamic</tt> option to all link commands. This feature depends on <tt>gcc</tt> and
87     the GNU-libc.
88
89     To apply these features (extensibility, backtrace) to a non-senf exception, the non-senf
90     exception can be wrapped and re-thrown.
91     \code
92     void foo() {
93         try {
94             // ... code that might throw std::bad_cast or somelib::FooException
95         }
96         SENF_WRAP_EXC(std::bad_cast)
97         SENF_WRAP_EXC(somelib::FooException)
98     }
99     \endcode
100     The re-thrown exception can then be caught as <tt>std::bad_cast</tt> or as senf::ExceptionMixin
101     as needed. It is safe, to wrap an exception twice (the macro will detect this case).
102     \code
103     bar() {
104     try {
105         try {
106             foo();
107         }
108         catch (senf::ExceptionMixin & ex) {
109             ex << "\n" "add this info";
110         }
111     }
112     catch (std::bad_cast const & ex) {
113         std::cerr << ex.what() << std::endl;
114     }
115     \endcode
116     The final error output will include
117     \li a backtrace if compiled in debug mode
118     \li the original error message from the <tt>std::bad_cast</tt> exception
119     \li the additional error message "add this info"
120
121     \todo Link against libcwd to add file-name/line-number information to the backtrace and remove
122         the dependency on -rdynamic
123     \todo Or better, use addr2line to obtain that information when showing the backtrace when
124         catched within Daemon (<tt>addr2line -fsiCe argv[0]</tt>)
125     \todo Add signal handlers for the bad signals which writes a backtrace to stderr and
126         terminates. This should probably write out a raw backtrace without de-mangling or
127         line-numbers since we don't want to mess with dynamic memory when the heap might be
128         corrupted ... Another handler for e.g. SIGUSR2 is nice to show a debug backtrace on demand
129  */
130
131 namespace senf {
132
133     /** \brief Generic extensible exception mixin
134
135         ExceptionMixin is a generic exception mixin which allows the exception to be later extended
136         by catching and re-throwing it (See example in \ref exception).
137
138         \ingroup exception
139       */
140     class ExceptionMixin
141     {
142     public:
143         std::string message() const; ///< get exception description
144         std::string backtrace() const; ///< Return backtrace (if available)
145
146         void append(std::string text);  ///< Extend exception description
147                                         /**< Adds \a text to the description text. */
148
149     protected:
150         explicit ExceptionMixin(std::string const & description = "");
151                                         ///< Initialize exception with string
152                                         /**< \a description is the initial error description
153                                              string. This should probably be a string constant
154                                              describing the exception for most derived
155                                              exceptions. */
156
157         std::string what_;
158     private:
159 #ifdef SENF_BACKTRACE
160         void addBacktrace();
161 #endif
162 #ifdef SENF_DEBUG
163         std::string::size_type excLen_;
164 #endif
165     };
166
167     /** \brief Extensible exception base-class
168
169         This base-class is an exception which already includes the ExceptionMixin. All SENF
170         exceptions are derived from this class. Other user-exception may be defined by deriving from
171         this class too.
172
173         \see \ref exception
174
175         \ingroup exception
176      */
177     class Exception
178         : public ExceptionMixin, public std::exception
179     {
180     public:
181         virtual ~Exception() throw();
182
183         virtual char const * what() const throw();
184                                         ///< get exception description and backtrace if available
185                                         /**< get description of the exception (message()) and backtrace
186                                              information if SENF is compiled with \c SENF_DEBUG */
187
188     protected:
189         explicit Exception(std::string const & description = "");
190     };
191
192     /** \brief Wrapper for standard non-senf exceptions
193
194         This class wraps an exception of type \a BaseException and adds functionality from
195         senf::ExceptionMixin.
196
197         \ingroup exception
198      */
199     template <class BaseException>
200     class WrapException
201         : public ExceptionMixin, public BaseException
202     {
203     public:
204         typedef BaseException Base;
205
206         WrapException(BaseException const & base);
207         virtual ~WrapException() throw();
208
209         virtual char const * what() const throw();
210     };
211
212     /** \brief Wrap a non-senf exception
213
214         This macro allows to wrap a non-senf exception adding functionality from ExceptionMixin
215         using the WrapException template. For an example, see \ref exception.
216
217         \ingroup exception
218      */
219 #   define SENF_WRAP_EXC(Ex)                                                                      \
220         catch (Ex const & base) {                                                                 \
221             if (dynamic_cast<senf::ExceptionMixin const *>(&base))                                \
222                 throw;                                                                            \
223             else                                                                                  \
224                 throw senf::WrapException<Ex>(base);                                              \
225         }
226
227     template <class Exc, class Arg>
228     typename boost::enable_if< boost::is_convertible<Exc*,ExceptionMixin*>, Exc & >::type
229     operator<<(Exc const & exc, Arg const & arg); ///< Extend exception description
230                                         /**< Adds \a arg converted to string to the end of the
231                                              exception description string. This operator allows to
232                                              use Exception instances like streams. The conversion is
233                                              performed using <code>boost::lexical_cast</code> and is
234                                              therefor identical to a streaming operation.
235                                              \see \ref exception */
236
237
238 #   ifdef SENF_DEBUG
239 #       define _SENF_EXC_DEBUG_ARGS ,char const * file=0,int line=0
240 #       define _SENF_EXC_DEBUG_ARGS_ND ,char const *file,int line
241 #       define _SENF_EXC_DEBUG_ARGS_P ,file,line
242 #   else
243 #       define _SENF_EXC_DEBUG_ARGS
244 #       define _SENF_EXC_DEBUG_ARGS_ND
245 #       define _SENF_EXC_DEBUG_ARGS_P
246 #   endif
247
248     /** \brief Exception handling standard UNIX errors (errno)
249
250         This exception is thrown to signal generic \c errno failures. Normally the \c errno value is
251         automatically taken from the \c errno variable but it may also be specified explicitly:
252
253         \code
254         // Standard usage: Take \c errno from environment
255         throw senf::SystemException("::open()")
256             << " while opening configuration file: " << filename;
257
258         // You may however explicitly specify the errno value
259         throw senf::SystemException("::open()", ENOFILE)
260
261         // Or leave the location information empty
262         throw senf::SystemException(ENOFILE);
263         throw senf::SystemException();
264         \endcode
265
266         From within SENF (<em>and only there because it depends on the \c SENF_DEBUG symbol</em>),
267         SystemException should be thrown using wrapper macros which add additional information to
268         the exception description:
269         \code
270         // Standard usage: Take \c errno from environment
271         SENF_THROW_SYSTEM_EXCEPTION("::open()")
272             << " while opening configuration file: " << filename;
273
274         // You may however explicitly specify the errno value
275         throw senf::SystemException("::open()", ENOFILE SENF_EXC_DEBUGINFO)
276         \endcode
277
278         \ingroup exception
279      */
280     class SystemException : public Exception
281     {
282     public:
283         //-////////////////////////////////////////////////////////////////////////
284         ///\name Structors and default members
285         //\{
286
287         explicit SystemException(std::string const & descr = "" _SENF_EXC_DEBUG_ARGS);
288         explicit SystemException(int code _SENF_EXC_DEBUG_ARGS);
289         SystemException(std::string const & descr, int code _SENF_EXC_DEBUG_ARGS);
290
291         virtual ~SystemException() throw();
292
293         //\}
294         //-////////////////////////////////////////////////////////////////////////
295
296         int errorNumber() const;        ///< Error code (\c errno number)
297         char const * errorString() const; ///< Error string (\c strerror() value)
298
299         bool anyOf(int c0, int c1=0, int c2=0, int c3=0, int c4=0, int c5=0,
300                    int c6=0, int c7=0, int c8=0, int c9=0) const;
301                                         ///< \c true, if errorNumber() is one of \a c0 ... \a c9
302
303     private:
304         void init(std::string const & descr, int code _SENF_EXC_DEBUG_ARGS_ND);
305
306         int code_;
307     };
308
309 #   ifdef SENF_DEBUG
310 #       define SENF_EXC_DEBUGINFO ,__FILE__,__LINE__
311 #   else
312 #       define SENF_EXC_DEBUGINFO
313 #   endif
314
315 #   define SENF_THROW_SYSTEM_EXCEPTION(desc) throw senf::SystemException(desc SENF_EXC_DEBUGINFO)
316
317 }
318
319 //-/////////////////////////////////////////////////////////////////////////////////////////////////
320 #include "Exception.cci"
321 //#include "Exception.ct"
322 #include "Exception.cti"
323 #endif
324
325 \f
326 // Local Variables:
327 // mode: c++
328 // fill-column: 100
329 // c-file-style: "senf"
330 // indent-tabs-mode: nil
331 // ispell-local-dictionary: "american"
332 // compile-command: "scons -u test"
333 // comment-column: 40
334 // End:
335