Imported Upstream version 17.14.0
[platform/upstream/libzypp.git] / zypp / base / Exception.h
index e4c822c..42c4d80 100644 (file)
 #ifndef ZYPP_BASE_EXCEPTION_H
 #define ZYPP_BASE_EXCEPTION_H
 
-#include <cerrno>
 #include <iosfwd>
+#include <string>
+#include <list>
 #include <stdexcept>
+#include <typeinfo>
+#include <type_traits>
+
+#include "zypp/base/Errno.h"
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
@@ -53,7 +58,8 @@ namespace zypp
     ///////////////////////////////////////////////////////////////////
 
     /** Create CodeLocation object storing the current location. */
-    #define ZYPP_EX_CODELOCATION ::zypp::exception_detail::CodeLocation(__FILE__,__FUNCTION__,__LINE__)
+    //#define ZYPP_EX_CODELOCATION ::zypp::exception_detail::CodeLocation(__FILE__,__FUNCTION__,__LINE__)
+#define ZYPP_EX_CODELOCATION ::zypp::exception_detail::CodeLocation(( *__FILE__ == '/' ? strrchr( __FILE__, '/' ) + 1 : __FILE__ ),__FUNCTION__,__LINE__)
 
     /** \relates CodeLocation Stream output */
     std::ostream & operator<<( std::ostream & str, const CodeLocation & obj );
@@ -104,12 +110,37 @@ namespace zypp
    *  Main.cc(main):57 CAUGHT:   Main.cc(main):51: Something bad happened.
    * \endcode
    *
+   *
+   * Class Exception now offers a history list of message strings.
+   * These messages should describe what lead to the exception.
+   *
+   * The Exceptions message itself is NOT included in the history.
+   *
+   * Rethrow, remembering an old exception:
+   * \code
+   * try
+   * {
+   *   ....
+   * }
+   * catch( const Exception & olderr_r )
+   * {
+   *    ZYPP_CAUGHT( olderr_r )
+   *    HighLevelException newerr( "Something failed." );
+   *    newerr.rember( olderr_r );
+   *    ZYPP_THROW( newerr );
+   * }
+   * \endcode
+   *
+   * Print an Exception followed by it's history if available:
+   * \code
+   * Exception error;
+   * ERR << error << endl << error.historyAsString();
+   * \endcode
+   *
    * \todo That's a draft to have a common way of throwing exceptions.
    * Most probabely we'll finally use blocxx exceptions. Here, but not
    * in the remaining code of zypp. If we can we should try to wrap
    * the blocxx macros and typedef the classes in here.
-   *
-   * \todo maybe location and message stack.
    **/
   class Exception : public std::exception
   {
@@ -117,6 +148,9 @@ namespace zypp
 
   public:
     typedef exception_detail::CodeLocation CodeLocation;
+    typedef std::list<std::string>         History;
+    typedef History::const_iterator        HistoryIterator;
+    typedef History::size_type             HistorySize;
 
     /** Default ctor.
      * Use \ref ZYPP_THROW to throw exceptions.
@@ -127,6 +161,20 @@ namespace zypp
      * Use \ref ZYPP_THROW to throw exceptions.
     */
     Exception( const std::string & msg_r );
+    /** \overload */
+    Exception( std::string && msg_r );
+
+    /** Ctor taking a message and an exception to remember as history
+     * \see \ref remember
+     * Use \ref ZYPP_THROW to throw exceptions.
+    */
+    Exception( const std::string & msg_r, const Exception & history_r );
+    /** \overload moving */
+    Exception( std::string && msg_r, const Exception & history_r );
+    /** \overload moving */
+    Exception( const std::string & msg_r, Exception && history_r );
+    /** \overload moving */
+    Exception( std::string && msg_r, Exception && history_r );
 
     /** Dtor. */
     virtual ~Exception() throw();
@@ -140,7 +188,7 @@ namespace zypp
     { _where = where_r; }
 
     /** Return the message string provided to the ctor.
-     * \note This is not neccessarily the complete error message.
+     * \note This is not necessarily the complete error message.
      * The whole error message is provided by \ref asString or
      * \ref dumpOn.
     */
@@ -150,6 +198,77 @@ namespace zypp
     /** Error message provided by \ref dumpOn as string. */
     std::string asString() const;
 
+    /** Translated error message as string suitable for the user.
+     * \see \ref asUserStringHistory
+    */
+    std::string asUserString() const;
+
+  public:
+    /** \name History list of message strings.
+     * Maintain a simple list of individual error messages, that lead
+     * to this Exception. The Exceptions message itself is not included
+     * in the history. The History list stores the most recent message
+     * fist.
+     */
+    //@{
+
+    /** Store an other Exception as history. */
+    void remember( const Exception & old_r );
+    /** \overload moving */
+    void remember( Exception && old_r );
+
+    /** Add some message text to the history. */
+    void addHistory( const std::string & msg_r );
+    /** \overload moving */
+    void addHistory( std::string && msg_r );
+
+    /** \ref addHistory from string container types (oldest first) */
+    template<class TContainer>
+    void addToHistory( const TContainer & msgc_r )
+    {
+      for ( const std::string & el : msgc_r )
+       addHistory( el );
+    }
+    /** \ref addHistory from string container types (oldest first) moving */
+    template<class TContainer>
+    void moveToHistory( TContainer && msgc_r )
+    {
+      for ( std::string & el : msgc_r )
+       addHistory( std::move(el) );
+    }
+
+    /** Iterator pointing to the most recent message. */
+    HistoryIterator historyBegin() const
+    { return _history.begin(); }
+
+    /** Iterator pointing behind the last message. */
+    HistoryIterator historyEnd() const
+    { return _history.end(); }
+
+    /** Whether the history list is empty. */
+    bool historyEmpty() const
+    { return _history.empty(); }
+
+    /** The size of the history list. */
+    HistorySize historySize() const
+    { return _history.size(); }
+
+    /** The history as string. Empty if \ref historyEmpty.
+     * Otherwise:
+     * \code
+     * History:
+     *  - most recent message
+     *  - 2nd message
+     * ...
+     *  - oldest message
+     * \endcode
+    */
+    std::string historyAsString() const;
+
+    /** A single (multiline) string composed of \ref asUserString  and  \ref historyAsString. */
+    std::string asUserHistory() const;
+    //@}
+
   protected:
 
     /** Overload this to print a proper error message. */
@@ -160,6 +279,8 @@ namespace zypp
     static std::string strErrno( int errno_r );
      /** Make a string from \a errno_r and \a msg_r. */
     static std::string strErrno( int errno_r, const std::string & msg_r );
+    /** \overload moving */
+    static std::string strErrno( int errno_r, std::string && msg_r );
 
   public:
     /** Drop a logline on throw, catch or rethrow.
@@ -167,10 +288,13 @@ namespace zypp
     */
     static void log( const Exception & excpt_r, const CodeLocation & where_r,
                      const char *const prefix_r );
-
+    /** \overrload for not-Exception types thrown via ZYPP_THROW */
+    static void log( const char * typename_r, const CodeLocation & where_r,
+                     const char *const prefix_r );
   private:
     mutable CodeLocation _where;
-    std::string _msg;
+    std::string          _msg;
+    History              _history;
 
     /** Return message string. */
     virtual const char * what() const throw()
@@ -188,32 +312,75 @@ namespace zypp
   std::ostream & operator<<( std::ostream & str, const Exception & obj );
 
   ///////////////////////////////////////////////////////////////////
+  namespace exception_detail
+  {
+    /** SFINAE: Hide template signature unless \a TExcpt is derived from \ref Exception. */
+    template<class TExcpt>
+    using EnableIfIsException = typename std::enable_if< std::is_base_of<Exception,TExcpt>::value, int>::type;
+
+    /** SFINAE: Hide template signature if \a TExcpt is derived from  \ref Exception. */
+    template<class TExcpt>
+    using EnableIfNotException = typename std::enable_if< !std::is_base_of<Exception,TExcpt>::value, int>::type;
+
 
-  /** Helper for \ref ZYPP_THROW. */
-  template<class _Excpt>
-    void _ZYPP_THROW( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r )
+    /** Helper for \ref ZYPP_THROW( Exception ). */
+    template<class TExcpt, EnableIfIsException<TExcpt> = 0>
+    void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
+    template<class TExcpt, EnableIfIsException<TExcpt>>
+    void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r )
     {
       excpt_r.relocate( where_r );
       Exception::log( excpt_r, where_r, "THROW:   " );
       throw( excpt_r );
     }
 
-  /** Helper for \ref ZYPP_THROW. */
-  template<class _Excpt>
-    void _ZYPP_CAUGHT( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r )
+    /** Helper for \ref ZYPP_THROW( not Exception ). */
+    template<class TExcpt, EnableIfNotException<TExcpt> = 0>
+    void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
+    template<class TExcpt, EnableIfNotException<TExcpt>>
+    void do_ZYPP_THROW( const TExcpt & excpt_r, const CodeLocation & where_r )
+    {
+      Exception::log( typeid(excpt_r).name(), where_r, "THROW:   " );
+      throw( excpt_r );
+    }
+
+
+    /** Helper for \ref ZYPP_THROW( Exception ). */
+    template<class TExcpt, EnableIfIsException<TExcpt> = 0>
+    void do_ZYPP_CAUGHT( const TExcpt & excpt_r, const CodeLocation & where_r )
     {
       Exception::log( excpt_r, where_r, "CAUGHT:  " );
     }
 
-  /** Helper for \ref ZYPP_THROW. */
-  template<class _Excpt>
-    void _ZYPP_RETHROW( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r )
+    /** Helper for \ref ZYPP_THROW( not Exception ). */
+    template<class TExcpt, EnableIfNotException<TExcpt> = 0>
+    void do_ZYPP_CAUGHT( const TExcpt & excpt_r, const CodeLocation & where_r )
+    {
+      Exception::log( typeid(excpt_r).name(), where_r, "CAUGHT:  " );
+    }
+
+
+    /** Helper for \ref ZYPP_THROW( Exception ). */
+    template<class TExcpt, EnableIfIsException<TExcpt> = 0>
+    void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
+    template<class TExcpt, EnableIfIsException<TExcpt>>
+    void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r )
     {
       Exception::log( excpt_r, where_r, "RETHROW: " );
       excpt_r.relocate( where_r );
-      throw excpt_r;
+      throw;
     }
 
+    /** Helper for \ref ZYPP_THROW( not Exception ). */
+    template<class TExcpt, EnableIfNotException<TExcpt> = 0>
+    void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r ) __attribute__((noreturn));
+    template<class TExcpt, EnableIfNotException<TExcpt>>
+    void do_ZYPP_RETHROW( const TExcpt & excpt_r, const CodeLocation & where_r )
+    {
+      Exception::log( excpt_r, where_r, "RETHROW: " );
+      throw;
+    }
+  } // namespace exception_detail
   ///////////////////////////////////////////////////////////////////
 
   /** \defgroup ZYPP_THROW ZYPP_THROW macros
@@ -223,15 +390,15 @@ namespace zypp
   //@{
   /** Drops a logline and throws the Exception. */
 #define ZYPP_THROW(EXCPT)\
-  _ZYPP_THROW( EXCPT, ZYPP_EX_CODELOCATION )
+  ::zypp::exception_detail::do_ZYPP_THROW( EXCPT, ZYPP_EX_CODELOCATION )
 
   /** Drops a logline telling the Exception was caught (in order to handle it). */
 #define ZYPP_CAUGHT(EXCPT)\
-  _ZYPP_CAUGHT( EXCPT, ZYPP_EX_CODELOCATION )
+  ::zypp::exception_detail::do_ZYPP_CAUGHT( EXCPT, ZYPP_EX_CODELOCATION )
 
   /** Drops a logline and rethrows, updating the CodeLocation. */
 #define ZYPP_RETHROW(EXCPT)\
-  _ZYPP_RETHROW( EXCPT, ZYPP_EX_CODELOCATION )
+  ::zypp::exception_detail::do_ZYPP_RETHROW( EXCPT, ZYPP_EX_CODELOCATION )
 
 
   /** Throw Exception built from a message string. */