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
160namespace glow {
161
162/// Forward declarations.
163namespace detail {
164class GlowError;
165class GlowErrorSuccess;
166class GlowErrorEmpty;
167class GlowErrorValue;
168template <typename T> class GlowExpected;
169} // namespace detail
170
171/// Type aliases to decouple Error and Expected from underlying implementation.
172using Error = detail::GlowError;
173using ErrorSuccess = detail::GlowErrorSuccess;
174using ErrorEmpty = detail::GlowErrorEmpty;
175using ErrorValue = detail::GlowErrorValue;
176template <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.
180namespace 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
185static constexpr bool enableCheckingErrors = false;
186#else
187static constexpr bool enableCheckingErrors = true;
188#endif
189
190/// Is true_type only if applied to Error or a descendant.
191template <typename T> struct IsError : public std::is_base_of<GlowError, T> {};
192
193/// Is true_type only if applied to Expected.
194template <typename> struct IsExpected : public std::false_type {};
195template <typename T>
196struct 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.
203template <bool DoChecks> class CheckState;
204
205/// Specialization of CheckState with checking enabled.
206template <> class CheckState<true> {
207 /// Whether or not the a check has occurred.
208 bool checked_ = false;
209
210public:
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.
228template <> class CheckState<false> {
229public:
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.
239template <typename T> class Opaque {
240private:
241 alignas(T) char payload_[sizeof(T)];
242
243public:
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.
262std::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.
266void 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.
272class GlowErrorValue final {
273public:
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
397private:
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.
412template <typename StreamT>
413StreamT &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.
420class 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
462public:
463#else
464protected:
465#endif
466 /// Construct a new empty Error.
467 explicit GlowError() { setErrorValue(nullptr, /*skipCheck*/ true); }
468
469public:
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().
549class GlowErrorSuccess final : public GlowError {};
550
551/// See declaration in Error for details.
552inline 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.
558class GlowErrorEmpty final : public GlowError {};
559
560/// See declaration in Error for details.
561inline GlowErrorEmpty GlowError::empty() { return GlowErrorEmpty(); }
562
563template <typename T> class GlowExpected;
564template <typename T>
565T 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.
572template <typename T>
573class 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
701public:
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.
819template <typename T>
820T 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.
840template <typename... Args>
841GlowError 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.
855bool 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.
865std::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.
874void 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.
881class OneErrOnly {
882 Error err_ = Error::empty();
883 std::mutex m_;
884
885public:
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