| //===- 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<T> smart pointer. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_SUPPORT_ERROROR_H | |
| #define LLVM_SUPPORT_ERROROR_H | |
| #include "llvm/ADT/PointerIntPair.h" | |
| #include "llvm/Support/AlignOf.h" | |
| #include <cassert> | |
| #include <system_error> | |
| #include <type_traits> | |
| namespace llvm { | |
| /// \brief Stores a reference that can be changed. | |
| template <typename T> | |
| class ReferenceStorage { | |
| T *Storage; | |
| public: | |
| ReferenceStorage(T &Ref) : Storage(&Ref) {} | |
| operator T &() const { return *Storage; } | |
| T &get() const { return *Storage; } | |
| }; | |
| /// \brief Represents either an error or a value T. | |
| /// | |
| /// ErrorOr<T> 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<Buffer> 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<std::remove_reference<T>::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 T> | |
| class ErrorOr { | |
| template <class OtherT> friend class ErrorOr; | |
| static const bool isRef = std::is_reference<T>::value; | |
| typedef ReferenceStorage<typename std::remove_reference<T>::type> wrap; | |
| public: | |
| typedef typename std::conditional<isRef, wrap, T>::type storage_type; | |
| private: | |
| typedef typename std::remove_reference<T>::type &reference; | |
| typedef const typename std::remove_reference<T>::type &const_reference; | |
| typedef typename std::remove_reference<T>::type *pointer; | |
| typedef const typename std::remove_reference<T>::type *const_pointer; | |
| public: | |
| template <class E> | |
| ErrorOr(E ErrorCode, | |
| typename std::enable_if<std::is_error_code_enum<E>::value || | |
| std::is_error_condition_enum<E>::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 <class OtherT> | |
| ErrorOr(OtherT &&Val, | |
| typename std::enable_if<std::is_convertible<OtherT, T>::value>::type | |
| * = nullptr) | |
| : HasError(false) { | |
| new (getStorage()) storage_type(std::forward<OtherT>(Val)); | |
| } | |
| ErrorOr(const ErrorOr &Other) { | |
| copyConstruct(Other); | |
| } | |
| template <class OtherT> | |
| ErrorOr( | |
| const ErrorOr<OtherT> &Other, | |
| typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * = | |
| nullptr) { | |
| copyConstruct(Other); | |
| } | |
| template <class OtherT> | |
| explicit ErrorOr( | |
| const ErrorOr<OtherT> &Other, | |
| typename std::enable_if< | |
| !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) { | |
| copyConstruct(Other); | |
| } | |
| ErrorOr(ErrorOr &&Other) { | |
| moveConstruct(std::move(Other)); | |
| } | |
| template <class OtherT> | |
| ErrorOr( | |
| ErrorOr<OtherT> &&Other, | |
| typename std::enable_if<std::is_convertible<OtherT, T>::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 <class OtherT> | |
| explicit ErrorOr( | |
| ErrorOr<OtherT> &&Other, | |
| typename std::enable_if<!std::is_convertible<OtherT, T>::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(); | |
| } | |
| /// \brief Return false if there is an error. | |
| explicit operator bool() const { | |
| return !HasError; | |
| } | |
| reference get() { return *getStorage(); } | |
| const_reference get() const { return const_cast<ErrorOr<T> *>(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 <class OtherT> | |
| void copyConstruct(const ErrorOr<OtherT> &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 <class T1> | |
| static bool compareThisIfSameType(const T1 &a, const T1 &b) { | |
| return &a == &b; | |
| } | |
| template <class T1, class T2> | |
| static bool compareThisIfSameType(const T1 &a, const T2 &b) { | |
| return false; | |
| } | |
| template <class OtherT> | |
| void copyAssign(const ErrorOr<OtherT> &Other) { | |
| if (compareThisIfSameType(*this, Other)) | |
| return; | |
| this->~ErrorOr(); | |
| new (this) ErrorOr(Other); | |
| } | |
| template <class OtherT> | |
| void moveConstruct(ErrorOr<OtherT> &&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 <class OtherT> | |
| void moveAssign(ErrorOr<OtherT> &&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<storage_type*>(TStorage.buffer); | |
| } | |
| const storage_type *getStorage() const { | |
| assert(!HasError && "Cannot get value when an error exists!"); | |
| return reinterpret_cast<const storage_type*>(TStorage.buffer); | |
| } | |
| std::error_code *getErrorStorage() { | |
| assert(HasError && "Cannot get error when a value exists!"); | |
| return reinterpret_cast<std::error_code *>(ErrorStorage.buffer); | |
| } | |
| const std::error_code *getErrorStorage() const { | |
| return const_cast<ErrorOr<T> *>(this)->getErrorStorage(); | |
| } | |
| union { | |
| AlignedCharArrayUnion<storage_type> TStorage; | |
| AlignedCharArrayUnion<std::error_code> ErrorStorage; | |
| }; | |
| bool HasError : 1; | |
| }; | |
| template <class T, class E> | |
| typename std::enable_if<std::is_error_code_enum<E>::value || | |
| std::is_error_condition_enum<E>::value, | |
| bool>::type | |
| operator==(const ErrorOr<T> &Err, E Code) { | |
| return Err.getError() == Code; | |
| } | |
| } // end namespace llvm | |
| #endif // LLVM_SUPPORT_ERROROR_H |