diff --git a/src/backend/thread-wrapper.hpp b/src/backend/thread-wrapper.hpp index 2de5fea70..5c501675f 100644 --- a/src/backend/thread-wrapper.hpp +++ b/src/backend/thread-wrapper.hpp @@ -192,7 +192,6 @@ namespace backend { lumiera_thread_join (thread_); thread_ = 0; - TODO("check this, is error::State approbiate or do we want Runtime as thrown by throwOnError()"); if (errorInOtherThread) throw lumiera::error::State ("Thread terminated with error:", errorInOtherThread); } diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 412a422ac..8b22775c1 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -2,7 +2,7 @@ ERROR.hpp - Lumiera Exception Interface Copyright (C) Lumiera.org - 2008, Hermann Vosseler + 2008-2010 Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -33,32 +33,32 @@ namespace lumiera { using std::string; - - + + /** error-ID for unspecified exceptions */ LUMIERA_ERROR_DECLARE(EXCEPTION); /** - * Interface and Base class of all Exceptions thrown + * Interface and Base class of all Exceptions thrown * from within Lumiera (C++) code. Common operations * for getting an diagnostic message and for obtaining * the root cause, i.e. the first exception encountered - * in a chain of exceptions. + * in a chain of exceptions. */ class Error : public std::exception { public: - Error (string description="", const char* id=LUMIERA_ERROR_EXCEPTION) throw(); + Error (string description="", const char* const id=LUMIERA_ERROR_EXCEPTION) throw(); Error (std::exception const& cause, - string description="", const char* id=LUMIERA_ERROR_EXCEPTION) throw(); + string description="", const char* const id=LUMIERA_ERROR_EXCEPTION) throw(); Error (const Error&) throw(); virtual ~Error () throw() {}; /** yield a diagnostic message characterising the problem */ virtual const char* what () const throw(); - + /** the internal Lumiera-error-ID (was set as C-errorstate in ctor) */ const char* getID () const throw() { return this->id_; } @@ -72,10 +72,10 @@ namespace lumiera { * @return the description string, maybe empty (if there is no known root cause) */ const string& rootCause () const throw() { return this->cause_; } - + /** replace the previous or default friendly message for the user. To be localised. */ Error& setUsermsg (const string& newMsg) throw() { this->msg_ = newMsg; return *this; } - + /** give additional developer info. Typically used at intermediate handlers to add context. */ Error& prependInfo (const string& text) throw() { this->desc_.insert (0,text); return *this; } @@ -86,19 +86,18 @@ namespace lumiera { string desc_; ///< detailed description of the error situation for the developers mutable string what_; ///< buffer for generating the detailed description on demand const string cause_; ///< description of first exception encountered in the chain - + static const string extractCauseMsg (std::exception const&) throw(); }; - - - - - + + + + + /* === Exception Sub-categories === */ - - namespace error - { + + namespace error { /** global function for handling unknown exceptions * encountered at functions declaring not to throw @@ -107,28 +106,28 @@ namespace lumiera { * add some diagnostics prior to halting. */ void lumiera_unexpectedException () throw(); - + /** throw an error::Fatal indicating "assertion failure" */ void assertion_terminate (const string& location); - + /* constants to be used as error IDs */ - LUMIERA_ERROR_DECLARE (LOGIC ); ///< contradiction to internal logic assumptions detected - LUMIERA_ERROR_DECLARE (FATAL ); ///< unable to cope with, internal logic floundered + LUMIERA_ERROR_DECLARE (LOGIC ); ///< contradiction to internal logic assumptions detected + LUMIERA_ERROR_DECLARE (FATAL ); ///< unable to cope with, internal logic floundered LUMIERA_ERROR_DECLARE (CONFIG ); ///< execution aborted due to misconfiguration LUMIERA_ERROR_DECLARE (STATE ); ///< unforeseen internal state + LUMIERA_ERROR_DECLARE (FLAG ); ///< non-cleared lumiera errorstate from C code LUMIERA_ERROR_DECLARE (INVALID ); ///< invalid input or parameters encountered LUMIERA_ERROR_DECLARE (EXTERNAL ); ///< failure in external service the application relies on LUMIERA_ERROR_DECLARE (ASSERTION); ///< assertion failure /* generic error situations */ - LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch - LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached - LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value - LUMIERA_ERROR_DECLARE (RUNTIME); ///< generic runtime error, will be overridden by id - + LUMIERA_ERROR_DECLARE (WRONG_TYPE); ///< runtime type mismatch + LUMIERA_ERROR_DECLARE (ITER_EXHAUST); ///< end of sequence reached + LUMIERA_ERROR_DECLARE (BOTTOM_VALUE); ///< invalid or NIL value -/** Macro for creating derived exception classes properly + +/** Macro for creating derived exception classes properly * integrated into Lumiera's exception hierarchy. Using * this macro assures that the new class will get the full * set of constructors and behaviour common to all exception @@ -143,44 +142,73 @@ namespace lumiera { const char* id=_ID_) throw() \ : PARENT (description, id) {} \ \ - CLASS (std::exception& cause, \ + CLASS (std::exception const& cause, \ std::string description="", \ const char* id=_ID_) throw() \ : PARENT (cause, description, id) {} \ }; - + //-------------------------CLASS-----PARENT--ID---------------------- LUMIERA_EXCEPTION_DECLARE (Logic, Error, LUMIERA_ERROR_LOGIC); LUMIERA_EXCEPTION_DECLARE (Fatal, Logic, LUMIERA_ERROR_FATAL); LUMIERA_EXCEPTION_DECLARE (Config, Error, LUMIERA_ERROR_CONFIG); LUMIERA_EXCEPTION_DECLARE (State, Error, LUMIERA_ERROR_STATE); + LUMIERA_EXCEPTION_DECLARE (Flag, State, LUMIERA_ERROR_FLAG); LUMIERA_EXCEPTION_DECLARE (Invalid, Error, LUMIERA_ERROR_INVALID); LUMIERA_EXCEPTION_DECLARE (External, Error, LUMIERA_ERROR_EXTERNAL); - LUMIERA_EXCEPTION_DECLARE (Runtime, Error, LUMIERA_ERROR_RUNTIME); /** install our own handler for undeclared exceptions. Will be * called automatically ON_BASIC_INIT when linking exception.cpp */ void install_unexpectedException_handler (); + /** @return error detail-info if currently set, a default message else */ + const char* detailInfo (); + } // namespace error - - + + + /** - * Throw a 'Runtime' error which wraps an existing lumiera error - * no-op when no error is pending. Does not clear the error state. + * Check the lumiera error state, which maybe was set by C-code. + * @throw Errorflag exception to signal an detected lumiera error + * @note specific error code and information is enclosed in + * the raised exception; the error state is \em not cleared. */ - static inline void throwOnError() + inline void + throwOnError() { - lumiera_err err = lumiera_error_peek(); - if (err) + if (lumiera_err errorFlag =lumiera_error()) { - const char* extra = lumiera_error_extra(); - throw error::Runtime(extra?extra:"", err); - } + throw error::Flag( error::detailInfo() + , errorFlag); + } } //causes the error state to be set + + /** Check the lumiera error state and throw a specific exception + * in case a non-cleared errorflag is detected. No-op else. + * @throw instance of the lumiera::Error subclass provided as + * template parameter, containing an lumiera::error::Flag + * as root cause to denote the detected error-flag state. + */ + template + inline void + maybeThrow(string description) + { + if (lumiera_err errorFlag =lumiera_error()) + { + throw EX (error::Flag( error::detailInfo() + , errorFlag) + ,description); + } } + + template + inline void + maybeThrow() + { + maybeThrow(""); } - - + + } // namespace lumiera diff --git a/src/lib/exception.cpp b/src/lib/exception.cpp index f3013009c..2e07447ad 100644 --- a/src/lib/exception.cpp +++ b/src/lib/exception.cpp @@ -34,6 +34,11 @@ using std::exception; namespace lumiera { + + typedef const char* CStr; + typedef const char* const CCStr; + + namespace error { /** the message shown to the user per default @@ -48,37 +53,46 @@ namespace lumiera { + typeid(*exception_obj).name() + ")"; } - inline const char* - default_or_given (const char* id) + inline CStr + default_or_given (CCStr id) { return id? id : LUMIERA_ERROR_STATE; } + CStr + detailInfo () + { + CCStr detailinfo = lumiera_error_extra(); + return isnil (detailinfo)? "Lumiera errorstate detected" + : detailinfo; + } + /* constants to be used as error IDs */ - LUMIERA_ERROR_DEFINE (LOGIC , "internal logic broken"); - LUMIERA_ERROR_DEFINE (FATAL , "floundered"); - LUMIERA_ERROR_DEFINE (CONFIG , "misconfiguration"); - LUMIERA_ERROR_DEFINE (STATE , "unforeseen state"); - LUMIERA_ERROR_DEFINE (INVALID , "invalid input or parameters"); - LUMIERA_ERROR_DEFINE (EXTERNAL , "failure in external service"); + LUMIERA_ERROR_DEFINE (LOGIC , "internal logic broken"); + LUMIERA_ERROR_DEFINE (FATAL , "floundered"); + LUMIERA_ERROR_DEFINE (CONFIG , "misconfiguration"); + LUMIERA_ERROR_DEFINE (STATE , "unforeseen state"); + LUMIERA_ERROR_DEFINE (FLAG , "non-cleared lumiera errorstate"); + LUMIERA_ERROR_DEFINE (INVALID , "invalid input or parameters"); + LUMIERA_ERROR_DEFINE (EXTERNAL , "failure in external service"); LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure"); - + /* some further generic error situations */ - LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch"); + LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch"); LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached"); - LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value"); + LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value"); } // namespace error - LUMIERA_ERROR_DEFINE (EXCEPTION, "generic Lumiera exception"); + LUMIERA_ERROR_DEFINE (EXCEPTION, "generic Lumiera exception"); /** @note we set the C-style errorstate as a side effect */ - Error::Error (string description, const char* id) throw() + Error::Error (string description, CCStr id) throw() : std::exception (), id_ (error::default_or_given (id)), msg_ (error::default_usermsg (this)), @@ -90,7 +104,7 @@ namespace lumiera { Error::Error (std::exception const& cause, - string description, const char* id) throw() + string description, CCStr id) throw() : std::exception (), id_ (error::default_or_given (id)), msg_ (error::default_usermsg (this)), @@ -117,7 +131,7 @@ namespace lumiera { * If a root cause can be obtained, this will be included in the * generated output as well. */ - const char* + CStr Error::what() const throw() { if (isnil (this->what_)) @@ -160,13 +174,13 @@ namespace lumiera { void lumiera_unexpectedException () throw() { - const char* is_halted + CCStr is_halted = "### Lumiera halted due to an unexpected Error ###"; std::cerr << "\n" << is_halted << "\n\n"; ERROR (NOBUG_ON, "%s", is_halted); - if (const char * errorstate = lumiera_error ()) + if (CCStr errorstate = lumiera_error ()) ERROR (NOBUG_ON, "last registered error was....\n%s", errorstate); std::terminate(); @@ -176,20 +190,20 @@ namespace lumiera { { throw Fatal (location, LUMIERA_ERROR_ASSERTION) .setUsermsg("Program terminated because of violating " - "an internal consistency check."); + "an internal consistency check."); } void install_unexpectedException_handler () { - std::set_unexpected (lumiera_unexpectedException); + std::set_unexpected (lumiera_unexpectedException); } namespace { - LifecycleHook schedule_ (ON_BASIC_INIT, &install_unexpectedException_handler); + LifecycleHook schedule_ (ON_BASIC_INIT, &install_unexpectedException_handler); } - - - } // namespace error + + } // namespace error + } // namespace lumiera diff --git a/tests/40components.tests b/tests/40components.tests index 6e078acaf..7b4ff8672 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -205,7 +205,9 @@ out: intermediate handler caught: LUMIERA_ERROR_EXTERNAL:failure in external ser out: caught lumiera::Error: LUMIERA_ERROR_STATE:unforeseen state -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-7\). out: intermediate handler caught: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-8\).....will rethrow as error::State out: 2nd intermediate handler caught: LUMIERA_ERROR_STATE:unforeseen state -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-8\).....will rethrow as error::Config -out: caught lumiera::Error: LUMIERA_ERROR_CONFIG:misconfiguration -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-8\). +out: caught lumiera::Error: LUMIERA_ERROR_CONFIG:misconfiguration -- caused by: LUMIERA_ERROR_EXTERNAL:failure in external service \(test-8\)\. +out: caught lumiera::Error: LUMIERA_ERROR_LIFE_AND_UNIVERSE:and everything\? \(what is the answer\?\)\. +out: caught error::Logic: LUMIERA_ERROR_LOGIC:internal logic broken \(the big bang\). -- caused by: LUMIERA_ERROR_LIFE_AND_UNIVERSE:and everything\? \(what is the answer\?\)\. END diff --git a/tests/lib/exceptionerrortest.cpp b/tests/lib/exceptionerrortest.cpp index 4407a39f8..802b6953f 100644 --- a/tests/lib/exceptionerrortest.cpp +++ b/tests/lib/exceptionerrortest.cpp @@ -83,6 +83,7 @@ namespace lumiera { catcher (&test::doubleNestedTh,"test-8"); checkErrorIntegration(); + checkErrorFlagPropagation(); checkRootCauseChaining(); } @@ -153,6 +154,27 @@ namespace lumiera { ASSERT (!lumiera_error ()); } + + void detectErrorflag (string) { throwOnError(); } + void detectErrorflagChained (string msg) { maybeThrow(msg); } + + + /** @test verify throwing of Exceptions + * based on a non-cleared C error flag + */ + void checkErrorFlagPropagation() + { + lumiera_error_set(LUMIERA_ERROR_LIFE_AND_UNIVERSE, "what is the answer?"); + ASSERT (lumiera_error_peek()); + + catcher (&test::detectErrorflag, ""); + ASSERT (LUMIERA_ERROR_LIFE_AND_UNIVERSE == lumiera_error_peek()); + + catcher (&test::detectErrorflagChained, "the big bang"); + ASSERT (LUMIERA_ERROR_LIFE_AND_UNIVERSE == lumiera_error()); + } + + /** @test the chaining of lumiera::Exception objects * and the retrieval of the original root cause. */