LLVM API Documentation

ErrorOr.h
Go to the documentation of this file.
00001 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -----------------------===//
00002 //
00003 //                             The LLVM Linker
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 ///
00010 /// \file
00011 ///
00012 /// Provides ErrorOr<T> smart pointer.
00013 ///
00014 //===----------------------------------------------------------------------===//
00015 
00016 #ifndef LLVM_SUPPORT_ERROROR_H
00017 #define LLVM_SUPPORT_ERROROR_H
00018 
00019 #include "llvm/ADT/PointerIntPair.h"
00020 #include "llvm/Support/AlignOf.h"
00021 #include <cassert>
00022 #include <system_error>
00023 #include <type_traits>
00024 
00025 namespace llvm {
00026 template<class T, class V>
00027 typename std::enable_if< std::is_constructible<T, V>::value
00028                        , typename std::remove_reference<V>::type>::type &&
00029  moveIfMoveConstructible(V &Val) {
00030   return std::move(Val);
00031 }
00032 
00033 template<class T, class V>
00034 typename std::enable_if< !std::is_constructible<T, V>::value
00035                        , typename std::remove_reference<V>::type>::type &
00036 moveIfMoveConstructible(V &Val) {
00037   return Val;
00038 }
00039 
00040 /// \brief Stores a reference that can be changed.
00041 template <typename T>
00042 class ReferenceStorage {
00043   T *Storage;
00044 
00045 public:
00046   ReferenceStorage(T &Ref) : Storage(&Ref) {}
00047 
00048   operator T &() const { return *Storage; }
00049   T &get() const { return *Storage; }
00050 };
00051 
00052 /// \brief Represents either an error or a value T.
00053 ///
00054 /// ErrorOr<T> is a pointer-like class that represents the result of an
00055 /// operation. The result is either an error, or a value of type T. This is
00056 /// designed to emulate the usage of returning a pointer where nullptr indicates
00057 /// failure. However instead of just knowing that the operation failed, we also
00058 /// have an error_code and optional user data that describes why it failed.
00059 ///
00060 /// It is used like the following.
00061 /// \code
00062 ///   ErrorOr<Buffer> getBuffer();
00063 ///
00064 ///   auto buffer = getBuffer();
00065 ///   if (error_code ec = buffer.getError())
00066 ///     return ec;
00067 ///   buffer->write("adena");
00068 /// \endcode
00069 ///
00070 ///
00071 /// An implicit conversion to bool provides a way to check if there was an
00072 /// error. The unary * and -> operators provide pointer like access to the
00073 /// value. Accessing the value when there is an error has undefined behavior.
00074 ///
00075 /// When T is a reference type the behaivor is slightly different. The reference
00076 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
00077 /// there is special handling to make operator -> work as if T was not a
00078 /// reference.
00079 ///
00080 /// T cannot be a rvalue reference.
00081 template<class T>
00082 class ErrorOr {
00083   template <class OtherT> friend class ErrorOr;
00084   static const bool isRef = std::is_reference<T>::value;
00085   typedef ReferenceStorage<typename std::remove_reference<T>::type> wrap;
00086 
00087 public:
00088   typedef typename std::conditional<isRef, wrap, T>::type storage_type;
00089 
00090 private:
00091   typedef typename std::remove_reference<T>::type &reference;
00092   typedef const typename std::remove_reference<T>::type &const_reference;
00093   typedef typename std::remove_reference<T>::type *pointer;
00094 
00095 public:
00096   template <class E>
00097   ErrorOr(E ErrorCode,
00098           typename std::enable_if<std::is_error_code_enum<E>::value ||
00099                                       std::is_error_condition_enum<E>::value,
00100                                   void *>::type = 0)
00101       : HasError(true) {
00102     new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
00103   }
00104 
00105   ErrorOr(std::error_code EC) : HasError(true) {
00106     new (getErrorStorage()) std::error_code(EC);
00107   }
00108 
00109   ErrorOr(T Val) : HasError(false) {
00110     new (getStorage()) storage_type(moveIfMoveConstructible<storage_type>(Val));
00111   }
00112 
00113   ErrorOr(const ErrorOr &Other) {
00114     copyConstruct(Other);
00115   }
00116 
00117   template <class OtherT>
00118   ErrorOr(
00119       const ErrorOr<OtherT> &Other,
00120       typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
00121           nullptr) {
00122     copyConstruct(Other);
00123   }
00124 
00125   template <class OtherT>
00126   explicit ErrorOr(
00127       const ErrorOr<OtherT> &Other,
00128       typename std::enable_if<
00129           !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
00130     copyConstruct(Other);
00131   }
00132 
00133   ErrorOr(ErrorOr &&Other) {
00134     moveConstruct(std::move(Other));
00135   }
00136 
00137   template <class OtherT>
00138   ErrorOr(
00139       ErrorOr<OtherT> &&Other,
00140       typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
00141           nullptr) {
00142     moveConstruct(std::move(Other));
00143   }
00144 
00145   // This might eventually need SFINAE but it's more complex than is_convertible
00146   // & I'm too lazy to write it right now.
00147   template <class OtherT>
00148   explicit ErrorOr(
00149       ErrorOr<OtherT> &&Other,
00150       typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
00151           nullptr) {
00152     moveConstruct(std::move(Other));
00153   }
00154 
00155   ErrorOr &operator=(const ErrorOr &Other) {
00156     copyAssign(Other);
00157     return *this;
00158   }
00159 
00160   ErrorOr &operator=(ErrorOr &&Other) {
00161     moveAssign(std::move(Other));
00162     return *this;
00163   }
00164 
00165   ~ErrorOr() {
00166     if (!HasError)
00167       getStorage()->~storage_type();
00168   }
00169 
00170   /// \brief Return false if there is an error.
00171   LLVM_EXPLICIT operator bool() const {
00172     return !HasError;
00173   }
00174 
00175   reference get() { return *getStorage(); }
00176   const_reference get() const { return const_cast<ErrorOr<T> >(this)->get(); }
00177 
00178   std::error_code getError() const {
00179     return HasError ? *getErrorStorage() : std::error_code();
00180   }
00181 
00182   pointer operator ->() {
00183     return toPointer(getStorage());
00184   }
00185 
00186   reference operator *() {
00187     return *getStorage();
00188   }
00189 
00190 private:
00191   template <class OtherT>
00192   void copyConstruct(const ErrorOr<OtherT> &Other) {
00193     if (!Other.HasError) {
00194       // Get the other value.
00195       HasError = false;
00196       new (getStorage()) storage_type(*Other.getStorage());
00197     } else {
00198       // Get other's error.
00199       HasError = true;
00200       new (getErrorStorage()) std::error_code(Other.getError());
00201     }
00202   }
00203 
00204   template <class T1>
00205   static bool compareThisIfSameType(const T1 &a, const T1 &b) {
00206     return &a == &b;
00207   }
00208 
00209   template <class T1, class T2>
00210   static bool compareThisIfSameType(const T1 &a, const T2 &b) {
00211     return false;
00212   }
00213 
00214   template <class OtherT>
00215   void copyAssign(const ErrorOr<OtherT> &Other) {
00216     if (compareThisIfSameType(*this, Other))
00217       return;
00218 
00219     this->~ErrorOr();
00220     new (this) ErrorOr(Other);
00221   }
00222 
00223   template <class OtherT>
00224   void moveConstruct(ErrorOr<OtherT> &&Other) {
00225     if (!Other.HasError) {
00226       // Get the other value.
00227       HasError = false;
00228       new (getStorage()) storage_type(std::move(*Other.getStorage()));
00229     } else {
00230       // Get other's error.
00231       HasError = true;
00232       new (getErrorStorage()) std::error_code(Other.getError());
00233     }
00234   }
00235 
00236   template <class OtherT>
00237   void moveAssign(ErrorOr<OtherT> &&Other) {
00238     if (compareThisIfSameType(*this, Other))
00239       return;
00240 
00241     this->~ErrorOr();
00242     new (this) ErrorOr(std::move(Other));
00243   }
00244 
00245   pointer toPointer(pointer Val) {
00246     return Val;
00247   }
00248 
00249   pointer toPointer(wrap *Val) {
00250     return &Val->get();
00251   }
00252 
00253   storage_type *getStorage() {
00254     assert(!HasError && "Cannot get value when an error exists!");
00255     return reinterpret_cast<storage_type*>(TStorage.buffer);
00256   }
00257 
00258   const storage_type *getStorage() const {
00259     assert(!HasError && "Cannot get value when an error exists!");
00260     return reinterpret_cast<const storage_type*>(TStorage.buffer);
00261   }
00262 
00263   std::error_code *getErrorStorage() {
00264     assert(HasError && "Cannot get error when a value exists!");
00265     return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
00266   }
00267 
00268   const std::error_code *getErrorStorage() const {
00269     return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
00270   }
00271 
00272 
00273   union {
00274     AlignedCharArrayUnion<storage_type> TStorage;
00275     AlignedCharArrayUnion<std::error_code> ErrorStorage;
00276   };
00277   bool HasError : 1;
00278 };
00279 
00280 template <class T, class E>
00281 typename std::enable_if<std::is_error_code_enum<E>::value ||
00282                             std::is_error_condition_enum<E>::value,
00283                         bool>::type
00284 operator==(ErrorOr<T> &Err, E Code) {
00285   return std::error_code(Err) == Code;
00286 }
00287 } // end namespace llvm
00288 
00289 #endif