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