Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-13 09:10:12

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