GTIRB  v2.1.0
GrammaTech Intermediate Representation for Binaries: C++ API
ErrorOr.hpp
Go to the documentation of this file.
1 //===- ErrorOr.hpp ----------------------------------------------*- C++ -*-===//
2 //
3 // Copyright (C) 2020 GrammaTech, Inc.
4 //
5 // This code is licensed under the MIT license. See the LICENSE file in the
6 // project root for license terms.
7 //
8 // This project is sponsored by the Office of Naval Research, One Liberty
9 // Center, 875 N. Randolph Street, Arlington, VA 22203 under contract #
10 // N68335-17-C-0700. The content of the information does not necessarily
11 // reflect the position or policy of the Government and no official
12 // endorsement should be inferred.
13 //
14 //===-Addition License Information-----------------------------------------===//
15 //
16 // This file was initially written for the LLVM Compiler Infrastructure
17 // project where it is distributed under the Apache License v2.0 with LLVM
18 // Exceptions. See the LICENSE file in the project root for license terms.
19 //
20 //===----------------------------------------------------------------------===//
21 
22 #ifndef GTIRB_ERROROR_H
23 #define GTIRB_ERROROR_H
24 
25 #include <gtirb/Export.hpp>
26 #include <cassert>
27 #include <iostream>
28 #include <system_error>
29 #include <type_traits>
30 #include <utility>
31 
32 namespace gtirb {
33 
37  std::error_code ErrorCode;
38  std::string Msg;
39  ErrorInfo() = default;
40  ErrorInfo(const std::error_code& EC, const std::string& S)
41  : ErrorCode(EC), Msg(S){};
42  std::string message() const;
43 };
44 
45 template <typename CharT, typename Traits>
46 std::ostream& operator<<(std::basic_ostream<CharT, Traits>& os,
47  const ErrorInfo& Info) {
48  os << Info.ErrorCode.message() << " " << Info.Msg;
49  return os;
50 }
51 
81 template <class T> class ErrorOr {
82  template <class OtherT> friend class ErrorOr;
83 
84  static constexpr bool isRef = std::is_reference<T>::value;
85 
86  using wrap = std::reference_wrapper<std::remove_reference_t<T>>;
87 
88 public:
89  using storage_type = std::conditional_t<isRef, wrap, T>;
90 
91 private:
92  using reference = std::remove_reference_t<T>&;
93  using const_reference = const std::remove_reference_t<T>&;
94  using pointer = std::remove_reference_t<T>*;
95  using const_pointer = const std::remove_reference_t<T>*;
96 
97 public:
98  template <class E>
99  ErrorOr(E ErrorCode, const std::string& Msg = "",
100  std::enable_if_t<std::is_error_code_enum<E>::value ||
101  std::is_error_condition_enum<E>::value,
102  void*> = nullptr)
103  : HasError(true) {
104  new (getErrorStorage()) ErrorInfo{make_error_code(ErrorCode), Msg};
105  }
106 
107  ErrorOr(std::error_code EC, const std::string& Msg = "") : HasError(true) {
108  new (getErrorStorage()) ErrorInfo{EC, Msg};
109  }
110 
111  ErrorOr(const ErrorInfo& EI) : HasError(true) {
112  new (getErrorStorage()) ErrorInfo(EI);
113  }
114 
115  template <class OtherT>
116  ErrorOr(OtherT&& Val,
117  std::enable_if_t<std::is_convertible<OtherT, T>::value>* = nullptr)
118  : HasError(false) {
119  new (getStorage()) storage_type(std::forward<OtherT>(Val));
120  }
121 
122  ErrorOr(const ErrorOr& Other) { copyConstruct(Other); }
123 
124  template <class OtherT>
125  ErrorOr(const ErrorOr<OtherT>& Other,
126  std::enable_if_t<std::is_convertible<OtherT, T>::value>* = nullptr) {
127  copyConstruct(Other);
128  }
129 
130  template <class OtherT>
131  explicit ErrorOr(
132  const ErrorOr<OtherT>& Other,
133  std::enable_if_t<!std::is_convertible<OtherT, const T&>::value>* =
134  nullptr) {
135  copyConstruct(Other);
136  }
137 
138  ErrorOr(ErrorOr&& Other) { moveConstruct(std::move(Other)); }
139 
140  template <class OtherT>
142  std::enable_if_t<std::is_convertible<OtherT, T>::value>* = nullptr) {
143  moveConstruct(std::move(Other));
144  }
145 
146  // This might eventually need SFINAE but it's more complex than is_convertible
147  // & I'm too lazy to write it right now.
148  template <class OtherT>
149  explicit ErrorOr(
150  ErrorOr<OtherT>&& Other,
151  std::enable_if_t<!std::is_convertible<OtherT, T>::value>* = nullptr) {
152  moveConstruct(std::move(Other));
153  }
154 
155  ErrorOr& operator=(const ErrorOr& Other) {
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 
171  explicit operator bool() const { return !HasError; }
172 
173  reference get() { return *getStorage(); }
174  const_reference get() const { return const_cast<ErrorOr<T>*>(this)->get(); }
175 
176  ErrorInfo getError() const {
177  return HasError ? *getErrorStorage() : ErrorInfo();
178  }
179 
180  pointer operator->() { return toPointer(getStorage()); }
181 
182  const_pointer operator->() const { return toPointer(getStorage()); }
183 
184  reference operator*() { return *getStorage(); }
185 
186  const_reference operator*() const { return *getStorage(); }
187 
188 private:
189  template <class OtherT> void copyConstruct(const ErrorOr<OtherT>& Other) {
190  if (!Other.HasError) {
191  // Get the other value.
192  HasError = false;
193  new (getStorage()) storage_type(*Other.getStorage());
194  } else {
195  // Get other's error.
196  HasError = true;
197  new (getErrorStorage()) ErrorInfo(Other.getError());
198  }
199  }
200 
201  template <class T1>
202  static bool compareThisIfSameType(const T1& a, const T1& b) {
203  return &a == &b;
204  }
205 
206  template <class T1, class T2>
207  static bool compareThisIfSameType(const T1&, const T2&) {
208  return false;
209  }
210 
211  template <class OtherT> void copyAssign(const ErrorOr<OtherT>& Other) {
212  if (compareThisIfSameType(*this, Other))
213  return;
214 
215  this->~ErrorOr();
216  new (this) ErrorOr(Other);
217  }
218 
219  template <class OtherT> void moveConstruct(ErrorOr<OtherT>&& Other) {
220  if (!Other.HasError) {
221  // Get the other value.
222  HasError = false;
223  new (getStorage()) storage_type(std::move(*Other.getStorage()));
224  } else {
225  // Get other's error.
226  HasError = true;
227  new (getErrorStorage()) ErrorInfo(std::move(*Other.getErrorStorage()));
228  }
229  }
230 
231  template <class OtherT> void moveAssign(ErrorOr<OtherT>&& Other) {
232  if (compareThisIfSameType(*this, Other))
233  return;
234 
235  this->~ErrorOr();
236  new (this) ErrorOr(std::move(Other));
237  }
238 
239  pointer toPointer(pointer Val) { return Val; }
240 
241  const_pointer toPointer(const_pointer Val) const { return Val; }
242 
243  pointer toPointer(wrap* Val) { return &Val->get(); }
244 
245  const_pointer toPointer(const wrap* Val) const { return &Val->get(); }
246 
247  storage_type* getStorage() {
248  assert(!HasError && "Cannot get value when an error exists!");
249  return reinterpret_cast<storage_type*>(TStorage);
250  }
251 
252  const storage_type* getStorage() const {
253  assert(!HasError && "Cannot get value when an error exists!");
254  return reinterpret_cast<const storage_type*>(TStorage);
255  }
256 
257  ErrorInfo* getErrorStorage() {
258  assert(HasError && "Cannot get error when a value exists!");
259  return reinterpret_cast<ErrorInfo*>(ErrorStorage);
260  }
261 
262  const ErrorInfo* getErrorStorage() const {
263  return const_cast<ErrorOr<T>*>(this)->getErrorStorage();
264  }
265 
266  union {
267  alignas(storage_type) char TStorage[sizeof(storage_type)];
268  alignas(ErrorInfo) char ErrorStorage[sizeof(ErrorInfo)];
269  };
270  bool HasError : 1;
271 };
272 
273 template <class T, class E>
274 std::enable_if_t<std::is_error_code_enum<E>::value ||
275  std::is_error_condition_enum<E>::value,
276  bool>
277 operator==(const ErrorOr<T>& Err, E Code) {
278  return Err.getError().ErrorCode == Code;
279 }
280 } // end namespace gtirb
281 
282 #endif // GTIRB_ERROROR_H
gtirb::ErrorOr::operator->
const_pointer operator->() const
Definition: ErrorOr.hpp:182
gtirb::ErrorOr::ErrorOr
ErrorOr(const ErrorOr< OtherT > &Other, std::enable_if_t< std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.hpp:125
gtirb::ErrorOr::TStorage
char TStorage[sizeof(storage_type)]
Definition: ErrorOr.hpp:267
gtirb::ErrorInfo::Msg
std::string Msg
Definition: ErrorOr.hpp:38
gtirb::operator==
std::enable_if_t< std::is_error_code_enum< E >::value||std::is_error_condition_enum< E >::value, bool > operator==(const ErrorOr< T > &Err, E Code)
Definition: ErrorOr.hpp:277
gtirb::SymAttribute::S
@ S
GTIRB_EXPORT_API
#define GTIRB_EXPORT_API
This macro controls the visibility of exported symbols (i.e. classes) in shared libraries....
Definition: Export.hpp:52
gtirb::ErrorOr::get
const_reference get() const
Definition: ErrorOr.hpp:174
gtirb
Main namespace for the GTIRB API.
Definition: Addr.hpp:28
gtirb::ErrorOr::ErrorOr
ErrorOr(E ErrorCode, const std::string &Msg="", std::enable_if_t< std::is_error_code_enum< E >::value||std::is_error_condition_enum< E >::value, void * >=nullptr)
Definition: ErrorOr.hpp:99
Export.hpp
gtirb::ErrorOr::ErrorOr
ErrorOr(OtherT &&Val, std::enable_if_t< std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.hpp:116
gtirb::ErrorOr::ErrorOr
ErrorOr(ErrorOr< OtherT > &&Other, std::enable_if_t<!std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.hpp:149
gtirb::ErrorOr::ErrorOr
ErrorOr(ErrorOr< OtherT > &&Other, std::enable_if_t< std::is_convertible< OtherT, T >::value > *=nullptr)
Definition: ErrorOr.hpp:141
gtirb::ErrorOr::~ErrorOr
~ErrorOr()
Definition: ErrorOr.hpp:165
gtirb::ErrorOr::operator*
const_reference operator*() const
Definition: ErrorOr.hpp:186
gtirb::ErrorOr::operator=
ErrorOr & operator=(const ErrorOr &Other)
Definition: ErrorOr.hpp:155
gtirb::ErrorOr::ErrorOr
ErrorOr(const ErrorOr &Other)
Definition: ErrorOr.hpp:122
gtirb::ErrorInfo
Definition: ErrorOr.hpp:36
gtirb::ErrorInfo::ErrorCode
std::error_code ErrorCode
Definition: ErrorOr.hpp:37
gtirb::ErrorOr::ErrorOr
ErrorOr(const ErrorInfo &EI)
Definition: ErrorOr.hpp:111
gtirb::ErrorOr::ErrorOr
ErrorOr(const ErrorOr< OtherT > &Other, std::enable_if_t<!std::is_convertible< OtherT, const T & >::value > *=nullptr)
Definition: ErrorOr.hpp:131
gtirb::ErrorOr::ErrorOr
ErrorOr(ErrorOr &&Other)
Definition: ErrorOr.hpp:138
gtirb::ErrorOr::getError
ErrorInfo getError() const
Definition: ErrorOr.hpp:176
gtirb::ErrorOr::operator=
ErrorOr & operator=(ErrorOr &&Other)
Definition: ErrorOr.hpp:160
gtirb::make_error_code
std::error_code make_error_code(gtirb::IR::load_error e)
Makes an std::error_code object from an IR::load_error object.
Definition: IR.hpp:1504
gtirb::ErrorOr::get
reference get()
Definition: ErrorOr.hpp:173
gtirb::operator<<
GTIRB_EXPORT_API std::ostream & operator<<(std::ostream &OS, const ConditionalEdge &CE)
gtirb::ErrorOr::ErrorOr
friend class ErrorOr
Definition: ErrorOr.hpp:82
gtirb::ErrorOr::operator->
pointer operator->()
Definition: ErrorOr.hpp:180
gtirb::ErrorOr
Definition: ByteInterval.hpp:59
gtirb::ErrorOr::storage_type
std::conditional_t< isRef, wrap, T > storage_type
Definition: ErrorOr.hpp:89
gtirb::ErrorOr::ErrorStorage
char ErrorStorage[sizeof(ErrorInfo)]
Definition: ErrorOr.hpp:268
gtirb::ErrorOr::ErrorOr
ErrorOr(std::error_code EC, const std::string &Msg="")
Definition: ErrorOr.hpp:107
gtirb::ErrorInfo::ErrorInfo
ErrorInfo(const std::error_code &EC, const std::string &S)
Definition: ErrorOr.hpp:40
gtirb::ErrorOr::operator*
reference operator*()
Definition: ErrorOr.hpp:184