LLVM API Documentation
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