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