#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
///////////////////////////////////////////////////////////////////
/** 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 );
* 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
{
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.
* 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();
{ _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.
*/
/** Error message provided by \ref dumpOn as string. */
std::string asString() const;
- /** Translated error message as string suitable for the user. */
+ /** 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. */
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.
*/
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()
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;
- /** Helper for \ref ZYPP_THROW. */
- template<class _Excpt>
- void _ZYPP_THROW( const _Excpt & excpt_r, const exception_detail::CodeLocation & where_r )
+ /** 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( 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;
}
+ /** 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
//@{
/** 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. */