|
||||
File indexing completed on 2025-01-30 10:22:27
0001 /// \file ROOT/RError.hxx 0002 /// \ingroup Base ROOT7 0003 /// \author Jakob Blomer <jblomer@cern.ch> 0004 /// \date 2019-12-11 0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback 0006 /// is welcome! 0007 0008 /************************************************************************* 0009 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. * 0010 * All rights reserved. * 0011 * * 0012 * For the licensing terms see $ROOTSYS/LICENSE. * 0013 * For the list of contributors see $ROOTSYS/README/CREDITS. * 0014 *************************************************************************/ 0015 0016 #ifndef ROOT7_RError 0017 #define ROOT7_RError 0018 0019 #include <ROOT/RConfig.hxx> // for R__[un]likely 0020 #include <ROOT/RLogger.hxx> // for R__LOG_PRETTY_FUNCTION 0021 0022 #include <cstddef> 0023 #include <memory> 0024 #include <new> 0025 #include <stdexcept> 0026 #include <string> 0027 #include <utility> 0028 #include <vector> 0029 0030 namespace ROOT { 0031 namespace Experimental { 0032 0033 // clang-format off 0034 /** 0035 \class ROOT::Experimental::RError 0036 \ingroup Base 0037 \brief Captures diagnostics related to a ROOT runtime error 0038 */ 0039 // clang-format on 0040 class RError { 0041 public: 0042 struct RLocation { 0043 RLocation() = default; 0044 RLocation(const char *func, const char *file, int line) 0045 : fFunction(func), fSourceFile(file), fSourceLine(line) {} 0046 0047 // TODO(jblomer) use std::source_location once available 0048 const char *fFunction; 0049 const char *fSourceFile; 0050 int fSourceLine; 0051 }; 0052 0053 private: 0054 /// User-facing error message 0055 std::string fMessage; 0056 /// The location of the error related to fMessage plus upper frames if the error is forwarded through the call stack 0057 std::vector<RLocation> fStackTrace; 0058 0059 public: 0060 /// Used by R__FAIL 0061 RError(const std::string &message, RLocation &&sourceLocation); 0062 /// Used by R__FORWARD_RESULT 0063 void AddFrame(RLocation &&sourceLocation); 0064 /// Add more information to the diagnostics 0065 void AppendToMessage(const std::string &info) { fMessage += info; } 0066 /// Format a dignostics report, e.g. for an exception message 0067 std::string GetReport() const; 0068 const std::vector<RLocation> &GetStackTrace() const { return fStackTrace; } 0069 }; 0070 0071 // clang-format off 0072 /** 0073 \class ROOT::Experimental::RException 0074 \ingroup Base 0075 \brief Base class for all ROOT issued exceptions 0076 */ 0077 // clang-format on 0078 class RException : public std::runtime_error { 0079 RError fError; 0080 public: 0081 explicit RException(const RError &error) : std::runtime_error(error.GetReport()), fError(error) {} 0082 const RError &GetError() const { return fError; } 0083 }; 0084 0085 // clang-format off 0086 /** 0087 \class ROOT::Experimental::RResultBase 0088 \ingroup Base 0089 \brief Common handling of the error case for RResult<T> (T != void) and RResult<void> 0090 0091 RResultBase captures a possible runtime error that might have occured. If the RResultBase leaves the scope unchecked, 0092 it will throw an exception. RResultBase should only be allocated on the stack, which is helped by deleting the 0093 new operator. RResultBase is movable but not copyable to avoid throwing multiple exceptions about the same failure. 0094 */ 0095 // clang-format on 0096 class RResultBase { 0097 protected: 0098 /// This is the nullptr for an RResult representing success 0099 std::unique_ptr<RError> fError; 0100 /// Switches to true once the user of an RResult object checks the object status 0101 bool fIsChecked{false}; 0102 0103 RResultBase() = default; 0104 explicit RResultBase(RError &&error) : fError(std::make_unique<RError>(std::move(error))) {} 0105 0106 /// Used by the RResult<T> bool operator 0107 bool Check() { 0108 fIsChecked = true; 0109 return !fError; 0110 } 0111 0112 public: 0113 RResultBase(const RResultBase &other) = delete; 0114 RResultBase(RResultBase &&other) = default; 0115 RResultBase &operator =(const RResultBase &other) = delete; 0116 RResultBase &operator =(RResultBase &&other) = default; 0117 0118 ~RResultBase() noexcept(false); 0119 0120 RError *GetError() { return fError.get(); } 0121 /// Throws an RException with fError 0122 void Throw(); 0123 0124 /// Used by R__FORWARD_ERROR in order to keep track of the stack trace. 0125 static RError ForwardError(RResultBase &&result, RError::RLocation &&sourceLocation) { 0126 if (!result.fError) { 0127 return RError("internal error: attempt to forward error of successful operation", 0128 std::move(sourceLocation)); 0129 } 0130 result.fError->AddFrame(std::move(sourceLocation)); 0131 return *result.fError; 0132 } 0133 }; // class RResultBase 0134 0135 // clang-format off 0136 /** 0137 \class ROOT::Experimental::RResult 0138 \ingroup Base 0139 \brief The class is used as a return type for operations that can fail; wraps a value of type T or an RError 0140 0141 The RResult<T> class and their related classes are used for call chains that can throw exceptions, 0142 such as I/O code paths. Throwing of the exception is deferred to allow for `if (result)` style error 0143 checking where it makes sense. If an RResult in error state leaves the scope unchecked, it will throw. 0144 0145 A function returning an RResult might look like this: 0146 0147 ~~~ {.cpp} 0148 RResult<int> MyIOFunc() 0149 { 0150 int rv = syscall(...); 0151 if (rv == -1) 0152 return R__FAIL("user-facing error message"); 0153 if (rv == kShortcut) 0154 return 42; 0155 return R__FORWARD_RESULT(FuncThatReturnsRResultOfInt()); 0156 } 0157 ~~~ 0158 0159 Code using MyIOFunc might look like this: 0160 0161 ~~~ {.cpp} 0162 auto result = MyIOOperation(); 0163 if (!result) { 0164 // custom error handling or result.Throw() 0165 } 0166 switch (result.Inspect()) { 0167 ... 0168 } 0169 ~~~ 0170 0171 Note that RResult<void> can be used for a function without return value, like this 0172 0173 ~~~ {.cpp} 0174 RResult<void> DoSomething() 0175 { 0176 if (failure) 0177 return R__FAIL("user-facing error messge"); 0178 return RResult<void>::Success(); 0179 } 0180 ~~~ 0181 0182 RResult<T>::Unwrap() can be used as a short hand for 0183 "give me the wrapped value or, in case of an error, throw". For instance: 0184 0185 ~~~ {.cpp} 0186 int value = FuncThatReturnsRResultOfInt().Unwrap(); // may throw 0187 ~~~ 0188 0189 There is no implict operator that converts RResult<T> to T. This is intentional to make it clear in the calling code 0190 where an exception may be thrown. 0191 */ 0192 // clang-format on 0193 template <typename T> 0194 class RResult : public RResultBase { 0195 private: 0196 /// The result value in case of successful execution 0197 T fValue; 0198 0199 // Ensure accessor methods throw in case of errors 0200 inline void ThrowOnError() { 0201 if (R__unlikely(fError)) { 0202 // Accessors can be wrapped in a try-catch block, so throwing the 0203 // exception here is akin to checking the error. 0204 // 0205 // Setting fIsChecked to true also avoids a spurious warning in the RResult destructor 0206 fIsChecked = true; 0207 0208 fError->AppendToMessage(" (unchecked RResult access!)"); 0209 throw RException(*fError); 0210 } 0211 } 0212 0213 public: 0214 RResult(const T &value) : fValue(value) {} 0215 RResult(T &&value) : fValue(std::move(value)) {} 0216 RResult(RError &&error) : RResultBase(std::move(error)) {} 0217 0218 RResult(const RResult &other) = delete; 0219 RResult(RResult &&other) = default; 0220 RResult &operator =(const RResult &other) = delete; 0221 RResult &operator =(RResult &&other) = default; 0222 0223 ~RResult() = default; 0224 0225 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors 0226 RResult &Forward(RError::RLocation &&sourceLocation) { 0227 if (fError) 0228 fError->AddFrame(std::move(sourceLocation)); 0229 return *this; 0230 } 0231 0232 /// If the operation was successful, returns a const reference to the inner type. 0233 /// If there was an error, Inspect() instead throws an exception. 0234 const T &Inspect() { 0235 ThrowOnError(); 0236 return fValue; 0237 } 0238 0239 /// If the operation was successful, returns the inner type by value. 0240 /// 0241 /// For move-only types, Unwrap can only be called once, as it yields ownership of 0242 /// the inner value to the caller using std::move, potentially leaving the 0243 /// RResult in an unspecified state. 0244 /// 0245 /// If there was an error, Unwrap() instead throws an exception. 0246 T Unwrap() { 0247 ThrowOnError(); 0248 return std::move(fValue); 0249 } 0250 0251 explicit operator bool() { return Check(); } 0252 }; 0253 0254 /// RResult<void> has no data member and no Inspect() method but instead a Success() factory method 0255 template <> 0256 class RResult<void> : public RResultBase { 0257 private: 0258 RResult() = default; 0259 0260 public: 0261 /// Returns a RResult<void> that captures the successful execution of the function 0262 static RResult Success() { return RResult(); } 0263 RResult(RError &&error) : RResultBase(std::move(error)) {} 0264 0265 RResult(const RResult &other) = delete; 0266 RResult(RResult &&other) = default; 0267 RResult &operator =(const RResult &other) = delete; 0268 RResult &operator =(RResult &&other) = default; 0269 0270 ~RResult() = default; 0271 0272 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors 0273 RResult &Forward(RError::RLocation &&sourceLocation) { 0274 if (fError) 0275 fError->AddFrame(std::move(sourceLocation)); 0276 return *this; 0277 } 0278 0279 /// Short-hand method to throw an exception in the case of errors. Does nothing for 0280 /// successful RResults. 0281 void ThrowOnError() { 0282 if (!Check()) 0283 Throw(); 0284 } 0285 0286 explicit operator bool() { return Check(); } 0287 }; 0288 0289 /// Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult<T> 0290 #define R__FAIL(msg) ROOT::Experimental::RError(msg, {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__}) 0291 /// Short-hand to return an RResult<T> value from a subroutine to the calling stack frame 0292 #define R__FORWARD_RESULT(res) std::move(res.Forward({R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})) 0293 /// Short-hand to return an RResult<T> in an error state (i.e. after checking) 0294 #define R__FORWARD_ERROR(res) res.ForwardError(std::move(res), {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__}) 0295 } // namespace Experimental 0296 } // namespace ROOT 0297 0298 #endif
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |