99#ifndef ORC_RT_ERROR_H
1010#define ORC_RT_ERROR_H
1111
12+ #include " orc-rt-c/config.h"
1213#include " orc-rt/CallableTraitsHelper.h"
1314#include " orc-rt/Compiler.h"
1415#include " orc-rt/RTTI.h"
1920#include < string>
2021#include < type_traits>
2122
23+ #if ORC_RT_ENABLE_EXCEPTIONS
24+ #include < exception>
25+ #endif // ORC_RT_ENABLE_EXCEPTIONS
26+
2227namespace orc_rt {
2328
29+ class Error ;
30+
2431// / Base class for all errors.
2532class ErrorInfoBase : public RTTIExtends <ErrorInfoBase, RTTIRoot> {
2633public:
27- virtual std::string toString () const = 0;
34+ virtual std::string toString () const noexcept = 0;
35+
36+ private:
37+ #if ORC_RT_ENABLE_EXCEPTIONS
38+ friend class Error ;
39+ friend Error restore_error (ErrorInfoBase &&);
40+
41+ virtual void throwAsException () = 0;
42+
43+ virtual Error restoreError () noexcept = 0;
44+ #endif // ORC_RT_ENABLE_EXCEPTIONS
45+ };
46+
47+ // / Like RTTI-extends, but injects error-related helper methods.
48+ template <typename ThisT, typename ParentT>
49+ class ErrorExtends : public ParentT {
50+ public:
51+ static_assert (std::is_base_of_v<ErrorInfoBase, ParentT>,
52+ " ErrorExtends must extend ErrorInfoBase derivatives" );
53+
54+ // Inherit constructors and isA methods from ParentT.
55+ using ParentT::isA;
56+ using ParentT::ParentT;
57+
58+ static char ID;
59+
60+ static const void *classID () noexcept { return &ThisT::ID; }
61+
62+ const void *dynamicClassID () const noexcept override { return &ThisT::ID; }
63+
64+ bool isA (const void *const ClassID) const noexcept override {
65+ return ClassID == classID () || ParentT::isA (ClassID);
66+ }
67+
68+ static bool classof (const RTTIRoot *R) { return R->isA <ThisT>(); }
69+
70+ #if ORC_RT_ENABLE_EXCEPTIONS
71+ void throwAsException () override {
72+ throw ThisT (std::move (static_cast <ThisT &>(*this )));
73+ }
74+
75+ Error restoreError () noexcept override ;
76+ #endif // ORC_RT_ENABLE_EXCEPTIONS
2877};
2978
79+ template <typename ThisT, typename ParentT>
80+ char ErrorExtends<ThisT, ParentT>::ID = 0 ;
81+
3082// / Represents an environmental error.
3183class ORC_RT_NODISCARD Error {
3284
3385 template <typename T> friend class Expected ;
3486
35- friend Error make_error (std::unique_ptr<ErrorInfoBase> Payload);
87+ friend Error make_error (std::unique_ptr<ErrorInfoBase> Payload) noexcept ;
3688
3789 template <typename ... HandlerTs>
3890 friend Error handleErrors (Error E, HandlerTs &&...Hs);
@@ -48,7 +100,7 @@ class ORC_RT_NODISCARD Error {
48100 // / Move-construct an error. The newly constructed error is considered
49101 // / unchecked, even if the source error had been checked. The original error
50102 // / becomes a checked success value.
51- Error (Error &&Other) {
103+ Error (Error &&Other) noexcept {
52104 setChecked (true );
53105 *this = std::move (Other);
54106 }
@@ -57,7 +109,7 @@ class ORC_RT_NODISCARD Error {
57109 // / you cannot overwrite an unhandled error. The current error is then
58110 // / considered unchecked. The source error becomes a checked success value,
59111 // / regardless of its original state.
60- Error &operator =(Error &&Other) {
112+ Error &operator =(Error &&Other) noexcept {
61113 // Don't allow overwriting of unchecked values.
62114 assertIsChecked ();
63115 setPtr (Other.getPtr ());
@@ -73,48 +125,58 @@ class ORC_RT_NODISCARD Error {
73125 }
74126
75127 // / Create a success value.
76- static Error success () { return Error (); }
128+ static Error success () noexcept { return Error (); }
77129
78130 // / Error values convert to true for failure values, false otherwise.
79- explicit operator bool () {
131+ explicit operator bool () noexcept {
80132 setChecked (getPtr () == nullptr );
81133 return getPtr () != nullptr ;
82134 }
83135
84136 // / Return true if this Error contains a failure value of the given type.
85- template <typename ErrT> bool isA () const {
137+ template <typename ErrT> bool isA () const noexcept {
86138 return getPtr () && getPtr ()->isA <ErrT>();
87139 }
88140
141+ #if ORC_RT_ENABLE_EXCEPTIONS
142+ void throwOnFailure () {
143+ if (auto P = takePayload ())
144+ P->throwAsException ();
145+ }
146+ #endif // ORC_RT_ENABLE_EXCEPTIONS
147+
89148private:
90- Error () = default ;
149+ Error () noexcept = default ;
91150
92- Error (std::unique_ptr<ErrorInfoBase> ErrInfo) {
151+ Error (std::unique_ptr<ErrorInfoBase> ErrInfo) noexcept {
93152 auto RawErrPtr = reinterpret_cast <uintptr_t >(ErrInfo.release ());
94153 assert ((RawErrPtr & 0x1 ) == 0 && " ErrorInfo is insufficiently aligned" );
95154 ErrPtr = RawErrPtr | 0x1 ;
96155 }
97156
98- void assertIsChecked () {
157+ void assertIsChecked () noexcept {
99158 if (ORC_RT_UNLIKELY (!isChecked () || getPtr ())) {
100159 fprintf (stderr, " Error must be checked prior to destruction.\n " );
101160 abort (); // Some sort of JIT program abort?
102161 }
103162 }
104163
105- template <typename ErrT = ErrorInfoBase> ErrT *getPtr () const {
164+ template <typename ErrT = ErrorInfoBase> ErrT *getPtr () const noexcept {
106165 return reinterpret_cast <ErrT *>(ErrPtr & ~uintptr_t (1 ));
107166 }
108167
109- void setPtr (ErrorInfoBase *Ptr) {
168+ void setPtr (ErrorInfoBase *Ptr) noexcept {
110169 ErrPtr = (reinterpret_cast <uintptr_t >(Ptr) & ~uintptr_t (1 )) | (ErrPtr & 1 );
111170 }
112171
113- bool isChecked () const { return ErrPtr & 0x1 ; }
172+ bool isChecked () const noexcept { return ErrPtr & 0x1 ; }
114173
115- void setChecked (bool Checked) { ErrPtr = (ErrPtr & ~uintptr_t (1 )) | Checked; }
174+ void setChecked (bool Checked) noexcept {
175+ ErrPtr = (ErrPtr & ~uintptr_t (1 )) | Checked;
176+ }
116177
117- template <typename ErrT = ErrorInfoBase> std::unique_ptr<ErrT> takePayload () {
178+ template <typename ErrT = ErrorInfoBase>
179+ std::unique_ptr<ErrT> takePayload () noexcept {
118180 static_assert (std::is_base_of_v<ErrorInfoBase, ErrT>,
119181 " ErrT is not an ErrorInfoBase subclass" );
120182 std::unique_ptr<ErrT> Tmp (getPtr<ErrT>());
@@ -127,10 +189,22 @@ class ORC_RT_NODISCARD Error {
127189};
128190
129191// / Create an Error from an ErrorInfoBase.
130- inline Error make_error (std::unique_ptr<ErrorInfoBase> Payload) {
192+ inline Error make_error (std::unique_ptr<ErrorInfoBase> Payload) noexcept {
131193 return Error (std::move (Payload));
132194}
133195
196+ #if ORC_RT_ENABLE_EXCEPTIONS
197+
198+ template <typename ThisT, typename ParentT>
199+ Error ErrorExtends<ThisT, ParentT>::restoreError() noexcept {
200+ return make_error (
201+ std::make_unique<ThisT>(std::move (*static_cast <ThisT *>(this ))));
202+ }
203+
204+ inline Error restore_error (ErrorInfoBase &&EIB) { return EIB.restoreError (); }
205+
206+ #endif // ORC_RT_ENABLE_EXCEPTIONS
207+
134208// / Construct an error of ErrT with the given arguments.
135209template <typename ErrT, typename ... ArgTs> Error make_error (ArgTs &&...Args) {
136210 static_assert (std::is_base_of<ErrorInfoBase, ErrT>::value,
@@ -497,7 +571,7 @@ template <typename T> T &cantFail(Expected<T &> E) {
497571
498572// / Convert the given error to a string. The error value is consumed in the
499573// / process.
500- inline std::string toString (Error Err) {
574+ inline std::string toString (Error Err) noexcept {
501575 assert (Err && " Cannot convert success value to string" );
502576 std::string ErrMsg;
503577 handleAllErrors (std::move (Err),
@@ -506,15 +580,111 @@ inline std::string toString(Error Err) {
506580}
507581
508582// / Simple string error type.
509- class StringError : public RTTIExtends <StringError, ErrorInfoBase> {
583+ class StringError : public ErrorExtends <StringError, ErrorInfoBase> {
510584public:
511585 StringError (std::string ErrMsg) : ErrMsg(std::move(ErrMsg)) {}
512- std::string toString () const override { return ErrMsg; }
586+ std::string toString () const noexcept override { return ErrMsg; }
513587
514588private:
515589 std::string ErrMsg;
516590};
517591
592+ // / APIs for C++ exception interop.
593+ #if ORC_RT_ENABLE_EXCEPTIONS
594+
595+ class ExceptionError : public ErrorExtends <ExceptionError, ErrorInfoBase> {
596+ public:
597+ ExceptionError (std::exception_ptr E) : E(std::move(E)) {}
598+ std::string toString () const noexcept override ;
599+ void throwAsException () override { std::rethrow_exception (E); }
600+
601+ private:
602+ mutable std::exception_ptr E;
603+ };
604+
605+ namespace detail {
606+
607+ // In general we need to wrap a return type of T with an Expected.
608+ template <typename RetT> struct ErrorWrapImpl {
609+ typedef Expected<RetT> return_type;
610+
611+ template <typename OpFn> static return_type run (OpFn &&Op) { return Op (); }
612+ };
613+
614+ // If the return is already an Expected value then we don't need to add
615+ // an additional level of wrapping.
616+ template <typename RetT> struct ErrorWrapImpl <Expected<RetT>> {
617+ typedef Expected<RetT> return_type;
618+
619+ template <typename OpFn> static return_type run (OpFn &&Op) { return Op (); }
620+ };
621+
622+ // Errors stay errors.
623+ template <> struct ErrorWrapImpl <Error> {
624+ typedef Error return_type;
625+
626+ template <typename OpFn> static return_type run (OpFn &&Op) { return Op (); }
627+ };
628+
629+ // void returns become Error returns.
630+ template <> struct ErrorWrapImpl <void > {
631+ typedef Error return_type;
632+
633+ template <typename OpFn> static return_type run (OpFn &&Op) {
634+ Op ();
635+ return Error::success ();
636+ }
637+ };
638+
639+ template <typename Callable>
640+ struct ErrorWrap
641+ : public CallableTraitsHelper<detail::ErrorWrapImpl, Callable> {};
642+
643+ } // namespace detail
644+
645+ // / Run the given callback capturing any exceptions thrown into an
646+ // / Error / Expected failure value.
647+ // /
648+ // / The return type depends on the return type of the callback:
649+ // / - void callbacks return Error
650+ // / - Error callbacks return Error
651+ // / - Expected<T> callbacks return Expected<T>
652+ // / - other T callbacks return Expected<T>
653+ // /
654+ // / If the operation succeeds then...
655+ // / - If its result is non-void it is returned as an Expected<T> success
656+ // / value
657+ // / - If its result is void then Error::success() is retured
658+ // /
659+ // / If the operation fails then...
660+ // / - If the exception type is std::unique_ptr<ErrorInfoBase> (i.e. a throw
661+ // / orc_rt failure value) then an Error is constructed to hold the
662+ // / failure value.
663+ // / - If the exception has any other type then it's captured as an
664+ // / ExceptionError.
665+ // /
666+ // / The scheme allaws...
667+ // / 1. orc_rt::Error values that have been converted to exceptions via
668+ // / Error::throwOnFailure to be converted back into Errors without loss
669+ // / of dynamic type info.
670+ // / 2. Other Exceptions caught by this function to be converted back into
671+ // / exceptions via Error::throwOnFailure without loss of dynamic
672+ // / type info.
673+
674+ template <typename OpFn>
675+ typename detail::ErrorWrap<OpFn>::return_type
676+ runCapturingExceptions (OpFn &&Op) noexcept {
677+ try {
678+ return detail::ErrorWrap<OpFn>::run (std::forward<OpFn>(Op));
679+ } catch (ErrorInfoBase &EIB) {
680+ return restore_error (std::move (EIB));
681+ } catch (...) {
682+ return make_error<ExceptionError>(std::current_exception ());
683+ }
684+ }
685+
686+ #endif // ORC_RT_ENABLE_EXCEPTIONS
687+
518688} // namespace orc_rt
519689
520690#endif // ORC_RT_ERROR_H
0 commit comments