1 | /** |
2 | * Copyright (c) Glow Contributors. See CONTRIBUTORS file. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | #ifndef GLOW_SUPPORT_ERROR_H |
17 | #define GLOW_SUPPORT_ERROR_H |
18 | |
19 | #include <cassert> |
20 | #include <memory> |
21 | #include <mutex> |
22 | #include <type_traits> |
23 | |
24 | #include <glog/logging.h> |
25 | |
26 | /// NOTE: please only use code and macros that resides outside of the detail |
27 | /// namespace in Error.h and Error.cpp so as to preserve a layer of |
28 | /// abstraction between Error/Expected types and the specific classes that |
29 | /// implement them. |
30 | |
31 | /// Consumes an Error and \returns true iff the error contained an |
32 | /// ErrorValue. Calls the log method on ErrorValue if the optional argument \p |
33 | /// log is passed. |
34 | #define ERR_TO_BOOL(...) \ |
35 | (glow::detail::errorToBool(__FILE__, __LINE__, __VA_ARGS__)) |
36 | |
37 | /// Consumes an Error and \returns "success" if it does not contain an |
38 | /// ErrorValue or the result of calling the log() if it does. |
39 | #define ERR_TO_STRING(...) \ |
40 | (glow::detail::errorToString(__FILE__, __LINE__, __VA_ARGS__)) |
41 | |
42 | /// Consumes an Error. Calls the log method on the ErrorValue if the |
43 | /// optional argument \p log is passed. |
44 | #define ERR_TO_VOID(...) \ |
45 | (glow::detail::errorToVoid(__FILE__, __LINE__, __VA_ARGS__)) |
46 | |
47 | /// Unwraps the T from within an Expected<T>. If the Expected<T> contains |
48 | /// an ErrorValue, the program will exit. |
49 | #define EXIT_ON_ERR(...) \ |
50 | (glow::detail::exitOnError(__FILE__, __LINE__, __VA_ARGS__)) |
51 | |
52 | /// A temporary placeholder for EXIT_ON_ERR. This should be used only during |
53 | /// refactoring to temporarily place an EXIT_ON_ERR and should eventually be |
54 | /// replaced with either an actual EXIT_ON_ERR or code that will propogate |
55 | /// potential errors up the stack. |
56 | #define TEMP_EXIT_ON_ERR(...) (EXIT_ON_ERR(__VA_ARGS__)) |
57 | |
58 | /// Makes a new Error. |
59 | #define MAKE_ERR(...) glow::detail::makeError(__FILE__, __LINE__, __VA_ARGS__) |
60 | |
61 | /// Takes an Expected<T> \p rhsOrErr and if it is an Error then returns |
62 | /// it, otherwise takes the value from rhsOrErr and assigns it to \p lhs. |
63 | #define ASSIGN_VALUE_OR_RETURN_ERR(lhs, rhsOrErr) \ |
64 | do { \ |
65 | auto rhsOrErrV = (rhsOrErr); \ |
66 | static_assert(glow::detail::IsExpected<decltype(rhsOrErrV)>(), \ |
67 | "Expected value to be a Expected"); \ |
68 | if (rhsOrErrV) { \ |
69 | lhs = std::move(rhsOrErrV.get()); \ |
70 | } else { \ |
71 | auto err = rhsOrErrV.takeError(); \ |
72 | err.addToStack(__FILE__, __LINE__); \ |
73 | return std::forward<Error>(err); \ |
74 | } \ |
75 | } while (0) |
76 | |
77 | /// Takes an Expected<T> \p rhsOrErr and if it is an Error then calls FAIL() |
78 | /// otherwise takes the value from rhsOrErr and assigns it to \p lhs. |
79 | #define ASSIGN_VALUE_OR_FAIL_TEST(lhs, rhsOrErr) \ |
80 | do { \ |
81 | auto rhsOrErrV = (rhsOrErr); \ |
82 | static_assert(glow::detail::IsExpected<decltype(rhsOrErrV)>(), \ |
83 | "Expected value to be a Expected"); \ |
84 | if (rhsOrErrV) { \ |
85 | lhs = std::move(rhsOrErrV.get()); \ |
86 | } else { \ |
87 | auto err = rhsOrErrV.takeError(); \ |
88 | FAIL() << ERR_TO_STRING(std::move(err)); \ |
89 | } \ |
90 | } while (0) |
91 | |
92 | /// Takes an Expected<T> \p rhsOrErr and if it is an Error then LOG(FATAL)'s, |
93 | /// otherwise takes the value from rhsOrErr and assigns it to \p lhs. |
94 | #define ASSIGN_VALUE_OR_FATAL(lhs, rhsOrErr) \ |
95 | do { \ |
96 | auto rhsOrErrV = (rhsOrErr); \ |
97 | static_assert(glow::detail::IsExpected<decltype(rhsOrErrV)>(), \ |
98 | "Expected value to be a Expected"); \ |
99 | if (rhsOrErrV) { \ |
100 | lhs = std::move(rhsOrErrV.get()); \ |
101 | } else { \ |
102 | auto err = rhsOrErrV.takeError(); \ |
103 | LOG(FATAL) << ERR_TO_STRING(std::move(err)); \ |
104 | } \ |
105 | } while (0) |
106 | |
107 | /// Takes an Error, adds stack information to it, and returns it unconditionally |
108 | #define RETURN_ERR(err) \ |
109 | do { \ |
110 | auto errV = std::forward<glow::detail::GlowError>(err); \ |
111 | static_assert(glow::detail::IsError<decltype(errV)>::value, \ |
112 | "Expected value to be a Error"); \ |
113 | errV.addToStack(__FILE__, __LINE__); \ |
114 | return std::forward<Error>(errV); \ |
115 | } while (0) |
116 | |
117 | /// Takes an Error and returns it if it's not success. |
118 | #define RETURN_IF_ERR(err) \ |
119 | do { \ |
120 | if (auto errV = std::forward<glow::detail::GlowError>(err)) { \ |
121 | static_assert(glow::detail::IsError<decltype(errV)>::value, \ |
122 | "Expected value to be a Error"); \ |
123 | errV.addToStack(__FILE__, __LINE__); \ |
124 | return std::forward<Error>(errV); \ |
125 | } \ |
126 | } while (0) |
127 | |
128 | /// Takes an Expected and returns it if it's not success. |
129 | #define RETURN_IF_EXPECTED_IS_ERR(expInp) \ |
130 | do { \ |
131 | auto expV = (expInp); \ |
132 | static_assert(glow::detail::IsExpected<decltype(expV)>(), \ |
133 | "Expected value to be a Expected"); \ |
134 | RETURN_IF_ERR((expV).takeError()); \ |
135 | } while (0) |
136 | |
137 | /// Takes an Error and if it contains an ErrorValue then calls FAIL(). |
138 | #define FAIL_TEST_IF_ERR(err) \ |
139 | do { \ |
140 | if (auto errV = std::forward<glow::detail::GlowError>(err)) { \ |
141 | static_assert(glow::detail::IsError<decltype(errV)>::value, \ |
142 | "Expected value to be a Error"); \ |
143 | FAIL() << ERR_TO_STRING(std::move(errV)); \ |
144 | } \ |
145 | } while (0) |
146 | |
147 | /// Takes a predicate \p and if it is false then creates a new Error |
148 | /// and returns it. |
149 | #define RETURN_ERR_IF_NOT(p, ...) \ |
150 | do { \ |
151 | if (!(p)) { \ |
152 | return MAKE_ERR(__VA_ARGS__); \ |
153 | } \ |
154 | } while (0) |
155 | |
156 | /// Takes an Error if it's not success then adds the given message to the stack. |
157 | #define ADD_MESSAGE_TO_ERR_STACK(err, msg) \ |
158 | (err).addToStack(__FILE__, __LINE__, msg) |
159 | |
160 | namespace glow { |
161 | |
162 | /// Forward declarations. |
163 | namespace detail { |
164 | class GlowError; |
165 | class GlowErrorSuccess; |
166 | class GlowErrorEmpty; |
167 | class GlowErrorValue; |
168 | template <typename T> class GlowExpected; |
169 | } // namespace detail |
170 | |
171 | /// Type aliases to decouple Error and Expected from underlying implementation. |
172 | using Error = detail::GlowError; |
173 | using ErrorSuccess = detail::GlowErrorSuccess; |
174 | using ErrorEmpty = detail::GlowErrorEmpty; |
175 | using ErrorValue = detail::GlowErrorValue; |
176 | template <typename T> using Expected = detail::GlowExpected<T>; |
177 | |
178 | /// NOTE: detail namespace contains code that should not be used outside of |
179 | /// Error.h and Error.cpp. Please instead use types and macros defined above. |
180 | namespace detail { |
181 | /// enableCheckingErrors is used to enable assertions that every Error and |
182 | /// Expected has its status checked before it is destroyed. This should be |
183 | /// enabled in debug builds but turned off otherwise. |
184 | #ifdef NDEBUG |
185 | static constexpr bool enableCheckingErrors = false; |
186 | #else |
187 | static constexpr bool enableCheckingErrors = true; |
188 | #endif |
189 | |
190 | /// Is true_type only if applied to Error or a descendant. |
191 | template <typename T> struct IsError : public std::is_base_of<GlowError, T> {}; |
192 | |
193 | /// Is true_type only if applied to Expected. |
194 | template <typename> struct IsExpected : public std::false_type {}; |
195 | template <typename T> |
196 | struct IsExpected<GlowExpected<T>> : public std::true_type {}; |
197 | |
198 | /// CheckState<DoChecks> is a common base class for Error and Expected that |
199 | /// tracks whether their state has been checked or not if DoChecks is true |
200 | /// and otherwise it does nothing and has no members so as to not take extra |
201 | /// space. This is used to ensure that all Errors and Expecteds are checked |
202 | /// before they are destroyed. |
203 | template <bool DoChecks> class CheckState; |
204 | |
205 | /// Specialization of CheckState with checking enabled. |
206 | template <> class CheckState<true> { |
207 | /// Whether or not the a check has occurred. |
208 | bool checked_ = false; |
209 | |
210 | public: |
211 | /// Set the state of checked. |
212 | inline void setChecked(bool checked) { checked_ = checked; } |
213 | |
214 | /// Asserts that the state has been checked. |
215 | inline void ensureChecked() const { |
216 | assert(checked_ && "Unchecked Error or Expected" ); |
217 | } |
218 | CheckState() : checked_(false) {} |
219 | |
220 | /// Destructor that is used to ensure that base classes have been checked. |
221 | ~CheckState() { ensureChecked(); } |
222 | |
223 | /// NOTE: Only use for testing! |
224 | bool isChecked() const { return checked_; } |
225 | }; |
226 | |
227 | /// Specialization of CheckState with checking disabled. |
228 | template <> class CheckState<false> { |
229 | public: |
230 | inline void setChecked(bool checked) {} |
231 | inline void ensureChecked() const {} |
232 | /// NOTE: Only use for testing! |
233 | bool isChecked() const { return true; } |
234 | }; |
235 | |
236 | /// Opaque is an aligned opaque container for some type T. It holds a T in-situ |
237 | /// but will not destroy it automatically when the Opaque is destroyed but |
238 | /// instead only when the destroy() method is called. |
239 | template <typename T> class Opaque { |
240 | private: |
241 | alignas(T) char payload_[sizeof(T)]; |
242 | |
243 | public: |
244 | /// Sets the value within this Opaque container. |
245 | void set(T t) { new (payload_) T(std::forward<T>(t)); } |
246 | |
247 | /// Gets the value within this Opaque container. |
248 | T &get() { return *reinterpret_cast<T *>(payload_); } |
249 | |
250 | /// Gets the value within this Opaque container. |
251 | const T &get() const { return *reinterpret_cast<const T *>(payload_); } |
252 | |
253 | /// Call the destructor of the value in this container. |
254 | void destroy() { get().~T(); } |
255 | }; |
256 | |
257 | /// This method is the only way to destroy an Error \p error and mark it as |
258 | /// checked when it contains an ErrorValue. It \returns the contained |
259 | /// ErrorValue. |
260 | /// NOTE: This method should not be used directly, use one of the methods that |
261 | /// calls this. |
262 | std::unique_ptr<GlowErrorValue> takeErrorValue(GlowError error); |
263 | |
264 | /// Takes an Error \p error and asserts that it does not contain an ErrorValue. |
265 | /// Uses \p fileName and \p lineNumber for logging. |
266 | void exitOnError(const char *fileName, size_t lineNumber, GlowError error); |
267 | |
268 | /// ErrorValue contains information about an error that occurs at runtime. It is |
269 | /// not used directly but instead is passed around inside of the Error and |
270 | /// Expected containers. It should only be constructed using the makeError |
271 | /// method. |
272 | class GlowErrorValue final { |
273 | public: |
274 | struct StackEntry { |
275 | std::string fileName; |
276 | size_t lineNumber; |
277 | std::string message; |
278 | |
279 | StackEntry(const std::string &fileNameParam, size_t lineNumberParam) |
280 | : fileName(fileNameParam), lineNumber(lineNumberParam) {} |
281 | |
282 | StackEntry(const std::string &fileNameParam, size_t lineNumberParam, |
283 | const std::string &messageParam) |
284 | : fileName(fileNameParam), lineNumber(lineNumberParam), |
285 | message(messageParam) {} |
286 | }; |
287 | |
288 | /// An enumeration of error codes representing various possible errors that |
289 | /// could occur. |
290 | /// NOTE: when updating this enum, also update errorCodeToString function |
291 | /// below. |
292 | enum class ErrorCode { |
293 | // An unknown error ocurred. This is the default value. |
294 | UNKNOWN, |
295 | // Model loader encountered an unsupported shape. |
296 | MODEL_LOADER_UNSUPPORTED_SHAPE, |
297 | // Model loader encountered an unsupported operator. |
298 | MODEL_LOADER_UNSUPPORTED_OPERATOR, |
299 | // Model loader encountered an unsupported attribute. |
300 | MODEL_LOADER_UNSUPPORTED_ATTRIBUTE, |
301 | // Model loader encountered an unsupported datatype. |
302 | MODEL_LOADER_UNSUPPORTED_DATATYPE, |
303 | // Model loader encountered an unsupported ONNX version. |
304 | MODEL_LOADER_UNSUPPORTED_ONNX_VERSION, |
305 | // Model loader encountered an invalid protobuf. |
306 | MODEL_LOADER_INVALID_PROTOBUF, |
307 | // Partitioner error. |
308 | PARTITIONER_ERROR, |
309 | // Runtime error, general error. |
310 | RUNTIME_ERROR, |
311 | // Runtime error, error while loading deferred weights. |
312 | RUNTIME_DEFERRED_WEIGHT_ERROR, |
313 | // Runtime error, out of device memory. |
314 | RUNTIME_OUT_OF_DEVICE_MEMORY, |
315 | // Runtime error, could not find the specified model network. |
316 | RUNTIME_NET_NOT_FOUND, |
317 | // Runtime error, runtime refusing to service request. |
318 | RUNTIME_REQUEST_REFUSED, |
319 | // Runtime error, device wasn't found. |
320 | RUNTIME_DEVICE_NOT_FOUND, |
321 | // Non-recoverable device error |
322 | RUNTIME_DEVICE_NONRECOVERABLE, |
323 | // Runtime error, network busy to perform any operation on it. |
324 | RUNTIME_NET_BUSY, |
325 | // Device error, not supported. |
326 | DEVICE_FEATURE_NOT_SUPPORTED, |
327 | // Compilation error; node unsupported after optimizations. |
328 | COMPILE_UNSUPPORTED_NODE_AFTER_OPTIMIZE, |
329 | // Compilation error; Compilation context not correctly setup. |
330 | COMPILE_CONTEXT_MALFORMED, |
331 | // Model writer encountered an invalid file name. |
332 | MODEL_WRITER_INVALID_FILENAME, |
333 | // Model writer cannot serialize graph to the file. |
334 | MODEL_WRITER_SERIALIZATION_ERROR, |
335 | // Compilation error; IR unsupported after generation. |
336 | COMPILE_UNSUPPORTED_IR_AFTER_GENERATE, |
337 | // Compilation error; IR unsupported after optimization. |
338 | COMPILE_UNSUPPORTED_IR_AFTER_OPTIMIZE, |
339 | }; |
340 | |
341 | /// Log to \p os relevant error information including the file name and |
342 | /// line number the ErrorValue was created on as well as the message and/or |
343 | /// error code the ErrorValue was created with. If \p warning is true then |
344 | /// the log message will replace "Error" with "Warning", this is useful for |
345 | /// when Errors are used in non-exceptional conditions. |
346 | template <typename StreamT> |
347 | void log(StreamT &os, bool warning = false) const { |
348 | const char *mType = warning ? "Warning" : "Error" ; |
349 | if (ec_ != ErrorCode::UNKNOWN) { |
350 | os << "\n" << mType << " code: " << errorCodeToString(ec_); |
351 | } |
352 | if (!message_.empty()) { |
353 | os << "\n" << mType << " message: " << message_; |
354 | } |
355 | os << "\n\n" << mType << " return stack:" ; |
356 | for (const auto &p : stack_) { |
357 | os << "\n----------------------------------------------------------------" |
358 | "----------------" ; |
359 | os << "\n" << p.fileName.c_str() << ":" << p.lineNumber; |
360 | if (!p.message.empty()) { |
361 | os << " with message:\n" ; |
362 | os << p.message.c_str(); |
363 | } |
364 | } |
365 | os << "\n----------------------------------------------------------------" |
366 | "----------------" ; |
367 | } |
368 | |
369 | /// Add \p filename and \p lineNumber to the ErrorValue's stack for logging. |
370 | void addToStack(const std::string &fileName, size_t lineNumber, |
371 | const std::string &message = "" ) { |
372 | stack_.emplace_back(fileName, lineNumber, message); |
373 | } |
374 | |
375 | /// If \p warning is true then the log message will replace "Error" with |
376 | /// "Warning", this is useful for when Errors are used in non-exceptional |
377 | /// conditions. |
378 | std::string logToString(bool warning = false) const; |
379 | |
380 | /// Return the error code. |
381 | bool isFatalError() const { |
382 | return ec_ == ErrorCode::RUNTIME_DEVICE_NONRECOVERABLE; |
383 | } |
384 | |
385 | ErrorCode getErrorCode() const { return ec_; } |
386 | |
387 | GlowErrorValue(std::string message, ErrorCode ec) |
388 | : message_(message), ec_(ec) {} |
389 | |
390 | GlowErrorValue(ErrorCode ec, std::string message) |
391 | : message_(message), ec_(ec) {} |
392 | |
393 | GlowErrorValue(ErrorCode ec) : ec_(ec) {} |
394 | |
395 | GlowErrorValue(std::string message) : message_(message) {} |
396 | |
397 | private: |
398 | /// Convert ErrorCode values to string. |
399 | static std::string errorCodeToString(const ErrorCode &ec); |
400 | |
401 | /// Optional message associated with the error. |
402 | std::string message_; |
403 | /// The (filename, line number, optional message) of all places that created, |
404 | /// forwarded, and destroyed the ErrorValue. |
405 | std::vector<StackEntry> stack_; |
406 | /// Optional error code associated with the error. |
407 | ErrorCode ec_ = ErrorCode::UNKNOWN; |
408 | }; |
409 | |
410 | /// Overload for operator<< for logging an ErrorValue \p errorValue to a stream |
411 | /// \p os. |
412 | template <typename StreamT> |
413 | StreamT &operator<<(StreamT &os, const GlowErrorValue &errorValue) { |
414 | errorValue.log(os); |
415 | return os; |
416 | } |
417 | |
418 | /// Error is a container for pointers to ErrorValues. If an ErrorValue is |
419 | /// contained Error ensures that it is checked before being destroyed. |
420 | class GlowError : protected detail::CheckState<detail::enableCheckingErrors> { |
421 | template <typename T> friend class GlowExpected; |
422 | friend std::unique_ptr<GlowErrorValue> detail::takeErrorValue(GlowError); |
423 | |
424 | /// Pointer to ErrorValue managed by this Error. Can be null if no error |
425 | /// occurred. Use getters and setters defined below to access this since they |
426 | /// also will modify the CheckState. |
427 | std::unique_ptr<GlowErrorValue> errorValue_; |
428 | |
429 | /// \return true if an ErrorValue is contained. |
430 | inline bool hasErrorValue() const { return nullptr != errorValue_; } |
431 | |
432 | /// Sets the value of errorValue_ to \p errorValue ensuring not to overwrite |
433 | /// any previously contained ErrorValues that were unchecked. This is skipped |
434 | /// however if \p skipCheck is passed. |
435 | /// NOTE: skipCheck should only be used by constructors. |
436 | inline void setErrorValue(std::unique_ptr<GlowErrorValue> errorValue, |
437 | bool skipCheck = false) { |
438 | // Can't overwrite an existing error unless we say not to check. |
439 | if (skipCheck) { |
440 | assert(errorValue_ == nullptr && |
441 | "Trying to skip state check on an Error that " |
442 | "contains an ErrorValue is a bug because this should only happen " |
443 | "in a constructor and then no ErrorValue should be contained." ); |
444 | } else { |
445 | ensureChecked(); |
446 | } |
447 | |
448 | errorValue_ = std::move(errorValue); |
449 | setChecked(false); |
450 | } |
451 | |
452 | /// \returns the contents of errorValue_ by moving them. Marks the Error as |
453 | /// checked no matter what. |
454 | /// NOTE: This is the only way to mark an Error that contains an ErrorValue as |
455 | /// checked. |
456 | inline std::unique_ptr<GlowErrorValue> takeErrorValue() { |
457 | setChecked(true); |
458 | return std::move(errorValue_); |
459 | } |
460 | |
461 | #ifdef WIN32 |
462 | public: |
463 | #else |
464 | protected: |
465 | #endif |
466 | /// Construct a new empty Error. |
467 | explicit GlowError() { setErrorValue(nullptr, /*skipCheck*/ true); } |
468 | |
469 | public: |
470 | /// Construct an Error from an ErrorValue \p errorValue. |
471 | GlowError(std::unique_ptr<GlowErrorValue> errorValue) { |
472 | assert(errorValue && |
473 | "Cannot construct an Error from a null ErrorValue ptr" ); |
474 | setErrorValue(std::move(errorValue), /*skipCheck*/ true); |
475 | } |
476 | |
477 | /// Move construct an Error from another Error \p other. |
478 | GlowError(GlowError &&other) { |
479 | setErrorValue(std::move(other.errorValue_), /*skipCheck*/ true); |
480 | other.setChecked(true); |
481 | } |
482 | |
483 | /// Construct an Error from an ErrorEmpty \p other. This is a special case |
484 | /// constructor that will mark the constructed Error as being checked. This |
485 | /// should only be used for creating Errors that will be passed into things |
486 | /// like fallible constructors of other classes to be written to. |
487 | GlowError(GlowErrorEmpty &&other); |
488 | |
489 | /// Move assign Error from another Error \p other. |
490 | GlowError &operator=(GlowError &&other) { |
491 | setErrorValue(std::move(other.errorValue_)); |
492 | other.setChecked(true); |
493 | return *this; |
494 | } |
495 | |
496 | /// Add \p filename and \p lineNumber to the contained ErrorValue's stack for |
497 | /// logging. |
498 | void addToStack(const std::string &fileName, size_t lineNumber, |
499 | const std::string &message = "" ) { |
500 | if (hasErrorValue()) { |
501 | errorValue_->addToStack(fileName, lineNumber, message); |
502 | } |
503 | } |
504 | |
505 | /// Create an Error not containing an ErrorValue that is signifies success |
506 | /// instead of failure of an operation. |
507 | /// NOTE: this Error must still be checked before being destroyed. |
508 | static GlowErrorSuccess success(); |
509 | |
510 | /// Create an empty Error that is signifies that an operation has not yet |
511 | /// occurred. This should only be used when another Error will be assigned to |
512 | /// this Error for example when calling a fallible constructor that takes an |
513 | /// Error reference as a parameter. |
514 | /// NOTE: this Error is considered to be "pre-checked" and therefore can be |
515 | /// destroyed at any time. |
516 | static GlowErrorEmpty empty(); |
517 | |
518 | // Disable copying Errors. |
519 | GlowError(const GlowError &) = delete; |
520 | GlowError &operator=(const GlowError &) = delete; |
521 | |
522 | /// Overload of operator bool() that \returns true if an ErrorValue is |
523 | /// contained. |
524 | /// NOTE: This marks the Error as checked only if no ErrorValue is contained. |
525 | /// If an ErrorValue is contained then that ErrorValue must be handled in |
526 | /// order to mark as checked. |
527 | explicit operator bool() { |
528 | // Only mark as checked when there isn't an ErrorValue contained. |
529 | bool hasError = hasErrorValue(); |
530 | if (!hasError) { |
531 | setChecked(true); |
532 | } |
533 | return hasError; |
534 | } |
535 | |
536 | /// \returns a pointer to the contained ErrorValue or nullptr if no |
537 | /// ErrorValue is contained in this Error. |
538 | inline const GlowErrorValue *peekErrorValue() const { |
539 | return errorValue_.get(); |
540 | } |
541 | |
542 | /// NOTE: Only use for testing! |
543 | bool isChecked_() const { return isChecked(); } |
544 | }; |
545 | |
546 | /// ErrorSuccess is a special Error that is used to mark the absents of an |
547 | /// error. It shouldn't be constructed directly but instead using |
548 | /// Error::success(). |
549 | class GlowErrorSuccess final : public GlowError {}; |
550 | |
551 | /// See declaration in Error for details. |
552 | inline GlowErrorSuccess GlowError::success() { return GlowErrorSuccess(); } |
553 | |
554 | /// ErrorSuccess is a special Error that is used to contain the future state of |
555 | /// a fallible process that hasn't yet occurred. It shouldn't be |
556 | /// constructed directly but instead using Error::empty(). See comments on |
557 | /// Error::empty() method for more details. |
558 | class GlowErrorEmpty final : public GlowError {}; |
559 | |
560 | /// See declaration in Error for details. |
561 | inline GlowErrorEmpty GlowError::empty() { return GlowErrorEmpty(); } |
562 | |
563 | template <typename T> class GlowExpected; |
564 | template <typename T> |
565 | T exitOnError(const char *fileName, size_t lineNumber, |
566 | GlowExpected<T> expected); |
567 | |
568 | /// Expected is a templated container for either a value of type T or an |
569 | /// ErrorValue. It is used for fallible processes that may return either a value |
570 | /// or encounter an error. Expected ensures that its state has been checked for |
571 | /// errors before destruction. |
572 | template <typename T> |
573 | class GlowExpected final |
574 | : protected detail::CheckState<detail::enableCheckingErrors> { |
575 | friend T detail::exitOnError<>(const char *fileName, size_t lineNumber, |
576 | GlowExpected<T> expected); |
577 | template <typename OtherT> friend class GlowExpected; |
578 | |
579 | /// Union type between ErrorValue and T. Holds both in Opaque containers so |
580 | /// that lifetime management is manual and tied to the lifetime of Expected. |
581 | union Payload { |
582 | detail::Opaque<std::unique_ptr<GlowErrorValue>> asErrorValue; |
583 | detail::Opaque<T> asValue; |
584 | }; |
585 | |
586 | /// A union that contains either an ErrorValue if an error occurred |
587 | /// or a value of type T. |
588 | Payload payload_; |
589 | |
590 | /// Whether or not payload_ contains an Error. Expected cannot be constructed |
591 | /// from ErrorSuccess so if an ErrorValue is contained it is a legitimate |
592 | /// Error. |
593 | bool isError_; |
594 | |
595 | /// Getter for isError_. |
596 | inline bool getIsError() const { return isError_; } |
597 | |
598 | /// Setter for isError_. |
599 | inline void setIsError(bool isError) { isError_ = isError; } |
600 | |
601 | /// Asserts that an ErrorValue is contained not a Value. |
602 | inline void ensureError() { |
603 | assert(getIsError() && "Trying to get an ErrorValue of an Expected that " |
604 | "doesn't contain an ErrorValue" ); |
605 | } |
606 | |
607 | /// Asserts that a Value is contained not an ErrorValue |
608 | inline void ensureValue() { |
609 | assert( |
610 | !getIsError() && |
611 | "Trying to get a Value of an Expected that doesn't contain an Value" ); |
612 | } |
613 | |
614 | /// Setter for payload_ that inserts an ErrorValue \p errorValue. If \p |
615 | /// skipCheck is true then don't check that the current payload has been |
616 | /// checked before setting otherwise do check. |
617 | /// NOTE: Only constructors of Expected should use skipCheck. |
618 | inline void setErrorValue(std::unique_ptr<GlowErrorValue> errorValue, |
619 | bool skipCheck = false) { |
620 | if (!skipCheck) { |
621 | ensureChecked(); |
622 | } |
623 | setIsError(true); |
624 | return payload_.asErrorValue.set(std::move(errorValue)); |
625 | } |
626 | |
627 | /// Getter for payload_ to retrieve an ErrorValue. Ensures that an ErrorValue |
628 | /// is contained and that it has been checked. |
629 | inline GlowErrorValue *getErrorValue() { |
630 | ensureError(); |
631 | ensureChecked(); |
632 | return payload_.asErrorValue.get().get(); |
633 | } |
634 | |
635 | /// Getter for payload_ to retrieve an ErrorValue. Ensures that an ErrorValue |
636 | /// is contained and that it has been checked. |
637 | inline const GlowErrorValue *getErrorValue() const { |
638 | ensureError(); |
639 | ensureChecked(); |
640 | return payload_.asErrorValue.get().get(); |
641 | } |
642 | |
643 | /// \returns the ErrorValue contents of payload_ by moving them. Marks the |
644 | /// Expected as checked no matter what. |
645 | /// NOTE: This is the only way to mark an Expected that contains an ErrorValue |
646 | /// as checked. |
647 | inline std::unique_ptr<GlowErrorValue> takeErrorValue() { |
648 | ensureError(); |
649 | setChecked(true); |
650 | return std::move(payload_.asErrorValue.get()); |
651 | } |
652 | |
653 | /// Sets payload_ with a value of type T \p value. If \p skipCheck is true |
654 | /// then don't check that the current payload has been checked before setting |
655 | /// otherwise do check. |
656 | /// NOTE: Only constructors of Expected should use skipCheck. |
657 | inline void setValue(T value, bool skipCheck = false) { |
658 | static_assert(!std::is_reference<T>::value, |
659 | "Expected has not been equipped to hold references yet." ); |
660 | |
661 | if (!skipCheck) { |
662 | ensureChecked(); |
663 | } |
664 | setIsError(false); |
665 | return payload_.asValue.set(std::move(value)); |
666 | } |
667 | |
668 | /// \returns a value T contained in payload_. Ensures that value is contained |
669 | /// by payload_ and that it has been checked already. |
670 | inline T *getValue() { |
671 | ensureValue(); |
672 | ensureChecked(); |
673 | return &payload_.asValue.get(); |
674 | } |
675 | |
676 | /// \returns a value T contained in payload_. Ensures that value is contained |
677 | /// by payload_ and that it has been checked already. |
678 | inline const T *getValue() const { |
679 | ensureValue(); |
680 | ensureChecked(); |
681 | return &payload_.asValue.get(); |
682 | } |
683 | |
684 | /// \returns the value contents of payload_ by moving them. Marks the Expected |
685 | /// as checked no matter what. |
686 | inline T takeValue() { |
687 | ensureValue(); |
688 | setChecked(true); |
689 | return std::move(payload_.asValue.get()); |
690 | } |
691 | |
692 | /// Add \p filename and \p lineNumber to the contained ErrorValue's stack for |
693 | /// logging. |
694 | void addToStack(const std::string &fileName, size_t lineNumber, |
695 | const std::string &message = "" ) { |
696 | if (getIsError()) { |
697 | payload_.asErrorValue.get()->addToStack(fileName, lineNumber, message); |
698 | } |
699 | } |
700 | |
701 | public: |
702 | /// Construct an Expected from an Error. The error must contain an ErrorValue. |
703 | /// Marks the Error as checked. |
704 | GlowExpected(GlowError error) { |
705 | assert(error.hasErrorValue() && |
706 | "Must have an ErrorValue to construct an Expected from an Error" ); |
707 | setErrorValue(std::move(error.takeErrorValue()), /*skipCheck*/ true); |
708 | } |
709 | |
710 | /// Disallow construction of Expected from ErrorSuccess and ErrorEmpty. |
711 | GlowExpected(GlowErrorSuccess) = delete; |
712 | GlowExpected(GlowErrorEmpty) = delete; |
713 | |
714 | /// Move construct Expected<T> from a value of type OtherT as long as OtherT |
715 | /// is convertible to T. |
716 | template <typename OtherT> |
717 | GlowExpected( |
718 | OtherT &&other, |
719 | typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = |
720 | nullptr) { |
721 | setValue(std::forward<OtherT>(other), /*skipCheck*/ true); |
722 | } |
723 | |
724 | /// Move construct Expected<T> from another Expected<T>. |
725 | GlowExpected(GlowExpected &&other) { |
726 | if (other.getIsError()) { |
727 | setErrorValue(std::move(other.takeErrorValue()), |
728 | /*skipCheck*/ true); |
729 | } else { |
730 | setValue(std::move(other.takeValue()), /*skipCheck*/ true); |
731 | } |
732 | } |
733 | |
734 | /// Move construct Expected<T> from Expected<OtherT> as long as OtherT is |
735 | /// convertible to T. |
736 | template <typename OtherT> |
737 | GlowExpected( |
738 | GlowExpected<OtherT> &&other, |
739 | typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = |
740 | nullptr) { |
741 | if (other.getIsError()) { |
742 | setErrorValue(std::move(other.takeErrorValue()), |
743 | /*skipCheck*/ true); |
744 | } else { |
745 | setValue(std::move(other.takeValue()), /*skipCheck*/ true); |
746 | } |
747 | } |
748 | |
749 | /// Move assign Expected<T> from another Expected<T>. |
750 | GlowExpected &operator=(GlowExpected &&other) { |
751 | if (other.getIsError()) { |
752 | setErrorValue(std::move(other.takeErrorValue())); |
753 | } else { |
754 | setValue(std::move(other.takeValue())); |
755 | } |
756 | return *this; |
757 | } |
758 | |
759 | /// Destructor for Expected, manually destroys the constents of payload_. |
760 | ~GlowExpected() { |
761 | if (getIsError()) { |
762 | payload_.asErrorValue.destroy(); |
763 | } else { |
764 | payload_.asValue.destroy(); |
765 | } |
766 | } |
767 | |
768 | /// Overload for operator bool that returns true if no ErrorValue is |
769 | /// contained. Marks the state as checked if no ErrorValue is contained. |
770 | explicit operator bool() { |
771 | bool isError = getIsError(); |
772 | if (!isError) { |
773 | setChecked(true); |
774 | } |
775 | return !isError; |
776 | } |
777 | |
778 | /// Get a reference to a value contained by payload_. |
779 | T &get() { return *getValue(); } |
780 | |
781 | /// Get a const reference to a value contained by payload_. |
782 | const T &get() const { return *getValue(); } |
783 | |
784 | /// Construct and \returns an Error and takes ownership of any ErrorValue in |
785 | /// payload_. If no ErrorValue is in payload_ then return Error::success(). |
786 | /// Marks the Exected as checked no matter what. |
787 | GlowError takeError() { |
788 | if (getIsError()) { |
789 | return GlowError(takeErrorValue()); |
790 | } |
791 | setChecked(true); |
792 | return GlowError::success(); |
793 | } |
794 | |
795 | /// \returns a pointer to the contained ErrorValue or nullptr if no |
796 | /// ErrorValue is contained in this Expected. |
797 | inline const GlowErrorValue *peekErrorValue() const { |
798 | if (getIsError()) { |
799 | return payload_.asErrorValue.get().get(); |
800 | } |
801 | return nullptr; |
802 | } |
803 | |
804 | T *operator->() { return getValue(); } |
805 | |
806 | const T *operator->() const { return getValue(); } |
807 | |
808 | T &operator*() { return *getValue(); } |
809 | |
810 | const T &operator*() const { return *getValue(); } |
811 | |
812 | /// NOTE: Only use for testing! |
813 | bool isChecked_() const { return isChecked(); } |
814 | }; |
815 | |
816 | /// Given an Expected<T>, asserts that it contains a value T and \returns it. If |
817 | /// an ErrorValue is contained in the expected then logs this along with \p |
818 | /// fileName and \p lineNumber and aborts. |
819 | template <typename T> |
820 | T exitOnError(const char *fileName, size_t lineNumber, |
821 | GlowExpected<T> expected) { |
822 | if (expected) { |
823 | return expected.takeValue(); |
824 | } else { |
825 | auto error = expected.takeError(); |
826 | std::unique_ptr<GlowErrorValue> errorValue = |
827 | detail::takeErrorValue(std::move(error)); |
828 | assert(errorValue != nullptr && "Expected should have a non-null " |
829 | "ErrorValue if bool(expected) is false" ); |
830 | errorValue->addToStack(fileName, lineNumber); |
831 | LOG(FATAL) << "exitOnError(Expected<T>) got an unexpected ErrorValue: " |
832 | << (*errorValue); |
833 | } |
834 | } |
835 | |
836 | /// Constructs an ErrorValue from \p args then wraps and \returns it in an |
837 | /// Error. |
838 | /// NOTE: this should not be used directly, use macros defined at the top of |
839 | /// Error.h instead. |
840 | template <typename... Args> |
841 | GlowError makeError(const char *fileName, size_t lineNumber, Args &&...args) { |
842 | auto errorValue = std::unique_ptr<GlowErrorValue>( |
843 | new GlowErrorValue(std::forward<Args>(args)...)); |
844 | errorValue->addToStack(fileName, lineNumber); |
845 | return GlowError(std::move(errorValue)); |
846 | } |
847 | |
848 | /// Given an Error \p error, destroys the Error and returns true if an |
849 | /// ErrorValue was contained. Logs if \p log is true and uses \p fileName and \p |
850 | /// lineNumber for additional logging information. If \p warning is true then |
851 | /// the log message will replace "Error" with "Warning", this is useful for when |
852 | /// Errors are used in non-exceptional conditions. |
853 | /// NOTE: this should not be used |
854 | /// directly, use macros defined at the top of Error.h instead. |
855 | bool errorToBool(const char *fileName, size_t lineNumber, GlowError error, |
856 | bool log = true, bool warning = false); |
857 | |
858 | /// Given an Error \p error, destroys the Error and returns a string that is the |
859 | /// result of calling log() on the ErrorValue it contained if any and "success" |
860 | /// otherwise. If \p warning is true then the log message will replace "Error" |
861 | /// with "Warning", this is useful for when Errors are used in non-exceptional |
862 | /// conditions. |
863 | /// NOTE: this should not be used directly, use macros defined at |
864 | /// the top of Error.h instead. |
865 | std::string errorToString(const char *fileName, size_t lineNumber, |
866 | GlowError error, bool warning = false); |
867 | |
868 | /// Given an Error \p error, destroys the Error. Logs if \p log is true and uses |
869 | /// \p fileName and \p lineNumber for additional logging information. If \p |
870 | /// warning is true then the log message will replace "Error" with "Warning", |
871 | /// this is useful for when Errors are used in non-exceptional conditions. |
872 | /// NOTE: this should not be used directly, use macros defined at the top of |
873 | /// Error.h instead. |
874 | void errorToVoid(const char *fileName, size_t lineNumber, GlowError error, |
875 | bool log = true, bool warning = false); |
876 | } // namespace detail |
877 | |
878 | /// This class holds an Error provided via the add method. If an Error is |
879 | /// added when the class already holds an Error, it will discard the new Error |
880 | /// in favor of the original one. All methods in OneErrOnly are thread-safe. |
881 | class OneErrOnly { |
882 | Error err_ = Error::empty(); |
883 | std::mutex m_; |
884 | |
885 | public: |
886 | /// Add a new Error \p err to be stored. If an existing Error has |
887 | /// already been added then the contents of the new error will be logged and |
888 | /// the new err will be discarded. \returns true if \p err was stored and |
889 | /// \returns false otherwise. If \p err is an empty Error then does nothing |
890 | /// and \returns false; |
891 | bool set(Error err); |
892 | |
893 | /// \returns the stored Error clearing out the storage of the class. |
894 | Error get(); |
895 | |
896 | /// \returns true if contains an Error and false otherwise. |
897 | bool containsErr(); |
898 | }; |
899 | |
900 | } // end namespace glow |
901 | |
902 | #endif // GLOW_SUPPORT_ERROR_H |
903 | |