diff --git a/src/lib/error-exception.cpp b/src/lib/error-exception.cpp index 2b8bf5cf9..b6de638c2 100644 --- a/src/lib/error-exception.cpp +++ b/src/lib/error-exception.cpp @@ -45,11 +45,6 @@ using std::exception; namespace lumiera { - - typedef const char* CStr; - typedef const char* const CCStr; - - namespace error { /** the message shown to the user per default @@ -58,22 +53,16 @@ namespace lumiera { * @todo to be localised */ inline const string - default_usermsg (Error* exception_obj) throw() + default_usermsg (Error* exception_obj) noexcept { return string("Sorry, Lumiera encountered an internal error. (") + util::typeStr(*exception_obj) + ")"; } - inline CStr - default_or_given (CCStr id) - { - return id? id : LUMIERA_ERROR_STATE; - } - CStr detailInfo () { - CCStr detailinfo = lumiera_error_extra(); + CStr detailinfo = lumiera_error_extra(); return isnil (detailinfo)? "Lumiera errorstate detected" : detailinfo; } @@ -90,16 +79,14 @@ namespace lumiera { LUMIERA_ERROR_DEFINE (ASSERTION, "assertion failure"); /* some further generic error situations */ - LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated"); - LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch"); + LUMIERA_ERROR_DEFINE (LIFECYCLE, "Lifecycle assumptions violated"); + LUMIERA_ERROR_DEFINE (WRONG_TYPE, "runtime type mismatch"); LUMIERA_ERROR_DEFINE (ITER_EXHAUST, "end of sequence reached"); LUMIERA_ERROR_DEFINE (CAPACITY, "predefined fixed storage capacity"); LUMIERA_ERROR_DEFINE (INDEX_BOUNDS, "index out of bounds"); LUMIERA_ERROR_DEFINE (BOTTOM_VALUE, "invalid or NIL value"); - LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); - LUMIERA_ERROR_DEFINE (UNIMPLEMENTED, "using a feature not yet implemented...."); - - + LUMIERA_ERROR_DEFINE (UNCONNECTED, "missing connection"); + LUMIERA_ERROR_DEFINE (UNIMPLEMENTED,"using a feature not yet implemented...."); } // namespace error @@ -109,38 +96,29 @@ namespace lumiera { /** @note we set the C-style errorstate as a side effect */ - Error::Error (string description, CCStr id) throw() - : std::exception (), - id_ (error::default_or_given (id)), - msg_ (error::default_usermsg (this)), - desc_ (description), - cause_ ("") - { - lumiera_error_set (this->id_, description.c_str ()); - } + Error::Error (string description, lumiera_err const id) noexcept + : std::exception{} + , id_{id} + , msg_{error::default_usermsg (this)} + , desc_{description} + , cause_{} + { + lumiera_error_set (this->id_, description.c_str()); + } Error::Error (std::exception const& cause, - string description, CCStr id) throw() - : std::exception (), - id_ (error::default_or_given (id)), - msg_ (error::default_usermsg (this)), - desc_ (description), - cause_ (extractCauseMsg(cause)) - { - lumiera_error_set (this->id_, description.c_str ()); - } + string description, lumiera_err const id) noexcept + : std::exception{} + , id_{id} + , msg_{error::default_usermsg (this)} + , desc_{description} + , cause_{extractCauseMsg(cause)} + { + lumiera_error_set (this->id_, description.c_str()); + } - /** @note copy ctor behaves like chaining, i.e setting the cause_. */ - Error::Error (const Error& ref) throw() - : std::exception (), - id_ (ref.id_), - msg_ (ref.msg_), - desc_ (ref.desc_), - cause_ (extractCauseMsg(ref)) - { } - /** Description of the problem, including the internal char constant @@ -149,7 +127,7 @@ namespace lumiera { * generated output as well. */ CStr - Error::what() const throw() + Error::what() const noexcept { if (isnil (this->what_)) { @@ -165,9 +143,9 @@ namespace lumiera { * first exception encountered in a chain of exceptions */ const string - Error::extractCauseMsg (const exception& cause) throw() + Error::extractCauseMsg (const exception& cause) noexcept { - const Error* err=dynamic_cast (&cause); + const Error* err = dynamic_cast (&cause); if (err) { if (isnil (err->cause_)) @@ -175,7 +153,6 @@ namespace lumiera { else return err->cause_; // cause was caused by another exception } - // unknown other exception type return cause.what (); } diff --git a/src/lib/error.h b/src/lib/error.h index f4fbc0857..6d1bf2485 100644 --- a/src/lib/error.h +++ b/src/lib/error.h @@ -69,16 +69,16 @@ typedef const char* lumiera_err; * @param err name of the error without the 'LUMIERA_ERROR_' prefix (example: NO_MEMORY) */ #define LUMIERA_ERROR_DECLARE(err) \ -extern lumiera_err LUMIERA_ERROR_##err +extern lumiera_err const LUMIERA_ERROR_##err /** - * Definition and initialization of an error constant. + * Definition and initialisation of an error constant. * This macro eases the error definition in implementation files * @param err name of the error without the 'LUMIERA_ERROR_' prefix (example: NO_MEMORY) - * @param msg message describing the error in plain english (example: "memory allocation failed") + * @param msg message describing the error in plain English (example: "memory allocation failed") */ #define LUMIERA_ERROR_DEFINE(err, msg) \ -lumiera_err LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg +lumiera_err const LUMIERA_ERROR_##err = "LUMIERA_ERROR_" #err ":" msg /** * Helper macro to raise an error for the current thread. diff --git a/src/lib/error.hpp b/src/lib/error.hpp index 6a8187571..0fd596e62 100644 --- a/src/lib/error.hpp +++ b/src/lib/error.hpp @@ -28,7 +28,7 @@ ** Within Lumiera, C-style error states and C++-style exceptions ** are tightly integrated. Creating an exception sets the error flag, ** and there are helpers available to throw an exception automatically - ** when a non-cleare error state is detected. + ** when a unclear error state is detected. ** ** @see error-state.c ** @see error.hpp @@ -42,67 +42,106 @@ #include "include/logging.h" #include "include/lifecycle.h" #include "lib/error.h" + #include #include + namespace lumiera { using std::string; + using CStr = const char*; +#define LERR_(_NAME_) LUMIERA_ERROR_##_NAME_ /** error-ID for unspecified exceptions */ - LUMIERA_ERROR_DECLARE(EXCEPTION); + LUMIERA_ERROR_DECLARE(EXCEPTION); /** - * 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. + * Interface and Base definition for all Lumiera Exceptions. + * Provides common operations for getting an diagnostic message + * and to obtaining the _root cause_ message, i.e. the message + * from the first exception encountered in a chain of exceptions. */ - class Error : public std::exception + class Error + : public std::exception { + lumiera_err const id_; ///< an LUMIERA_ERROR id, which is set as errorstate on construction + string msg_; ///< friendly message intended for users (to be localised) + 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 + + public: - Error (string description="", const char* const id=LUMIERA_ERROR_EXCEPTION) throw(); - Error (std::exception const& cause, - string description="", const char* const id=LUMIERA_ERROR_EXCEPTION) throw(); + virtual ~Error () noexcept { }; ///< this is an interface - Error (const Error&) throw(); - virtual ~Error () throw() {}; - /** yield a diagnostic message characterising the problem */ - virtual const char* what () const throw(); + Error (string description="" + ,lumiera_err const id =LERR_(EXCEPTION)) noexcept; + Error (std::exception const& cause + ,string description="" + ,lumiera_err const id =LERR_(EXCEPTION)) noexcept; - /** the internal Lumiera-error-ID (was set as C-errorstate in ctor) */ - const char* getID () const throw() { return this->id_; } + Error (Error &&) = default; + Error (Error const&) = default; + Error& operator= (Error &&) = delete; + Error& operator= (Error const&) = delete; + + /** std::exception interface : yield a diagnostic message */ + virtual CStr + what () const noexcept override; + + /** the internal Lumiera-error-ID + * (was set as C-errorstate in ctor) */ + lumiera_err + getID () const noexcept + { + return id_; + } /** extract the message to be displayed for the user */ - const string& getUsermsg () const throw(); + string const& + getUsermsg () const noexcept + { + return msg_; + } /** If this exception was caused by a chain of further exceptions, * return the description of the first one registered in this throw sequence. - * This works only if every exceptions thrown as a consequence of another exception + * This works only if every exceptions thrown as a consequence of another exception * is properly constructed by passing the original exception to the constructor * @return the description string, maybe empty (if there is no known root cause) */ - const string& rootCause () const throw() { return this->cause_; } + string const& + rootCause () const noexcept + { + return 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; } + /** replace the previous or default friendly message for the user. + * @note to be localised / translated. + */ + Error& + setUsermsg (string const& newMsg) noexcept + { + 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; } + Error& + prependInfo (string const& text) noexcept + { + desc_.insert (0, text); + return *this; + } private: - const char* id_; ///< an LUMIERA_ERROR id, which is set as errorstate on construction - string msg_; ///< friendly message intended for users (to be localised) - 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(); + static const string + extractCauseMsg (std::exception const&) noexcept; }; @@ -115,7 +154,7 @@ namespace lumiera { namespace error { /** global function for handling unknown exceptions - * encountered at functions declaring not to throw + * encountered at functions declaring not to throw * this kind of exception. Basically, any such event * can be considered a severe design flaw; we can just * add some diagnostics prior to halting. @@ -148,43 +187,41 @@ namespace lumiera { -/** 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 - * classes, so it should be used when creating an derived - * exception type for more then strictly local purposes - */ -#define LUMIERA_EXCEPTION_DECLARE(CLASS, PARENT, _ID_) \ - class CLASS : public PARENT \ - { \ - public: \ - CLASS (std::string description="", \ - const char* id=_ID_) throw() \ - : PARENT (description, id) {} \ - \ - CLASS (std::exception const& cause, \ - std::string description="", \ - const char* id=_ID_) throw() \ - : PARENT (cause, description, id) {} \ + /** + * Derived specific exceptions within Lumiera's exception hierarchy. + */ + template + class LumieraError + : public PAR + { + public: + LumieraError (std::string description="" + ,lumiera_err const id=eID) noexcept + : PAR{description, id} + { } + LumieraError (std::exception const& cause + ,std::string description="" + ,lumiera_err const id=eID) noexcept + : PAR{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); + //----CLASS-------------------ID--------------PARENT------ + using Logic = LumieraError; + using Fatal = LumieraError; + using State = LumieraError; + using Flag = LumieraError; + using Invalid = LumieraError; + using Config = LumieraError; + using External = LumieraError; /** install our own handler for undeclared exceptions. Will be * called automatically ON_BASIC_INIT when linking exception.cpp */ - void install_unexpectedException_handler (); + void install_unexpectedException_handler(); /** @return error detail-info if currently set, a default message else */ - const char* detailInfo (); + CStr detailInfo(); } // namespace error @@ -213,22 +250,14 @@ namespace lumiera { */ template inline void - maybeThrow(string description) + maybeThrow (string description ="") { if (lumiera_err errorFlag =lumiera_error()) { - throw EX (error::Flag( error::detailInfo() - , errorFlag) + throw EX (error::Flag{error::detailInfo(), errorFlag} ,description); } } - template - inline void - maybeThrow() - { - maybeThrow(""); - } - } // namespace lumiera @@ -256,7 +285,7 @@ namespace lumiera { /**************************************************//** - * if NoBug is used, redefine some macros + * if NoBug is used, redefine some macros * to rather throw Lumiera Errors instead of aborting */ #if 0 ///////////////////////////////////TODO disabled for now. NoBug aborts are hard and may hold some locks. There are hooks to allow throwing from NoBug TODO use them.... diff --git a/tests/library/exception-error-test.cpp b/tests/library/exception-error-test.cpp index 0b0d38d0a..104883e7f 100644 --- a/tests/library/exception-error-test.cpp +++ b/tests/library/exception-error-test.cpp @@ -28,41 +28,39 @@ #include "lib/error.h" #include "lib/error.hpp" - #include "lib/test/run.hpp" +#include "lib/format-cout.hpp" #include "lib/util.hpp" - -#include -#include #include #include +#include using std::runtime_error; using std::exception; using std::string; -using std::cout; namespace lumiera { namespace test { - /** local specific error-constant for use in the + LUMIERA_ERROR_DEFINE (LIFE_AND_UNIVERSE, "and everything?"); + /** local specific error-constant for use in the * constructor of the nested SpecificError class. */ - LUMIERA_ERROR_DEFINE(LIFE_AND_UNIVERSE, "and everything?"); - LUMIERA_ERROR_DEFINE(DERIVED, "convoluted exception"); + LUMIERA_ERROR_DECLARE(DERIVED); + LUMIERA_ERROR_DEFINE (DERIVED, "convoluted exception"); - /** declare a specific Error class with parent class error::external */ - LUMIERA_EXCEPTION_DECLARE (DerivedError, error::External, LUMIERA_ERROR_DERIVED); + /** define a specific Error subclass derived from error::external */ + using DerivedError = error::LumieraError; /******************************************************//** * Some aspects of C++ exception handling. - * Not to be confused with the basic C-style error value + * Not to be confused with the basic C-style error value * mechanism used by the low-level parts of the backend. - * Both approaches are laregely orthogonal, but the + * Both approaches are largely orthogonal, but the * C++ exception handling uses the C-style error constants. * */ @@ -71,8 +69,8 @@ namespace lumiera { typedef ExceptionError_test test; virtual void run (Arg arg) { - if (0 < arg.size() && arg[1]=="terminate") - terminateUnknown (); + if (0 < arg.size() and arg[1]=="terminate") + terminateUnknown(); catcher (&test::throwSpecial, ""); @@ -109,22 +107,22 @@ namespace lumiera { * which we are able to catch because it is derived from std::exception. * We don't need to know the exact type, but we can classify the error situation * as a "state error" and throw an error::State, passing on the root cause. - * Some levels up, this error get caught and the root cause can be + * Some levels up, this error get caught and the root cause can be * extracted successfully. */ - void nestedThrower (string msg) throw(Error) + void nestedThrower (string msg) { try { throwExternal(msg); } catch (std::exception& e) { - cout << "intermediate handler caught: " << e.what() + cout << "intermediate handler caught: " << e.what() << "....will rethrow as error::State\n"; throw error::State (e); } } /** @test repeated repackaging and rethrowing */ - void doubleNestedTh (string msg) throw(error::Config) + void doubleNestedTh (string msg) { try { nestedThrower (msg); } catch (Error& e) @@ -142,25 +140,25 @@ namespace lumiera { void checkErrorIntegration() { lumiera_error (); - CHECK (!lumiera_error ()); + CHECK (not lumiera_error()); Error err1; - Error err2("boo",LUMIERA_ERROR_DERIVED); + Error err2("boo",LERR_(DERIVED)); CHECK (err1.getID () == lumiera_error ()); // (we didn't clear the first one!) - Error err3("boooo",LUMIERA_ERROR_DERIVED); + Error err3("boooo",LERR_(DERIVED)); CHECK (err3.getID () == lumiera_error ()); SpecificError err4; - CHECK (err4.getID () == LUMIERA_ERROR_LIFE_AND_UNIVERSE); + CHECK (err4.getID () == LERR_(LIFE_AND_UNIVERSE)); CHECK (err4.getID () == lumiera_error ()); - CHECK (!lumiera_error ()); + CHECK (not lumiera_error()); } void detectErrorflag (string) { throwOnError(); } - void detectErrorflagChained (string msg) { maybeThrow(msg); } + void detectErrorflagChained (string msg) { maybeThrow (msg); } /** @test verify throwing of Exceptions @@ -168,14 +166,14 @@ namespace lumiera { */ void checkErrorFlagPropagation() { - lumiera_error_set(LUMIERA_ERROR_LIFE_AND_UNIVERSE, "what is the answer?"); + lumiera_error_set(LERR_(LIFE_AND_UNIVERSE), "what is the answer?"); CHECK (lumiera_error_peek()); catcher (&test::detectErrorflag, ""); - CHECK (LUMIERA_ERROR_LIFE_AND_UNIVERSE == lumiera_error_peek()); + CHECK (LERR_(LIFE_AND_UNIVERSE) == lumiera_error_peek()); catcher (&test::detectErrorflagChained, "the big bang"); - CHECK (LUMIERA_ERROR_LIFE_AND_UNIVERSE == lumiera_error()); + CHECK (LERR_(LIFE_AND_UNIVERSE) == lumiera_error()); } @@ -186,8 +184,8 @@ namespace lumiera { { error::Logic err1; error::Config err2(err1); - error::Config err3(err2); //note: using copy ctor behaves like chaining - Error err4(err1); // note: copy ctor + error::Config err3(err2); + Error err4(err1); // note: copy ctor std::runtime_error rerr("what a shame"); error::External err5(rerr); @@ -195,7 +193,7 @@ namespace lumiera { CHECK (err2.rootCause() == err1.what()); CHECK (err3.rootCause() == err1.what()); - CHECK (err4.rootCause() == err1.what()); + CHECK (err4.rootCause() == err1.rootCause()); // mere copy is not a root cause CHECK (err5.rootCause() == rerr.what()); CHECK (err6.rootCause() == rerr.what()); @@ -204,31 +202,40 @@ namespace lumiera { /** @test terminate the Application by throwing an undeclared exception. * this should result in the global unknown() handler to be called, - * so usually it will terminate the test run. + * so usually it will terminate the test run. * @note inside error.hpp, an initialisation hook has been installed into * AppState, causing our own unknown() handler to be installed and * invoked, which gives additional diagnostics.*/ - void terminateUnknown () throw() + void terminateUnknown () noexcept { throw Error("You'll never get me, won't you?"); } /** a very specific Exception class - * local to this scope and with + * local to this scope and with * additional behaviour. - */ - class SpecificError : public error::Invalid + */ + class SpecificError + : public error::Invalid { - int value; + int value_; public: - SpecificError () : Invalid("don't panic",LUMIERA_ERROR_LIFE_AND_UNIVERSE), value(42) {} - int revealIt () { return value; } + SpecificError() + : error::Invalid{"don't panic", LUMIERA_ERROR_LIFE_AND_UNIVERSE} + , value_(42) + { } + + int + revealIt() + { + return value_; + } }; - /** helper: provides a bunch of catch-clauses and + /** helper: provides a bunch of catch-clauses and * runs the given member functions within */ void catcher (void (ExceptionError_test::*funky)(string), string context)