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)
45 : fFunction(func), fSourceFile(file), fSourceLine(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// clang-format off
86/**
87\class ROOT::Experimental::RResultBase
88\ingroup Base
89\brief Common handling of the error case for RResult<T> (T != void) and RResult<void>
90
91RResultBase captures a possible runtime error that might have occured. If the RResultBase leaves the scope unchecked,
92it will throw an exception. RResultBase should only be allocated on the stack, which is helped by deleting the
93new operator. RResultBase is movable but not copyable to avoid throwing multiple exceptions about the same failure.
94*/
95// clang-format on
97protected:
98 /// This is the nullptr for an RResult representing success
99 std::unique_ptr<RError> fError;
100 /// Switches to true once the user of an RResult object checks the object status
101 bool fIsChecked{false};
102
103 RResultBase() = default;
104 explicit RResultBase(RError &&error) : fError(std::make_unique<RError>(std::move(error))) {}
105
106 /// Used by the RResult<T> bool operator
107 bool Check() {
108 fIsChecked = true;
109 return !fError;
110 }
111
112public:
113 RResultBase(const RResultBase &other) = delete;
114 RResultBase(RResultBase &&other) = default;
115 RResultBase &operator =(const RResultBase &other) = delete;
116 RResultBase &operator =(RResultBase &&other) = default;
117
118 ~RResultBase() noexcept(false);
119
120 RError *GetError() { return fError.get(); }
121 /// Throws an RException with fError
122 void Throw();
123
124 /// Used by R__FORWARD_ERROR in order to keep track of the stack trace.
126 if (!result.fError) {
127 return RError("internal error: attempt to forward error of successful operation",
128 std::move(sourceLocation));
129 }
130 result.fError->AddFrame(std::move(sourceLocation));
131 return *result.fError;
132 }
133}; // class RResultBase
134
135// clang-format off
136/**
137\class ROOT::Experimental::RResult
138\ingroup Base
139\brief The class is used as a return type for operations that can fail; wraps a value of type T or an RError
140
141The RResult<T> class and their related classes are used for call chains that can throw exceptions,
142such as I/O code paths. Throwing of the exception is deferred to allow for `if (result)` style error
143checking where it makes sense. If an RResult in error state leaves the scope unchecked, it will throw.
144
145A function returning an RResult might look like this:
146
147~~~ {.cpp}
148RResult<int> MyIOFunc()
149{
150 int rv = syscall(...);
151 if (rv == -1)
152 return R__FAIL("user-facing error message");
153 if (rv == kShortcut)
154 return 42;
155 return R__FORWARD_RESULT(FuncThatReturnsRResultOfInt());
156}
157~~~
158
159Code using MyIOFunc might look like this:
160
161~~~ {.cpp}
162auto result = MyIOOperation();
163if (!result) {
164 // custom error handling or result.Throw()
165}
166switch (result.Inspect()) {
167 ...
168}
169~~~
170
171Note that RResult<void> can be used for a function without return value, like this
172
173~~~ {.cpp}
174RResult<void> DoSomething()
175{
176 if (failure)
177 return R__FAIL("user-facing error messge");
178 return RResult<void>::Success();
179}
180~~~
181
182RResult<T>::Unwrap() can be used as a short hand for
183"give me the wrapped value or, in case of an error, throw". For instance:
184
185~~~ {.cpp}
186int value = FuncThatReturnsRResultOfInt().Unwrap(); // may throw
187~~~
188
189There is no implict operator that converts RResult<T> to T. This is intentional to make it clear in the calling code
190where an exception may be thrown.
191*/
192// clang-format on
193template <typename T>
194class RResult : public RResultBase {
195private:
196 /// The result value in case of successful execution
198
199 // Ensure accessor methods throw in case of errors
200 inline void ThrowOnError() {
201 if (R__unlikely(fError)) {
202 // Accessors can be wrapped in a try-catch block, so throwing the
203 // exception here is akin to checking the error.
204 //
205 // Setting fIsChecked to true also avoids a spurious warning in the RResult destructor
206 fIsChecked = true;
207
208 fError->AppendToMessage(" (unchecked RResult access!)");
209 throw RException(*fError);
210 }
211 }
212
213public:
214 RResult(const T &value) : fValue(value) {}
215 RResult(T &&value) : fValue(std::move(value)) {}
216 RResult(RError &&error) : RResultBase(std::move(error)) {}
217
218 RResult(const RResult &other) = delete;
219 RResult(RResult &&other) = default;
220 RResult &operator =(const RResult &other) = delete;
221 RResult &operator =(RResult &&other) = default;
222
223 ~RResult() = default;
224
225 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
226 RResult &Forward(RError::RLocation &&sourceLocation) {
227 if (fError)
228 fError->AddFrame(std::move(sourceLocation));
229 return *this;
230 }
231
232 /// If the operation was successful, returns a const reference to the inner type.
233 /// If there was an error, Inspect() instead throws an exception.
234 const T &Inspect() {
235 ThrowOnError();
236 return fValue;
237 }
238
239 /// If the operation was successful, returns the inner type by value.
240 ///
241 /// For move-only types, Unwrap can only be called once, as it yields ownership of
242 /// the inner value to the caller using std::move, potentially leaving the
243 /// RResult in an unspecified state.
244 ///
245 /// If there was an error, Unwrap() instead throws an exception.
246 T Unwrap() {
247 ThrowOnError();
248 return std::move(fValue);
249 }
250
251 explicit operator bool() { return Check(); }
252};
253
254/// RResult<void> has no data member and no Inspect() method but instead a Success() factory method
255template <>
256class RResult<void> : public RResultBase {
257private:
258 RResult() = default;
259
260public:
261 /// Returns a RResult<void> that captures the successful execution of the function
262 static RResult Success() { return RResult(); }
263 RResult(RError &&error) : RResultBase(std::move(error)) {}
264
265 RResult(const RResult &other) = delete;
266 RResult(RResult &&other) = default;
267 RResult &operator =(const RResult &other) = delete;
268 RResult &operator =(RResult &&other) = default;
269
270 ~RResult() = default;
271
272 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
273 RResult &Forward(RError::RLocation &&sourceLocation) {
274 if (fError)
275 fError->AddFrame(std::move(sourceLocation));
276 return *this;
277 }
278
279 /// Short-hand method to throw an exception in the case of errors. Does nothing for
280 /// successful RResults.
282 if (!Check())
283 Throw();
284 }
285
286 explicit operator bool() { return Check(); }
287};
288
289/// Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult<T>
290#define R__FAIL(msg) ROOT::Experimental::RError(msg, {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
291/// Short-hand to return an RResult<T> value from a subroutine to the calling stack frame
292#define R__FORWARD_RESULT(res) std::move(res.Forward({R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__}))
293/// Short-hand to return an RResult<T> in an error state (i.e. after checking)
294#define R__FORWARD_ERROR(res) res.ForwardError(std::move(res), {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
295} // namespace Experimental
296} // namespace ROOT
297
298#endif
#define R__unlikely(expr)
Definition RConfig.hxx:611
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: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
Common handling of the error case for RResult<T> (T != void) and RResult<void>
Definition RError.hxx:96
bool fIsChecked
Switches to true once the user of an RResult object checks the object status.
Definition RError.hxx:101
RResultBase(const RResultBase &other)=delete
std::unique_ptr< RError > fError
This is the nullptr for an RResult representing success.
Definition RError.hxx:99
void Throw()
Throws an RException with fError.
Definition RError.cxx:67
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:125
bool Check()
Used by the RResult<T> bool operator.
Definition RError.hxx:107
~RResultBase() noexcept(false)
Definition RError.cxx:50
RResultBase & operator=(const RResultBase &other)=delete
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:273
RResult(RResult &&other)=default
void ThrowOnError()
Short-hand method to throw an exception in the case of errors.
Definition RError.hxx:281
static RResult Success()
Returns a RResult<void> that captures the successful execution of the function.
Definition RError.hxx:262
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:194
const T & Inspect()
If the operation was successful, returns a const reference to the inner type.
Definition RError.hxx:234
T fValue
The result value in case of successful execution.
Definition RError.hxx:197
RResult(const RResult &other)=delete
T Unwrap()
If the operation was successful, returns the inner type by value.
Definition RError.hxx:246
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:226
RResult(RResult &&other)=default
RResult(RError &&error)
Definition RError.hxx:216
RResult(const T &value)
Definition RError.hxx:214
RResult & operator=(const RResult &other)=delete
TLine * line
This file contains a specialised ROOT message handler to test for diagnostic in unit tests.
RLocation(const char *func, const char *file, int line)
Definition RError.hxx:44