//===- llvm/Support/ErrorOr.h - Error Smart Pointer -------------*- C++ -*-===// // // The LLVM Linker // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// /// Provides ErrorOr smart pointer. /// //===----------------------------------------------------------------------===// #ifndef WPIUTIL_WPI_ERROROR_H #define WPIUTIL_WPI_ERROROR_H #include "wpi/AlignOf.h" #include #include #include #include namespace wpi { /// Represents either an error or a value T. /// /// ErrorOr is a pointer-like class that represents the result of an /// operation. The result is either an error, or a value of type T. This is /// designed to emulate the usage of returning a pointer where nullptr indicates /// failure. However instead of just knowing that the operation failed, we also /// have an error_code and optional user data that describes why it failed. /// /// It is used like the following. /// \code /// ErrorOr getBuffer(); /// /// auto buffer = getBuffer(); /// if (error_code ec = buffer.getError()) /// return ec; /// buffer->write("adena"); /// \endcode /// /// /// Implicit conversion to bool returns true if there is a usable value. The /// unary * and -> operators provide pointer like access to the value. Accessing /// the value when there is an error has undefined behavior. /// /// When T is a reference type the behavior is slightly different. The reference /// is held in a std::reference_wrapper::type>, and /// there is special handling to make operator -> work as if T was not a /// reference. /// /// T cannot be a rvalue reference. template class ErrorOr { template friend class ErrorOr; static const bool isRef = std::is_reference::value; using wrap = std::reference_wrapper::type>; public: using storage_type = typename std::conditional::type; private: using reference = typename std::remove_reference::type &; using const_reference = const typename std::remove_reference::type &; using pointer = typename std::remove_reference::type *; using const_pointer = const typename std::remove_reference::type *; public: template ErrorOr(E ErrorCode, typename std::enable_if::value || std::is_error_condition_enum::value, void *>::type = nullptr) : HasError(true) { new (getErrorStorage()) std::error_code(make_error_code(ErrorCode)); } ErrorOr(std::error_code EC) : HasError(true) { new (getErrorStorage()) std::error_code(EC); } template ErrorOr(OtherT &&Val, typename std::enable_if::value>::type * = nullptr) : HasError(false) { new (getStorage()) storage_type(std::forward(Val)); } ErrorOr(const ErrorOr &Other) { copyConstruct(Other); } template ErrorOr( const ErrorOr &Other, typename std::enable_if::value>::type * = nullptr) { copyConstruct(Other); } template explicit ErrorOr( const ErrorOr &Other, typename std::enable_if< !std::is_convertible::value>::type * = nullptr) { copyConstruct(Other); } ErrorOr(ErrorOr &&Other) { moveConstruct(std::move(Other)); } template ErrorOr( ErrorOr &&Other, typename std::enable_if::value>::type * = nullptr) { moveConstruct(std::move(Other)); } // This might eventually need SFINAE but it's more complex than is_convertible // & I'm too lazy to write it right now. template explicit ErrorOr( ErrorOr &&Other, typename std::enable_if::value>::type * = nullptr) { moveConstruct(std::move(Other)); } ErrorOr &operator=(const ErrorOr &Other) { copyAssign(Other); return *this; } ErrorOr &operator=(ErrorOr &&Other) { moveAssign(std::move(Other)); return *this; } ~ErrorOr() { if (!HasError) getStorage()->~storage_type(); } /// Return false if there is an error. explicit operator bool() const { return !HasError; } reference get() { return *getStorage(); } const_reference get() const { return const_cast *>(this)->get(); } std::error_code getError() const { return HasError ? *getErrorStorage() : std::error_code(); } pointer operator ->() { return toPointer(getStorage()); } const_pointer operator->() const { return toPointer(getStorage()); } reference operator *() { return *getStorage(); } const_reference operator*() const { return *getStorage(); } private: template void copyConstruct(const ErrorOr &Other) { if (!Other.HasError) { // Get the other value. HasError = false; new (getStorage()) storage_type(*Other.getStorage()); } else { // Get other's error. HasError = true; new (getErrorStorage()) std::error_code(Other.getError()); } } template static bool compareThisIfSameType(const T1 &a, const T1 &b) { return &a == &b; } template static bool compareThisIfSameType(const T1 &a, const T2 &b) { return false; } template void copyAssign(const ErrorOr &Other) { if (compareThisIfSameType(*this, Other)) return; this->~ErrorOr(); new (this) ErrorOr(Other); } template void moveConstruct(ErrorOr &&Other) { if (!Other.HasError) { // Get the other value. HasError = false; new (getStorage()) storage_type(std::move(*Other.getStorage())); } else { // Get other's error. HasError = true; new (getErrorStorage()) std::error_code(Other.getError()); } } template void moveAssign(ErrorOr &&Other) { if (compareThisIfSameType(*this, Other)) return; this->~ErrorOr(); new (this) ErrorOr(std::move(Other)); } pointer toPointer(pointer Val) { return Val; } const_pointer toPointer(const_pointer Val) const { return Val; } pointer toPointer(wrap *Val) { return &Val->get(); } const_pointer toPointer(const wrap *Val) const { return &Val->get(); } storage_type *getStorage() { assert(!HasError && "Cannot get value when an error exists!"); return reinterpret_cast(TStorage.buffer); } const storage_type *getStorage() const { assert(!HasError && "Cannot get value when an error exists!"); return reinterpret_cast(TStorage.buffer); } std::error_code *getErrorStorage() { assert(HasError && "Cannot get error when a value exists!"); return reinterpret_cast(ErrorStorage.buffer); } const std::error_code *getErrorStorage() const { return const_cast *>(this)->getErrorStorage(); } union { AlignedCharArrayUnion TStorage; AlignedCharArrayUnion ErrorStorage; }; bool HasError : 1; }; template typename std::enable_if::value || std::is_error_condition_enum::value, bool>::type operator==(const ErrorOr &Err, E Code) { return Err.getError() == Code; } } // end namespace wpi #endif // LLVM_SUPPORT_ERROROR_H