diff --git a/src/lib/result.hpp b/src/lib/result.hpp index c7dcd122b..2f5f065e9 100644 --- a/src/lib/result.hpp +++ b/src/lib/result.hpp @@ -22,9 +22,9 @@ /** @file result.hpp - ** Intermediary value object to represent the result of an operation. - ** This operation might have produced a value result or failed with an exception. - ** Typically, the Result token is used _inline_ -- immediately either invoking + ** Intermediary value object to represent »either« an operation result or a failure. + ** Some operation might have produced a value result or failed with an exception. + ** Typically, the Result token is used _inline_ — immediately either invoking ** one of the member function or employing the built-in result type conversion. ** It will be copyable iff the result value is copyable. There is an implicit ** valid or failure state, which can be tested. Any attempt to get the value @@ -42,15 +42,14 @@ #include "lib/wrapper.hpp" #include "lib/util.hpp" +#include #include -#include namespace lib { using util::isnil; - using std::string; namespace error = lumiera::error; @@ -74,27 +73,27 @@ namespace lib { template<> class Result { - string failureLog_; + std::exception_ptr failure_; public: /** mark either failure (default) or success */ Result (bool success =false) - : failureLog_{success? "": "operation failed"} + : failure_{success? nullptr: std::make_exception_ptr (error::State{"operation failed"})} { } /** failed result, with reason given.*/ Result (lumiera::Error const& reason) - : failureLog_{reason.what()} + : failure_{std::make_exception_ptr (reason)} { } operator bool() const { return isValid(); } - bool isValid() const { return isnil (failureLog_); } + bool isValid() const { return not failure_; } void maybeThrow() const { - if (not isValid()) - throw error::State (failureLog_); + if (failure_) + std::rethrow_exception(failure_); } }; @@ -115,12 +114,7 @@ namespace lib { wrapper::ItemWrapper value_; public: - /** mark an invalid/failed result */ - Result (string const& failureReason ="no result") - : Result{failureReason} - { } - - /** failed result, with reason given.*/ + /** mark failed result, with reason given.*/ Result (lumiera::Error const& reason) : Result{reason} { } @@ -131,6 +125,7 @@ namespace lib { , value_{std::forward (value)} { } + // is or is not copyable depending on RES operator RES() const @@ -158,9 +153,9 @@ namespace lib { } }; + /** deduction guard: allow _perfect forwarding_ of a any result into the ctor call. */ template - Result (VAL&& x) -> Result; - + Result (VAL&&) -> Result; diff --git a/src/stage/model/element-access.hpp b/src/stage/model/element-access.hpp index df89410cf..3a5040dc3 100644 --- a/src/stage/model/element-access.hpp +++ b/src/stage/model/element-access.hpp @@ -133,7 +133,7 @@ namespace model { struct ElementAccess::TypeConverter : RawResult::Visitor { - lib::Result result{"not convertible to desired target widget"}; + lib::Result result{error::Invalid{"not convertible to desired target widget"}}; template // note the "backward" use. We pick that base interface using canUpcast = std::is_convertible; // into which our desired result type can be upcast, because @@ -150,7 +150,7 @@ namespace model { result = *downcasted; } else - result = lib::Result{"access returns empty answer"}; + result = lib::Result{error::State{"access returns empty answer"}}; } }; diff --git a/tests/library/result-test.cpp b/tests/library/result-test.cpp index da9c0b441..77fc033ce 100644 --- a/tests/library/result-test.cpp +++ b/tests/library/result-test.cpp @@ -44,7 +44,6 @@ namespace test{ namespace error = lumiera::error; using error::LUMIERA_ERROR_FATAL; - using error::LUMIERA_ERROR_STATE; ///////////////////TODO namespace { @@ -89,12 +88,12 @@ namespace test{ CHECK (not isSameObject (sequel.get(), THE_END)); // »Either Right« case : mark as failure - Result facepalm{error::Fatal()}; + Result facepalm{error::Fatal("zOMG")}; CHECK (not facepalm.isValid()); - VERIFY_ERROR (STATE, (double)facepalm ); - VERIFY_ERROR (STATE, facepalm.get()); - VERIFY_ERROR (STATE, facepalm.maybeThrow() ); + VERIFY_ERROR (FATAL, (double)facepalm ); + VERIFY_ERROR (FATAL, facepalm.get()); + VERIFY_ERROR (FATAL, facepalm.maybeThrow() ); CHECK (42.0 == facepalm.getOrElse([]{ return 42; })); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index ed26b55db..46f9e38f2 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79584,14 +79584,14 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + - - - + + + @@ -79620,6 +79620,26 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + +

+ incl perfect forwarding +

+ +
+ + +
+ + + +
+ +
@@ -79634,7 +79654,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -79660,8 +79680,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
hab das gestern versucht — so kann das nicht stehen bleiben, das ist ja lächerlich (brauche explizite Template-Instantiierung) — zumindest solange wir keine C++-Module verwenden (C++20)

- -
+
@@ -79673,8 +79692,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
die Code-Anordnung hat keinen Flow

- -
+
@@ -79687,8 +79705,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Ich habe doch selber oben diese Prinzipien formuliert: eine Library-Lösung sollte nicht mutwillig beschränken, sondern eine sinnvolle Gliederung anbieten...

- - +
@@ -79703,8 +79720,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
also vielleicht doch Policy-based-Design?

- - + @@ -79722,8 +79738,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
⟹ wenn überhaupt, müßte die Policy einen mittleren Binding-Layer bilden

- - +
@@ -79748,8 +79763,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
Policy ⤵ main<POL,FUN,ARGS...>

- - +
@@ -89176,6 +89190,164 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + + + + + +

+ Wenn man eine Klasse C<T> hat, und einen Konstruktur definiert, der ein T&& - Argument nimmt, dann ist das T bereits durch den Klassen-Templateparameter festgelegt; es kommt in diesem Fall nicht zu einem reference collapsing, sondern es handelt sich einfach um eine rvalue-Referenz T&&. Symptom "can not bind rvalue reference to lvalue" +

+ + +
+
+ + + + + + +

+ ...denn in diesem Fall würde es sich zwar um eine »universelle Referenz« handeln, aber der Zusammenhang zum Klassen-Template-Parameter ist nicht klar +

+ + +
+
+ + + + + + +
    +
  • + steht im enclosing scope nach der Template-Definition +
  • +
  • + syntaktisch eine frei stehende Funktions-Deklaration mit einem trailing return type, welcher die resultierende Template-Spezialisierung konstruieren muß (kann beliebig komplexe Typ-Ausdrücke enthalten) +
  • +
+

+ +

+

+ Beispiel +

+

+   /** deduction guard: allow _perfect forwarding_ of a any result into the ctor call. */ +

+

+   template<typename VAL> +

+

+   Result (VAL&&) -> Result<VAL>; +

+ + +
+ + + + + + + +

+ Wenn ich die Beschreibung in CPPReference.com richtig verstehe, dann würde ohne expliziten deduction guide folgender guide automatisch generiert: +

+

+ +

+

+ template<class X> +

+

+ class C { +

+

+   C(X&& val); +

+

+ }; +

+

+ +

+

+ Auto-Guides: +

+

+ +

+

+ template<class X> +

+

+ C<X> g1(X&& val) +

+

+ +

+

+ template<class X> +

+

+ C<X> g2(C<X>) +

+

+ +

+

+ Hilfs-Deduction+Resolution... +

+

+ +

+

+ struct S { +

+

+   template<class X> +

+

+   S(X&&); +

+

+   template<class X> +

+

+   S(C<X>); +

+

+ }; +

+

+ +

+

+ S = <initializer> +

+

+ +

+

+ +

+

+ Was ich nicht verstehe: der gemäß Regeln auto-generierte Guide sieht exakt genauso aus, wie der Guide, den man manuell definieren muß, damit es funktioniert. Warum wird dann hier ein anderer Typ deduziert? Ich habe Experimente gemacht: ohne den manuellen deduction Guide ergibt sich für einen <initializer> ≔ int&  ⟼ ein deduzierter Parameter X = int  (was dann natürlich scheitert) +

+ + +
+ +
+
+