Imported Upstream version 17.14.0
[platform/upstream/libzypp.git] / zypp / base / Exception.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/base/Exception.h
10  *
11 */
12 #ifndef ZYPP_BASE_EXCEPTION_H
13 #define ZYPP_BASE_EXCEPTION_H
14
15 #include <iosfwd>
16 #include <string>
17 #include <list>
18 #include <stdexcept>
19 #include <typeinfo>
20 #include <type_traits>
21
22 #include "zypp/base/Errno.h"
23
24 ///////////////////////////////////////////////////////////////////
25 namespace zypp
26 { /////////////////////////////////////////////////////////////////
27   ///////////////////////////////////////////////////////////////////
28   namespace exception_detail
29   { /////////////////////////////////////////////////////////////////
30
31     /** Keep _FILE_, _FUNCTION_ and _LINE_.
32      * Construct it using the \ref ZYPP_EX_CODELOCATION macro.
33     */
34     struct CodeLocation
35     {
36       friend std::ostream & operator<<( std::ostream & str, const CodeLocation & obj );
37
38       /** Ctor */
39       CodeLocation()
40       : _line( 0 )
41       {}
42
43       /** Ctor */
44       CodeLocation( const std::string & file_r,
45                     const std::string & func_r,
46                     unsigned            line_r )
47       : _file( file_r ), _func( func_r ), _line( line_r )
48       {}
49
50       /** Location as string */
51       std::string asString() const;
52
53     private:
54       std::string _file;
55       std::string _func;
56       unsigned    _line;
57     };
58     ///////////////////////////////////////////////////////////////////
59
60     /** Create CodeLocation object storing the current location. */
61     //#define ZYPP_EX_CODELOCATION ::zypp::exception_detail::CodeLocation(__FILE__,__FUNCTION__,__LINE__)
62 #define ZYPP_EX_CODELOCATION ::zypp::exception_detail::CodeLocation(( *__FILE__ == '/' ? strrchr( __FILE__, '/' ) + 1 : __FILE__ ),__FUNCTION__,__LINE__)
63
64     /** \relates CodeLocation Stream output */
65     std::ostream & operator<<( std::ostream & str, const CodeLocation & obj );
66
67     /////////////////////////////////////////////////////////////////
68   } // namespace exception_detail
69   ///////////////////////////////////////////////////////////////////
70
71   ///////////////////////////////////////////////////////////////////
72   //
73   //    CLASS NAME : Exception
74   /** Base class for Exception.
75    *
76    * Exception offers to store a message string passed to the ctor.
77    * Derived classes may provide additional information. Overload
78    * \ref dumpOn to provide a proper error text.
79    *
80    * \li Use \ref ZYPP_THROW to throw exceptions.
81    * \li Use \ref ZYPP_CAUGHT If you caught an exceptions in order to handle it.
82    * \li Use \ref ZYPP_RETHROW to rethrow a caught exception.
83    *
84    * The use of these macros is not mandatory. but \c ZYPP_THROW and
85    * \c ZYPP_RETHROW will adjust the code location information stored in
86    * the Exception. All three macros will drop a line in the logfile.
87
88    * \code
89    *  43   try
90    *  44     {
91    *  45       try
92    *  46         {
93    *  47           ZYPP_THROW( Exception("Something bad happened.") );
94    *  48         }
95    *  49       catch ( Exception & excpt )
96    *  50         {
97    *  51           ZYPP_RETHROW( excpt );
98    *  52         }
99    *  53
100    *  54     }
101    *  55   catch ( Exception & excpt )
102    *  56     {
103    *  57       ZYPP_CAUGHT( excpt );
104    *  58     }
105    * \endcode
106    * The above produces the following log lines:
107    * \code
108    *  Main.cc(main):47 THROW:    Main.cc(main):47: Something bad happened.
109    *  Main.cc(main):51 RETHROW:  Main.cc(main):47: Something bad happened.
110    *  Main.cc(main):57 CAUGHT:   Main.cc(main):51: Something bad happened.
111    * \endcode
112    *
113    *
114    * Class Exception now offers a history list of message strings.
115    * These messages should describe what lead to the exception.
116    *
117    * The Exceptions message itself is NOT included in the history.
118    *
119    * Rethrow, remembering an old exception:
120    * \code
121    * try
122    * {
123    *   ....
124    * }
125    * catch( const Exception & olderr_r )
126    * {
127    *    ZYPP_CAUGHT( olderr_r )
128    *    HighLevelException newerr( "Something failed." );
129    *    newerr.rember( olderr_r );
130    *    ZYPP_THROW( newerr );
131    * }
132    * \endcode
133    *
134    * Print an Exception followed by it's history if available:
135    * \code
136    * Exception error;
137    * ERR << error << endl << error.historyAsString();
138    * \endcode
139    *
140    * \todo That's a draft to have a common way of throwing exceptions.
141    * Most probabely we'll finally use blocxx exceptions. Here, but not
142    * in the remaining code of zypp. If we can we should try to wrap
143    * the blocxx macros and typedef the classes in here.
144    **/
145   class Exception : public std::exception
146   {
147     friend std::ostream & operator<<( std::ostream & str, const Exception & obj );
148
149   public:
150     typedef exception_detail::CodeLocation CodeLocation;
151     typedef std::list<std::string>         History;
152     typedef History::const_iterator        HistoryIterator;
153     typedef History::size_type             HistorySize;
154
155     /** Default ctor.
156      * Use \ref ZYPP_THROW to throw exceptions.
157     */
158     Exception();
159
160     /** Ctor taking a message.
161      * Use \ref ZYPP_THROW to throw exceptions.
162     */
163     Exception( const std::string & msg_r );
164     /** \overload */
165     Exception( std::string && msg_r );
166
167     /** Ctor taking a message and an exception to remember as history
168      * \see \ref remember
169      * Use \ref ZYPP_THROW to throw exceptions.
170     */
171     Exception( const std::string & msg_r, const Exception & history_r );
172     /** \overload moving */
173     Exception( std::string && msg_r, const Exception & history_r );
174     /** \overload moving */
175     Exception( const std::string & msg_r, Exception && history_r );
176     /** \overload moving */
177     Exception( std::string && msg_r, Exception && history_r );
178
179     /** Dtor. */
180     virtual ~Exception() throw();
181
182     /** Return CodeLocation. */
183     const CodeLocation & where() const
184     { return _where; }
185
186     /** Exchange location on rethrow. */
187     void relocate( const CodeLocation & where_r ) const
188     { _where = where_r; }
189
190     /** Return the message string provided to the ctor.
191      * \note This is not necessarily the complete error message.
192      * The whole error message is provided by \ref asString or
193      * \ref dumpOn.
194     */
195     const std::string & msg() const
196     { return _msg; }
197
198     /** Error message provided by \ref dumpOn as string. */
199     std::string asString() const;
200
201     /** Translated error message as string suitable for the user.
202      * \see \ref asUserStringHistory
203     */
204     std::string asUserString() const;
205
206   public:
207     /** \name History list of message strings.
208      * Maintain a simple list of individual error messages, that lead
209      * to this Exception. The Exceptions message itself is not included
210      * in the history. The History list stores the most recent message
211      * fist.
212      */
213     //@{
214
215     /** Store an other Exception as history. */
216     void remember( const Exception & old_r );
217     /** \overload moving */
218     void remember( Exception && old_r );
219
220     /** Add some message text to the history. */
221     void addHistory( const std::string & msg_r );
222     /** \overload moving */
223     void addHistory( std::string && msg_r );
224
225     /** \ref addHistory from string container types (oldest first) */
226     template<class TContainer>
227     void addToHistory( const TContainer & msgc_r )
228     {
229       for ( const std::string & el : msgc_r )
230         addHistory( el );
231     }
232     /** \ref addHistory from string container types (oldest first) moving */
233     template<class TContainer>
234     void moveToHistory( TContainer && msgc_r )
235     {
236       for ( std::string & el : msgc_r )
237         addHistory( std::move(el) );
238     }
239
240     /** Iterator pointing to the most recent message. */
241     HistoryIterator historyBegin() const
242     { return _history.begin(); }
243
244     /** Iterator pointing behind the last message. */
245     HistoryIterator historyEnd() const
246     { return _history.end(); }
247
248     /** Whether the history list is empty. */
249     bool historyEmpty() const
250     { return _history.empty(); }
251
252     /** The size of the history list. */
253     HistorySize historySize() const
254     { return _history.size(); }
255
256     /** The history as string. Empty if \ref historyEmpty.
257      * Otherwise:
258      * \code
259      * History:
260      *  - most recent message
261      *  - 2nd message
262      * ...
263      *  - oldest message
264      * \endcode
265     */
266     std::string historyAsString() const;
267
268     /** A single (multiline) string composed of \ref asUserString  and  \ref historyAsString. */
269     std::string asUserHistory() const;
270     //@}
271
272   protected:
273
274     /** Overload this to print a proper error message. */
275     virtual std::ostream & dumpOn( std::ostream & str ) const;
276
277   public:
278      /** Make a string from \a errno_r. */
279     static std::string strErrno( int errno_r );
280      /** Make a string from \a errno_r and \a msg_r. */
281     static std::string strErrno( int errno_r, const std::string & msg_r );
282     /** \overload moving */
283     static std::string strErrno( int errno_r, std::string && msg_r );
284
285   public:
286     /** Drop a logline on throw, catch or rethrow.
287      * Used by \ref ZYPP_THROW macros.
288     */
289     static void log( const Exception & excpt_r, const CodeLocation & where_r,
290                      const char *const prefix_r );
291     /** \overrload for not-Exception types thrown via ZYPP_THROW */
292     static void log( const char * typename_r, const CodeLocation & where_r,
293                      const char *const prefix_r );
294   private:
295     mutable CodeLocation _where;
296     std::string          _msg;
297     History              _history;
298
299     /** Return message string. */
300     virtual const char * what() const throw()
301     { return _msg.c_str(); }
302
303     /** Called by <tt>std::ostream & operator\<\<</tt>.
304      * Prints \ref CodeLocation and the error message provided by
305      * \ref dumpOn.
306     */
307     std::ostream & dumpError( std::ostream & str ) const;
308   };
309   ///////////////////////////////////////////////////////////////////
310
311   /** \relates Exception Stream output */
312   std::ostream & operator<<( std::ostream & str, const Exception & obj );
313
314   ///////////////////////////////////////////////////////////////////
315   namespace exception_detail
316   {
317     /** SFINAE: Hide template signature unless \a TExcpt is derived from \ref Exception. */
318     template<class TExcpt>
319     using EnableIfIsException = typename std::enable_if< std::is_base_of<Exception,TExcpt>::value, int>::type;
320
321     /** SFINAE: Hide template signature if \a TExcpt is derived from  \ref Exception. */
322     template<class TExcpt>
323     using EnableIfNotException = typename std::enable_if< !std::is_base_of<Exception,TExcpt>::value, int>::type;
324
325
326     /** Helper for \ref ZYPP_THROW( Exception ). */
327     template<class TExcpt, EnableIfIsException<TExcpt> = 0>
328     void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
329     template<class TExcpt, EnableIfIsException<TExcpt>>
330     void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r )
331     {
332       excpt_r.relocate( where_r );
333       Exception::log( excpt_r, where_r, "THROW:   " );
334       throw( excpt_r );
335     }
336
337     /** Helper for \ref ZYPP_THROW( not Exception ). */
338     template<class TExcpt, EnableIfNotException<TExcpt> = 0>
339     void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
340     template<class TExcpt, EnableIfNotException<TExcpt>>
341     void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r )
342     {
343       Exception::log( typeid(excpt_r).name(), where_r, "THROW:   " );
344       throw( excpt_r );
345     }
346
347
348     /** Helper for \ref ZYPP_THROW( Exception ). */
349     template<class TExcpt, EnableIfIsException<TExcpt> = 0>
350     void do_ZYPP_CAUGHT( const TExcpt & excpt_r, const CodeLocation & where_r )
351     {
352       Exception::log( excpt_r, where_r, "CAUGHT:  " );
353     }
354
355     /** Helper for \ref ZYPP_THROW( not Exception ). */
356     template<class TExcpt, EnableIfNotException<TExcpt> = 0>
357     void do_ZYPP_CAUGHT( const TExcpt & excpt_r, const CodeLocation & where_r )
358     {
359       Exception::log( typeid(excpt_r).name(), where_r, "CAUGHT:  " );
360     }
361
362
363     /** Helper for \ref ZYPP_THROW( Exception ). */
364     template<class TExcpt, EnableIfIsException<TExcpt> = 0>
365     void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
366     template<class TExcpt, EnableIfIsException<TExcpt>>
367     void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r )
368     {
369       Exception::log( excpt_r, where_r, "RETHROW: " );
370       excpt_r.relocate( where_r );
371       throw;
372     }
373
374     /** Helper for \ref ZYPP_THROW( not Exception ). */
375     template<class TExcpt, EnableIfNotException<TExcpt> = 0>
376     void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
377     template<class TExcpt, EnableIfNotException<TExcpt>>
378     void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r )
379     {
380       Exception::log( excpt_r, where_r, "RETHROW: " );
381       throw;
382     }
383   } // namespace exception_detail
384   ///////////////////////////////////////////////////////////////////
385
386   /** \defgroup ZYPP_THROW ZYPP_THROW macros
387    * Macros for throwing Exception.
388    * \see \ref zypp::Exception for an example.
389   */
390   //@{
391   /** Drops a logline and throws the Exception. */
392 #define ZYPP_THROW(EXCPT)\
393   ::zypp::exception_detail::do_ZYPP_THROW( EXCPT, ZYPP_EX_CODELOCATION )
394
395   /** Drops a logline telling the Exception was caught (in order to handle it). */
396 #define ZYPP_CAUGHT(EXCPT)\
397   ::zypp::exception_detail::do_ZYPP_CAUGHT( EXCPT, ZYPP_EX_CODELOCATION )
398
399   /** Drops a logline and rethrows, updating the CodeLocation. */
400 #define ZYPP_RETHROW(EXCPT)\
401   ::zypp::exception_detail::do_ZYPP_RETHROW( EXCPT, ZYPP_EX_CODELOCATION )
402
403
404   /** Throw Exception built from a message string. */
405 #define ZYPP_THROW_MSG(EXCPTTYPE, MSG)\
406   ZYPP_THROW( EXCPTTYPE( MSG ) )
407
408   /** Throw Exception built from errno. */
409 #define ZYPP_THROW_ERRNO(EXCPTTYPE)\
410   ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(errno) ) )
411
412   /** Throw Exception built from errno provided as argument. */
413 #define ZYPP_THROW_ERRNO1(EXCPTTYPE, ERRNO)\
414   ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(ERRNO) ) )
415
416   /** Throw Exception built from errno and a message string. */
417 #define ZYPP_THROW_ERRNO_MSG(EXCPTTYPE, MSG)\
418   ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(errno,MSG) ) )
419
420   /** Throw Exception built from errno provided as argument and a message string */
421 #define ZYPP_THROW_ERRNO_MSG1(EXCPTTYPE, ERRNO,MSG)\
422   ZYPP_THROW( EXCPTTYPE( ::zypp::Exception::strErrno(ERRNO,MSG) ) )
423   //@}
424
425   /////////////////////////////////////////////////////////////////
426 } // namespace zypp
427 ///////////////////////////////////////////////////////////////////
428 #endif // ZYPP_BASE_EXCEPTION_H