Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RError.hxx
Go to the documentation of this file.
1/// \file ROOT/RError.hxx
2/// \ingroup Base
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2019-12-11
5
6/*************************************************************************
7 * Copyright (C) 1995-2020, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#ifndef ROOT_RError
15#define ROOT_RError
16
17#include <ROOT/RConfig.hxx> // for R__[un]likely
18#include <ROOT/RLogger.hxx> // for R__LOG_PRETTY_FUNCTION
19
20#include <cstddef>
21#include <memory>
22#include <new>
23#include <optional>
24#include <stdexcept>
25#include <string>
26#include <string_view>
27#include <utility>
28#include <vector>
29
30namespace ROOT {
31
32// clang-format off
33/**
34\class ROOT::RError
35\ingroup Base
36\brief Captures diagnostics related to a ROOT runtime error
37*/
38// clang-format on
39class RError {
40public:
41 struct RLocation {
42 RLocation() = default;
43 RLocation(const char *func, const char *file, unsigned int line)
44 : fFunction(func), fSourceFile(file), fSourceLine(line)
45 {
46 }
47
48 // TODO(jblomer) use std::source_location as of C++20
49 const char *fFunction;
50 const char *fSourceFile;
51 unsigned int fSourceLine;
52 };
53
54private:
55 /// User-facing error message
56 std::string fMessage;
57 /// The location of the error related to fMessage plus upper frames if the error is forwarded through the call stack
58 std::vector<RLocation> fStackTrace;
59
60public:
61 /// Used by R__FAIL
62 RError(std::string_view message, RLocation &&sourceLocation);
63 /// Used by R__FORWARD_RESULT
65 /// Add more information to the diagnostics
66 void AppendToMessage(std::string_view info) { fMessage += info; }
67 /// Format a dignostics report, e.g. for an exception message
68 std::string GetReport() const;
69 const std::vector<RLocation> &GetStackTrace() const { return fStackTrace; }
70};
71
72// clang-format off
73/**
74\class ROOT::RException
75\ingroup Base
76\brief Base class for all ROOT issued exceptions
77*/
78// clang-format on
79class RException : public std::runtime_error {
81
82public:
83 explicit RException(const RError &error) : std::runtime_error(error.GetReport()), fError(error) {}
84 const RError &GetError() const { return fError; }
85};
86
87// clang-format off
88/**
89\class ROOT::RResultBase
90\ingroup Base
91\brief Common handling of the error case for RResult<T> (T != void) and RResult<void>
92
93RResultBase captures a possible runtime error that might have occured. If the RResultBase leaves the scope unchecked,
94it will throw an exception. RResultBase should only be allocated on the stack, which is helped by deleting the
95new operator. RResultBase is movable but not copyable to avoid throwing multiple exceptions about the same failure.
96*/
97// clang-format on
99protected:
100 /// This is the nullptr for an RResult representing success
101 std::unique_ptr<RError> fError;
102 /// Switches to true once the user of an RResult object checks the object status
103 bool fIsChecked{false};
104
105 RResultBase() = default;
106 explicit RResultBase(RError &&error) : fError(std::make_unique<RError>(std::move(error))) {}
107
108 /// Used by the RResult<T> bool operator
109 bool Check()
110 {
111 fIsChecked = true;
112 return !fError;
113 }
114
115public:
116 RResultBase(const RResultBase &other) = delete;
120
122
123 std::optional<RError> GetError() const { return fError ? *fError : std::optional<RError>(); }
124 /// Throws an RException with fError
125 void Throw();
126
127 /// Used by R__FORWARD_ERROR in order to keep track of the stack trace.
128 [[nodiscard]]
130 {
131 if (!result.fError) {
132 return RError("internal error: attempt to forward error of successful operation", std::move(sourceLocation));
133 }
134 result.fError->AddFrame(std::move(sourceLocation));
135 return *result.fError;
136 }
137}; // class RResultBase
138
139// clang-format off
140/**
141\class ROOT::RResult
142\ingroup Base
143\brief The class is used as a return type for operations that can fail; wraps a value of type T or an RError
144
145The RResult<T> class and their related classes are used for call chains that can throw exceptions,
146such as I/O code paths. Throwing of the exception is deferred to allow for `if (result)` style error
147checking where it makes sense. If an RResult in error state leaves the scope unchecked, it will throw.
148
149A function returning an RResult might look like this:
150
151~~~ {.cpp}
152RResult<int> MyIOFunc()
153{
154 int rv = syscall(...);
155 if (rv == -1)
156 return R__FAIL("user-facing error message");
157 if (rv == kShortcut)
158 return 42;
159 return R__FORWARD_RESULT(FuncThatReturnsRResultOfInt());
160}
161~~~
162
163Code using MyIOFunc might look like this:
164
165~~~ {.cpp}
166auto result = MyIOOperation();
167if (!result) {
168 // custom error handling or result.Throw()
169}
170switch (result.Inspect()) {
171 ...
172}
173~~~
174
175Note that RResult<void> can be used for a function without return value, like this
176
177~~~ {.cpp}
178RResult<void> DoSomething()
179{
180 if (failure)
181 return R__FAIL("user-facing error messge");
182 return RResult<void>::Success();
183}
184~~~
185
186RResult<T>::Unwrap() can be used as a short hand for
187"give me the wrapped value or, in case of an error, throw". For instance:
188
189~~~ {.cpp}
190int value = FuncThatReturnsRResultOfInt().Unwrap(); // may throw
191~~~
192
193There is no implicit operator that converts RResult<T> to T. This is intentional to make it clear in the calling code
194where an exception may be thrown.
195*/
196// clang-format on
197template <typename T>
198class RResult : public RResultBase {
199private:
200 /// The result value, only present in case of successful execution.
201 std::optional<T> fValue;
202
203 // Ensure accessor methods throw in case of errors
204 inline void ThrowOnError()
205 {
206 if (R__unlikely(fError)) {
207 // Accessors can be wrapped in a try-catch block, so throwing the
208 // exception here is akin to checking the error.
209 //
210 // Setting fIsChecked to true also avoids a spurious warning in the RResult destructor
211 fIsChecked = true;
212
213 fError->AppendToMessage(" (unchecked RResult access!)");
214 throw RException(*fError);
215 }
216 }
217
218public:
219 RResult(const T &value) : fValue(value) {}
220 RResult(T &&value) : fValue(std::move(value)) {}
221 RResult(RError &&error) : RResultBase(std::move(error)) {}
222
223 RResult(const RResult &other) = delete;
224 RResult(RResult &&other) = default;
225 RResult &operator=(const RResult &other) = delete;
227
228 ~RResult() = default;
229
230 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
232 {
233 if (fError)
234 fError->AddFrame(std::move(sourceLocation));
235 return *this;
236 }
237
238 /// If the operation was successful, returns a const reference to the inner type.
239 /// If there was an error, Inspect() instead throws an exception.
240 const T &Inspect()
241 {
242 ThrowOnError();
243 return *fValue;
244 }
245
246 /// If the operation was successful, returns the inner type by value.
247 ///
248 /// For move-only types, Unwrap can only be called once, as it yields ownership of
249 /// the inner value to the caller using std::move, potentially leaving the
250 /// RResult in an unspecified state.
251 ///
252 /// If there was an error, Unwrap() instead throws an exception.
254 {
255 ThrowOnError();
256 return std::move(*fValue);
257 }
258
259 explicit operator bool() { return Check(); }
260};
261
262/// RResult<void> has no data member and no Inspect() method but instead a Success() factory method
263template <>
264class RResult<void> : public RResultBase {
265private:
266 RResult() = default;
267
268public:
269 /// Returns a RResult<void> that captures the successful execution of the function
270 static RResult Success() { return RResult(); }
271 RResult(RError &&error) : RResultBase(std::move(error)) {}
272
273 RResult(const RResult &other) = delete;
274 RResult(RResult &&other) = default;
275 RResult &operator=(const RResult &other) = delete;
277
278 ~RResult() = default;
279
280 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
282 {
283 if (fError)
284 fError->AddFrame(std::move(sourceLocation));
285 return *this;
286 }
287
288 /// Short-hand method to throw an exception in the case of errors. Does nothing for
289 /// successful RResults.
291 {
292 if (!Check())
293 Throw();
294 }
295
296 explicit operator bool() { return Check(); }
297};
298
299/// Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult<T>
300#define R__FAIL(msg) ROOT::RError(msg, {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
301/// Short-hand to return an RResult<T> value from a subroutine to the calling stack frame
302#define R__FORWARD_RESULT(res) std::move(res.Forward({R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__}))
303/// Short-hand to return an RResult<T> in an error state (i.e. after checking)
304#define R__FORWARD_ERROR(res) res.ForwardError(std::move(res), {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
305
306} // namespace ROOT
307
308#endif
#define R__unlikely(expr)
Definition RConfig.hxx:594
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Captures diagnostics related to a ROOT runtime error.
Definition RError.hxx:39
RError(std::string_view message, RLocation &&sourceLocation)
Used by R__FAIL.
Definition RError.cxx:32
void AddFrame(RLocation &&sourceLocation)
Used by R__FORWARD_RESULT.
Definition RError.cxx:40
std::string fMessage
User-facing error message.
Definition RError.hxx:56
std::vector< RLocation > fStackTrace
The location of the error related to fMessage plus upper frames if the error is forwarded through the...
Definition RError.hxx:58
std::string GetReport() const
Format a dignostics report, e.g. for an exception message.
Definition RError.cxx:22
void AppendToMessage(std::string_view info)
Add more information to the diagnostics.
Definition RError.hxx:66
const std::vector< RLocation > & GetStackTrace() const
Definition RError.hxx:69
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
RException(const RError &error)
Definition RError.hxx:83
const RError & GetError() const
Definition RError.hxx:84
Common handling of the error case for RResult<T> (T != void) and RResult<void>
Definition RError.hxx:98
RResultBase & operator=(RResultBase &&other)=default
~RResultBase() noexcept(false)
Definition RError.cxx:45
void Throw()
Throws an RException with fError.
Definition RError.cxx:62
RResultBase(RResultBase &&other)=default
static RError ForwardError(RResultBase &&result, RError::RLocation &&sourceLocation)
Used by R__FORWARD_ERROR in order to keep track of the stack trace.
Definition RError.hxx:129
bool Check()
Used by the RResult<T> bool operator.
Definition RError.hxx:109
RResultBase(const RResultBase &other)=delete
RResultBase(RError &&error)
Definition RError.hxx:106
RResultBase & operator=(const RResultBase &other)=delete
RResultBase()=default
bool fIsChecked
Switches to true once the user of an RResult object checks the object status.
Definition RError.hxx:103
std::optional< RError > GetError() const
Definition RError.hxx:123
std::unique_ptr< RError > fError
This is the nullptr for an RResult representing success.
Definition RError.hxx:101
void ThrowOnError()
Short-hand method to throw an exception in the case of errors.
Definition RError.hxx:290
RResult(RError &&error)
Definition RError.hxx:271
RResult & operator=(RResult &&other)=default
RResult & operator=(const RResult &other)=delete
RResult(const RResult &other)=delete
RResult(RResult &&other)=default
static RResult Success()
Returns a RResult<void> that captures the successful execution of the function.
Definition RError.hxx:270
RResult & Forward(RError::RLocation &&sourceLocation)
Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors.
Definition RError.hxx:281
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:198
RResult(RError &&error)
Definition RError.hxx:221
T Unwrap()
If the operation was successful, returns the inner type by value.
Definition RError.hxx:253
RResult(RResult &&other)=default
RResult & Forward(RError::RLocation &&sourceLocation)
Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors.
Definition RError.hxx:231
RResult(T &&value)
Definition RError.hxx:220
RResult(const RResult &other)=delete
void ThrowOnError()
Definition RError.hxx:204
RResult(const T &value)
Definition RError.hxx:219
RResult & operator=(RResult &&other)=default
std::optional< T > fValue
The result value, only present in case of successful execution.
Definition RError.hxx:201
~RResult()=default
const T & Inspect()
If the operation was successful, returns a const reference to the inner type.
Definition RError.hxx:240
RResult & operator=(const RResult &other)=delete
TLine * line
Namespace for new ROOT classes and functions.
const char * fSourceFile
Definition RError.hxx:50
const char * fFunction
Definition RError.hxx:49
RLocation(const char *func, const char *file, unsigned int line)
Definition RError.hxx:43
unsigned int fSourceLine
Definition RError.hxx:51