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