Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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
64 void AddFrame(RLocation &&sourceLocation);
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;
117 RResultBase(RResultBase &&other) = default;
118 RResultBase &operator=(const RResultBase &other) = delete;
119 RResultBase &operator=(RResultBase &&other) = default;
120
121 ~RResultBase() noexcept(false);
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.
129 {
130 if (!result.fError) {
131 return RError("internal error: attempt to forward error of successful operation", std::move(sourceLocation));
132 }
133 result.fError->AddFrame(std::move(sourceLocation));
134 return *result.fError;
135 }
136}; // class RResultBase
137
138// clang-format off
139/**
140\class ROOT::RResult
141\ingroup Base
142\brief The class is used as a return type for operations that can fail; wraps a value of type T or an RError
143
144The RResult<T> class and their related classes are used for call chains that can throw exceptions,
145such as I/O code paths. Throwing of the exception is deferred to allow for `if (result)` style error
146checking where it makes sense. If an RResult in error state leaves the scope unchecked, it will throw.
147
148A function returning an RResult might look like this:
149
150~~~ {.cpp}
151RResult<int> MyIOFunc()
152{
153 int rv = syscall(...);
154 if (rv == -1)
155 return R__FAIL("user-facing error message");
156 if (rv == kShortcut)
157 return 42;
158 return R__FORWARD_RESULT(FuncThatReturnsRResultOfInt());
159}
160~~~
161
162Code using MyIOFunc might look like this:
163
164~~~ {.cpp}
165auto result = MyIOOperation();
166if (!result) {
167 // custom error handling or result.Throw()
168}
169switch (result.Inspect()) {
170 ...
171}
172~~~
173
174Note that RResult<void> can be used for a function without return value, like this
175
176~~~ {.cpp}
177RResult<void> DoSomething()
178{
179 if (failure)
180 return R__FAIL("user-facing error messge");
181 return RResult<void>::Success();
182}
183~~~
184
185RResult<T>::Unwrap() can be used as a short hand for
186"give me the wrapped value or, in case of an error, throw". For instance:
187
188~~~ {.cpp}
189int value = FuncThatReturnsRResultOfInt().Unwrap(); // may throw
190~~~
191
192There is no implict operator that converts RResult<T> to T. This is intentional to make it clear in the calling code
193where an exception may be thrown.
194*/
195// clang-format on
196template <typename T>
197class RResult : public RResultBase {
198private:
199 /// The result value in case of successful execution
201
202 // Ensure accessor methods throw in case of errors
203 inline void ThrowOnError()
204 {
205 if (R__unlikely(fError)) {
206 // Accessors can be wrapped in a try-catch block, so throwing the
207 // exception here is akin to checking the error.
208 //
209 // Setting fIsChecked to true also avoids a spurious warning in the RResult destructor
210 fIsChecked = true;
211
212 fError->AppendToMessage(" (unchecked RResult access!)");
213 throw RException(*fError);
214 }
215 }
216
217public:
218 RResult(const T &value) : fValue(value) {}
219 RResult(T &&value) : fValue(std::move(value)) {}
220 RResult(RError &&error) : RResultBase(std::move(error)) {}
221
222 RResult(const RResult &other) = delete;
223 RResult(RResult &&other) = default;
224 RResult &operator=(const RResult &other) = delete;
225 RResult &operator=(RResult &&other) = default;
226
227 ~RResult() = default;
228
229 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
231 {
232 if (fError)
233 fError->AddFrame(std::move(sourceLocation));
234 return *this;
235 }
236
237 /// If the operation was successful, returns a const reference to the inner type.
238 /// If there was an error, Inspect() instead throws an exception.
239 const T &Inspect()
240 {
241 ThrowOnError();
242 return fValue;
243 }
244
245 /// If the operation was successful, returns the inner type by value.
246 ///
247 /// For move-only types, Unwrap can only be called once, as it yields ownership of
248 /// the inner value to the caller using std::move, potentially leaving the
249 /// RResult in an unspecified state.
250 ///
251 /// If there was an error, Unwrap() instead throws an exception.
253 {
254 ThrowOnError();
255 return std::move(fValue);
256 }
257
258 explicit operator bool() { return Check(); }
259};
260
261/// RResult<void> has no data member and no Inspect() method but instead a Success() factory method
262template <>
263class RResult<void> : public RResultBase {
264private:
265 RResult() = default;
266
267public:
268 /// Returns a RResult<void> that captures the successful execution of the function
269 static RResult Success() { return RResult(); }
270 RResult(RError &&error) : RResultBase(std::move(error)) {}
271
272 RResult(const RResult &other) = delete;
273 RResult(RResult &&other) = default;
274 RResult &operator=(const RResult &other) = delete;
275 RResult &operator=(RResult &&other) = default;
276
277 ~RResult() = default;
278
279 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
281 {
282 if (fError)
283 fError->AddFrame(std::move(sourceLocation));
284 return *this;
285 }
286
287 /// Short-hand method to throw an exception in the case of errors. Does nothing for
288 /// successful RResults.
290 {
291 if (!Check())
292 Throw();
293 }
294
295 explicit operator bool() { return Check(); }
296};
297
298/// Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult<T>
299#define R__FAIL(msg) ROOT::RError(msg, {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
300/// Short-hand to return an RResult<T> value from a subroutine to the calling stack frame
301#define R__FORWARD_RESULT(res) std::move(res.Forward({R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__}))
302/// Short-hand to return an RResult<T> in an error state (i.e. after checking)
303#define R__FORWARD_ERROR(res) res.ForwardError(std::move(res), {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
304
305} // namespace ROOT
306
307#endif
#define R__unlikely(expr)
Definition RConfig.hxx:602
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
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:128
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:289
RResult(RError &&error)
Definition RError.hxx:270
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:269
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:280
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:197
RResult(RError &&error)
Definition RError.hxx:220
T Unwrap()
If the operation was successful, returns the inner type by value.
Definition RError.hxx:252
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:230
RResult(T &&value)
Definition RError.hxx:219
T fValue
The result value in case of successful execution.
Definition RError.hxx:200
RResult(const RResult &other)=delete
void ThrowOnError()
Definition RError.hxx:203
RResult(const T &value)
Definition RError.hxx:218
RResult & operator=(RResult &&other)=default
~RResult()=default
const T & Inspect()
If the operation was successful, returns a const reference to the inner type.
Definition RError.hxx:239
RResult & operator=(const RResult &other)=delete
TLine * line
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
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