Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-05 08:50:52

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    [[nodiscard]]
0129    static RError ForwardError(RResultBase &&result, RError::RLocation &&sourceLocation)
0130    {
0131       if (!result.fError) {
0132          return RError("internal error: attempt to forward error of successful operation", std::move(sourceLocation));
0133       }
0134       result.fError->AddFrame(std::move(sourceLocation));
0135       return *result.fError;
0136    }
0137 }; // class RResultBase
0138 
0139 // clang-format off
0140 /**
0141 \class ROOT::RResult
0142 \ingroup Base
0143 \brief The class is used as a return type for operations that can fail; wraps a value of type T or an RError
0144 
0145 The RResult<T> class and their related classes are used for call chains that can throw exceptions,
0146 such as I/O code paths.  Throwing of the exception is deferred to allow for `if (result)` style error
0147 checking where it makes sense.  If an RResult in error state leaves the scope unchecked, it will throw.
0148 
0149 A function returning an RResult might look like this:
0150 
0151 ~~~ {.cpp}
0152 RResult<int> MyIOFunc()
0153 {
0154    int rv = syscall(...);
0155    if (rv == -1)
0156       return R__FAIL("user-facing error message");
0157    if (rv == kShortcut)
0158       return 42;
0159    return R__FORWARD_RESULT(FuncThatReturnsRResultOfInt());
0160 }
0161 ~~~
0162 
0163 Code using MyIOFunc might look like this:
0164 
0165 ~~~ {.cpp}
0166 auto result = MyIOOperation();
0167 if (!result) {
0168    // custom error handling or result.Throw()
0169 }
0170 switch (result.Inspect()) {
0171    ...
0172 }
0173 ~~~
0174 
0175 Note that RResult<void> can be used for a function without return value, like this
0176 
0177 ~~~ {.cpp}
0178 RResult<void> DoSomething()
0179 {
0180    if (failure)
0181       return R__FAIL("user-facing error messge");
0182    return RResult<void>::Success();
0183 }
0184 ~~~
0185 
0186 RResult<T>::Unwrap() can be used as a short hand for
0187 "give me the wrapped value or, in case of an error, throw". For instance:
0188 
0189 ~~~ {.cpp}
0190 int value = FuncThatReturnsRResultOfInt().Unwrap();  // may throw
0191 ~~~
0192 
0193 There is no implicit operator that converts RResult<T> to T. This is intentional to make it clear in the calling code
0194 where an exception may be thrown.
0195 */
0196 // clang-format on
0197 template <typename T>
0198 class RResult : public RResultBase {
0199 private:
0200    /// The result value, only present in case of successful execution.
0201    std::optional<T> fValue;
0202 
0203    // Ensure accessor methods throw in case of errors
0204    inline void ThrowOnError()
0205    {
0206       if (R__unlikely(fError)) {
0207          // Accessors can be wrapped in a try-catch block, so throwing the
0208          // exception here is akin to checking the error.
0209          //
0210          // Setting fIsChecked to true also avoids a spurious warning in the RResult destructor
0211          fIsChecked = true;
0212 
0213          fError->AppendToMessage(" (unchecked RResult access!)");
0214          throw RException(*fError);
0215       }
0216    }
0217 
0218 public:
0219    RResult(const T &value) : fValue(value) {}
0220    RResult(T &&value) : fValue(std::move(value)) {}
0221    RResult(RError &&error) : RResultBase(std::move(error)) {}
0222 
0223    RResult(const RResult &other) = delete;
0224    RResult(RResult &&other) = default;
0225    RResult &operator=(const RResult &other) = delete;
0226    RResult &operator=(RResult &&other) = default;
0227 
0228    ~RResult() = default;
0229 
0230    /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
0231    RResult &Forward(RError::RLocation &&sourceLocation)
0232    {
0233       if (fError)
0234          fError->AddFrame(std::move(sourceLocation));
0235       return *this;
0236    }
0237 
0238    /// If the operation was successful, returns a const reference to the inner type.
0239    /// If there was an error, Inspect() instead throws an exception.
0240    const T &Inspect()
0241    {
0242       ThrowOnError();
0243       return *fValue;
0244    }
0245 
0246    /// If the operation was successful, returns the inner type by value.
0247    ///
0248    /// For move-only types, Unwrap can only be called once, as it yields ownership of
0249    /// the inner value to the caller using std::move, potentially leaving the
0250    /// RResult in an unspecified state.
0251    ///
0252    /// If there was an error, Unwrap() instead throws an exception.
0253    T Unwrap()
0254    {
0255       ThrowOnError();
0256       return std::move(*fValue);
0257    }
0258 
0259    explicit operator bool() { return Check(); }
0260 };
0261 
0262 /// RResult<void> has no data member and no Inspect() method but instead a Success() factory method
0263 template <>
0264 class RResult<void> : public RResultBase {
0265 private:
0266    RResult() = default;
0267 
0268 public:
0269    /// Returns a RResult<void> that captures the successful execution of the function
0270    static RResult Success() { return RResult(); }
0271    RResult(RError &&error) : RResultBase(std::move(error)) {}
0272 
0273    RResult(const RResult &other) = delete;
0274    RResult(RResult &&other) = default;
0275    RResult &operator=(const RResult &other) = delete;
0276    RResult &operator=(RResult &&other) = default;
0277 
0278    ~RResult() = default;
0279 
0280    /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
0281    RResult &Forward(RError::RLocation &&sourceLocation)
0282    {
0283       if (fError)
0284          fError->AddFrame(std::move(sourceLocation));
0285       return *this;
0286    }
0287 
0288    /// Short-hand method to throw an exception in the case of errors. Does nothing for
0289    /// successful RResults.
0290    void ThrowOnError()
0291    {
0292       if (!Check())
0293          Throw();
0294    }
0295 
0296    explicit operator bool() { return Check(); }
0297 };
0298 
0299 /// Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult<T>
0300 #define R__FAIL(msg) ROOT::RError(msg, {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
0301 /// Short-hand to return an RResult<T> value from a subroutine to the calling stack frame
0302 #define R__FORWARD_RESULT(res) std::move(res.Forward({R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__}))
0303 /// Short-hand to return an RResult<T> in an error state (i.e. after checking)
0304 #define R__FORWARD_ERROR(res) res.ForwardError(std::move(res), {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
0305 
0306 } // namespace ROOT
0307 
0308 #endif