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