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