LLVM  3.7.0
ErrorOr.h
Go to the documentation of this file.
1 //===- llvm/Support/ErrorOr.h - Error Smart Pointer -----------------------===//
2 //
3 // The LLVM Linker
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 ///
12 /// Provides ErrorOr<T> smart pointer.
13 ///
14 //===----------------------------------------------------------------------===//
15 
16 #ifndef LLVM_SUPPORT_ERROROR_H
17 #define LLVM_SUPPORT_ERROROR_H
18 
20 #include "llvm/Support/AlignOf.h"
21 #include <cassert>
22 #include <system_error>
23 #include <type_traits>
24 
25 namespace llvm {
26 template<class T, class V>
27 typename std::enable_if< std::is_constructible<T, V>::value
28  , typename std::remove_reference<V>::type>::type &&
30  return std::move(Val);
31 }
32 
33 template<class T, class V>
34 typename std::enable_if< !std::is_constructible<T, V>::value
35  , typename std::remove_reference<V>::type>::type &
37  return Val;
38 }
39 
40 /// \brief Stores a reference that can be changed.
41 template <typename T>
43  T *Storage;
44 
45 public:
46  ReferenceStorage(T &Ref) : Storage(&Ref) {}
47 
48  operator T &() const { return *Storage; }
49  T &get() const { return *Storage; }
50 };
51 
52 /// \brief Represents either an error or a value T.
53 ///
54 /// ErrorOr<T> is a pointer-like class that represents the result of an
55 /// operation. The result is either an error, or a value of type T. This is
56 /// designed to emulate the usage of returning a pointer where nullptr indicates
57 /// failure. However instead of just knowing that the operation failed, we also
58 /// have an error_code and optional user data that describes why it failed.
59 ///
60 /// It is used like the following.
61 /// \code
62 /// ErrorOr<Buffer> getBuffer();
63 ///
64 /// auto buffer = getBuffer();
65 /// if (error_code ec = buffer.getError())
66 /// return ec;
67 /// buffer->write("adena");
68 /// \endcode
69 ///
70 ///
71 /// Implicit conversion to bool returns true if there is a usable value. The
72 /// unary * and -> operators provide pointer like access to the value. Accessing
73 /// the value when there is an error has undefined behavior.
74 ///
75 /// When T is a reference type the behaivor is slightly different. The reference
76 /// is held in a std::reference_wrapper<std::remove_reference<T>::type>, and
77 /// there is special handling to make operator -> work as if T was not a
78 /// reference.
79 ///
80 /// T cannot be a rvalue reference.
81 template<class T>
82 class ErrorOr {
83  template <class OtherT> friend class ErrorOr;
84  static const bool isRef = std::is_reference<T>::value;
86 
87 public:
88  typedef typename std::conditional<isRef, wrap, T>::type storage_type;
89 
90 private:
91  typedef typename std::remove_reference<T>::type &reference;
92  typedef const typename std::remove_reference<T>::type &const_reference;
93  typedef typename std::remove_reference<T>::type *pointer;
94 
95 public:
96  template <class E>
97  ErrorOr(E ErrorCode,
98  typename std::enable_if<std::is_error_code_enum<E>::value ||
99  std::is_error_condition_enum<E>::value,
100  void *>::type = 0)
101  : HasError(true) {
102  new (getErrorStorage()) std::error_code(make_error_code(ErrorCode));
103  }
104 
105  ErrorOr(std::error_code EC) : HasError(true) {
106  new (getErrorStorage()) std::error_code(EC);
107  }
108 
109  ErrorOr(T Val) : HasError(false) {
110  new (getStorage()) storage_type(moveIfMoveConstructible<storage_type>(Val));
111  }
112 
114  copyConstruct(Other);
115  }
116 
117  template <class OtherT>
119  const ErrorOr<OtherT> &Other,
120  typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
121  nullptr) {
122  copyConstruct(Other);
123  }
124 
125  template <class OtherT>
126  explicit ErrorOr(
127  const ErrorOr<OtherT> &Other,
128  typename std::enable_if<
129  !std::is_convertible<OtherT, const T &>::value>::type * = nullptr) {
130  copyConstruct(Other);
131  }
132 
134  moveConstruct(std::move(Other));
135  }
136 
137  template <class OtherT>
140  typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
141  nullptr) {
142  moveConstruct(std::move(Other));
143  }
144 
145  // This might eventually need SFINAE but it's more complex than is_convertible
146  // & I'm too lazy to write it right now.
147  template <class OtherT>
148  explicit ErrorOr(
150  typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
151  nullptr) {
152  moveConstruct(std::move(Other));
153  }
154 
156  copyAssign(Other);
157  return *this;
158  }
159 
161  moveAssign(std::move(Other));
162  return *this;
163  }
164 
166  if (!HasError)
167  getStorage()->~storage_type();
168  }
169 
170  /// \brief Return false if there is an error.
171  explicit operator bool() const {
172  return !HasError;
173  }
174 
175  reference get() { return *getStorage(); }
176  const_reference get() const { return const_cast<ErrorOr<T> *>(this)->get(); }
177 
178  std::error_code getError() const {
179  return HasError ? *getErrorStorage() : std::error_code();
180  }
181 
182  pointer operator ->() {
183  return toPointer(getStorage());
184  }
185 
186  reference operator *() {
187  return *getStorage();
188  }
189 
190 private:
191  template <class OtherT>
192  void copyConstruct(const ErrorOr<OtherT> &Other) {
193  if (!Other.HasError) {
194  // Get the other value.
195  HasError = false;
196  new (getStorage()) storage_type(*Other.getStorage());
197  } else {
198  // Get other's error.
199  HasError = true;
200  new (getErrorStorage()) std::error_code(Other.getError());
201  }
202  }
203 
204  template <class T1>
205  static bool compareThisIfSameType(const T1 &a, const T1 &b) {
206  return &a == &b;
207  }
208 
209  template <class T1, class T2>
210  static bool compareThisIfSameType(const T1 &a, const T2 &b) {
211  return false;
212  }
213 
214  template <class OtherT>
215  void copyAssign(const ErrorOr<OtherT> &Other) {
216  if (compareThisIfSameType(*this, Other))
217  return;
218 
219  this->~ErrorOr();
220  new (this) ErrorOr(Other);
221  }
222 
223  template <class OtherT>
224  void moveConstruct(ErrorOr<OtherT> &&Other) {
225  if (!Other.HasError) {
226  // Get the other value.
227  HasError = false;
228  new (getStorage()) storage_type(std::move(*Other.getStorage()));
229  } else {
230  // Get other's error.
231  HasError = true;
232  new (getErrorStorage()) std::error_code(Other.getError());
233  }
234  }
235 
236  template <class OtherT>
237  void moveAssign(ErrorOr<OtherT> &&Other) {
238  if (compareThisIfSameType(*this, Other))
239  return;
240 
241  this->~ErrorOr();
242  new (this) ErrorOr(std::move(Other));
243  }
244 
245  pointer toPointer(pointer Val) {
246  return Val;
247  }
248 
249  pointer toPointer(wrap *Val) {
250  return &Val->get();
251  }
252 
253  storage_type *getStorage() {
254  assert(!HasError && "Cannot get value when an error exists!");
255  return reinterpret_cast<storage_type*>(TStorage.buffer);
256  }
257 
258  const storage_type *getStorage() const {
259  assert(!HasError && "Cannot get value when an error exists!");
260  return reinterpret_cast<const storage_type*>(TStorage.buffer);
261  }
262 
263  std::error_code *getErrorStorage() {
264  assert(HasError && "Cannot get error when a value exists!");
265  return reinterpret_cast<std::error_code *>(ErrorStorage.buffer);
266  }
267 
268  const std::error_code *getErrorStorage() const {
269  return const_cast<ErrorOr<T> *>(this)->getErrorStorage();
270  }
271 
272 
273  union {
276  };
277  bool HasError : 1;
278 };
279 
280 template <class T, class E>
281 typename std::enable_if<std::is_error_code_enum<E>::value ||
282  std::is_error_condition_enum<E>::value,
283  bool>::type
284 operator==(const ErrorOr<T> &Err, E Code) {
285  return Err.getError() == Code;
286 }
287 } // end namespace llvm
288 
289 #endif
ErrorOr(const ErrorOr< OtherT > &Other, typename std::enable_if< std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:118
std::error_code getError() const
Definition: ErrorOr.h:178
Represents either an error or a value T.
Definition: ErrorOr.h:82
ErrorOr(E ErrorCode, typename std::enable_if< std::is_error_code_enum< E >::value||std::is_error_condition_enum< E >::value, void * >::type=0)
Definition: ErrorOr.h:97
ErrorOr & operator=(const ErrorOr &Other)
Definition: ErrorOr.h:155
reference operator*()
Definition: ErrorOr.h:186
pointer operator->()
Definition: ErrorOr.h:182
std::error_code make_error_code(BitcodeError E)
Definition: ReaderWriter.h:150
#define false
Definition: ConvertUTF.c:65
ELFYAML::ELF_STO Other
Definition: ELFYAML.cpp:591
LLVMTargetDataRef wrap(const DataLayout *P)
Definition: DataLayout.h:469
ErrorOr(T Val)
Definition: ErrorOr.h:109
friend class ErrorOr
Definition: ErrorOr.h:83
AlignedCharArrayUnion< std::error_code > ErrorStorage
Definition: ErrorOr.h:275
#define true
Definition: ConvertUTF.c:66
ReferenceStorage(T &Ref)
Definition: ErrorOr.h:46
ErrorOr(const ErrorOr &Other)
Definition: ErrorOr.h:113
AlignedCharArrayUnion< storage_type > TStorage
Definition: ErrorOr.h:274
ErrorOr(ErrorOr< OtherT > &&Other, typename std::enable_if<!std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:148
std::enable_if< std::is_constructible< T, V >::value, typename std::remove_reference< V >::type >::type && moveIfMoveConstructible(V &Val)
Definition: ErrorOr.h:29
ErrorOr(std::error_code EC)
Definition: ErrorOr.h:105
ErrorOr(ErrorOr< OtherT > &&Other, typename std::enable_if< std::is_convertible< OtherT, T >::value >::type *=nullptr)
Definition: ErrorOr.h:138
std::conditional< isRef, wrap, T >::type storage_type
Definition: ErrorOr.h:88
ErrorOr(const ErrorOr< OtherT > &Other, typename std::enable_if< !std::is_convertible< OtherT, const T & >::value >::type *=nullptr)
Definition: ErrorOr.h:126
ErrorOr(ErrorOr &&Other)
Definition: ErrorOr.h:133
Stores a reference that can be changed.
Definition: ErrorOr.h:42
ErrorOr & operator=(ErrorOr &&Other)
Definition: ErrorOr.h:160
bool operator==(uint64_t V1, const APInt &V2)
Definition: APInt.h:1734
#define T1