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