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