Back to home page

EIC code displayed by LXR

 
 

    


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