Logo ROOT  
Reference Guide
RError.hxx
Go to the documentation of this file.
1/// \file ROOT/RError.h
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-2019, 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.Get()) {
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 // Help to prevent heap construction of RResult objects. Unchecked RResult objects in failure state should throw
164 // an exception close to the error location. For stack allocated RResult objects, an exception is thrown
165 // the latest when leaving the scope. Heap allocated RResult objects in failure state can live much longer making it
166 // difficult to trace back the original error.
167 void *operator new(std::size_t size) = delete;
168 void *operator new(std::size_t, void *) = delete;
169 void *operator new[](std::size_t) = delete;
170 void *operator new[](std::size_t, void *) = delete;
171}; // class RResultBase
172} // namespace Internal
173
174
175// clang-format off
176/**
177\class ROOT::Experimental::RResult
178\ingroup Base
179\brief The class is used as a return type for operations that can fail; wraps a value of type T or an RError
180
181RResult enforces checking whether it contains a valid value or an error state. If the RResult leaves the scope
182unchecked, it will throw an exception (due to ~RResultBase).
183*/
184// clang-format on
185template <typename T>
187private:
188 /// The result value in case of successful execution
190
191public:
192 RResult(const T &value) : fValue(value) {}
193 RResult(T &&value) : fValue(std::move(value)) {}
194 RResult(RError &&error) : RResultBase(std::move(error)) {}
195
196 RResult(const RResult &other) = delete;
197 RResult(RResult &&other) = default;
198 RResult &operator =(const RResult &other) = delete;
199 RResult &operator =(RResult &&other) = default;
200
201 ~RResult() = default;
202
203 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
204 static RResult &Forward(RResult &result, RError::RLocation &&sourceLocation) {
205 if (result.fError)
206 result.fError->AddFrame(std::move(sourceLocation));
207 return result;
208 }
209
210 const T &Get() {
211 if (R__unlikely(fError)) {
212 // Get() can be wrapped in a try-catch block, so throwing the exception here is akin to checking the error.
213 // Setting fIsChecked to true also avoids a spurious warning in the RResult destructor
214 fIsChecked = true;
215
216 fError->AppendToMessage(" (unchecked RResult access!)");
217 throw RException(*fError);
218 }
219 return fValue;
220 }
221
222 explicit operator bool() { return Check(); }
223};
224
225/// RResult<void> has no data member and no Get() method but instead a Success() factory method
226template<>
228private:
229 RResult() = default;
230
231public:
232 /// Returns a RResult<void> that captures the successful execution of the function
233 static RResult Success() { return RResult(); }
234 RResult(RError &&error) : RResultBase(std::move(error)) {}
235
236 RResult(const RResult &other) = delete;
237 RResult(RResult &&other) = default;
238 RResult &operator =(const RResult &other) = delete;
239 RResult &operator =(RResult &&other) = default;
240
241 ~RResult() = default;
242
243 /// Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors
244 static RResult &Forward(RResult &result, RError::RLocation &&sourceLocation) {
245 if (result.fError)
246 result.fError->AddFrame(std::move(sourceLocation));
247 return result;
248 }
249
250 explicit operator bool() { return Check(); }
251};
252
253/// Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult<T>
254#define R__FAIL(msg) ROOT::Experimental::RError(msg, {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__})
255/// Short-hand to return an RResult<T> value from a subroutine to the calling stack frame
256#define R__FORWARD_RESULT(res) std::move(res.Forward(res, {R__LOG_PRETTY_FUNCTION, __FILE__, __LINE__}))
257} // namespace Experimental
258} // namespace ROOT
259
260#endif
#define R__unlikely(expr)
Definition: RConfig.hxx:604
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
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
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:115
RException(const RError &error)
Definition: RError.hxx:118
const RError & GetError() const
Definition: RError.hxx:119
RResult(const RResult &other)=delete
static RResult & Forward(RResult &result, RError::RLocation &&sourceLocation)
Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors.
Definition: RError.hxx:244
RResult(RResult &&other)=default
static RResult Success()
Returns a RResult<void> that captures the successful execution of the function.
Definition: RError.hxx:233
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:186
T fValue
The result value in case of successful execution.
Definition: RError.hxx:189
static RResult & Forward(RResult &result, RError::RLocation &&sourceLocation)
Used by R__FORWARD_RESULT in order to keep track of the stack trace in case of errors.
Definition: RError.hxx:204
RResult(const RResult &other)=delete
RResult(RResult &&other)=default
RResult(RError &&error)
Definition: RError.hxx:194
RResult(const T &value)
Definition: RError.hxx:192
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: StringConv.hxx:21
Definition: file.py:1
RLocation(const char *func, const char *file, int line)
Definition: RError.hxx:81