pstore2
error_or.hpp
Go to the documentation of this file.
1 //===- include/pstore/adt/error_or.hpp --------------------*- mode: C++ -*-===//
2 //* *
3 //* ___ _ __ _ __ ___ _ __ ___ _ __ *
4 //* / _ \ '__| '__/ _ \| '__| / _ \| '__| *
5 //* | __/ | | | | (_) | | | (_) | | *
6 //* \___|_| |_| \___/|_| \___/|_| *
7 //* *
8 //===----------------------------------------------------------------------===//
9 //
10 // Part of the pstore project, under the Apache License v2.0 with LLVM Exceptions.
11 // See https://github.com/SNSystems/pstore/blob/master/LICENSE.txt for license
12 // information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //===----------------------------------------------------------------------===//
18 
19 #ifndef PSTORE_ADT_ERROR_OR_HPP
20 #define PSTORE_ADT_ERROR_OR_HPP
21 
22 #include <new>
23 #include <system_error>
24 #include <tuple>
25 
26 #include "pstore/adt/utility.hpp"
28 #include "pstore/support/gsl.hpp"
30 
31 namespace pstore {
32 
33  template <typename Error>
34  struct is_error : std::integral_constant<bool, std::is_error_code_enum<Error>::value ||
35  std::is_error_condition_enum<Error>::value> {
36  };
37 
38  template <typename T>
39  class error_or {
40  template <typename Other>
41  friend class error_or;
42 
43  using wrapper = std::reference_wrapper<typename std::remove_reference<T>::type>;
44  using storage_type =
45  typename std::conditional<std::is_reference<T>::value, wrapper, T>::type;
46 
47  public:
48  using value_type = T;
49  using reference = T &;
50  using const_reference = typename std::remove_reference<T>::type const &;
53 
54  // ****
55  // construction
56  // ****
57 
58  template <typename ErrorCode,
59  typename = typename std::enable_if<is_error<ErrorCode>::value>::type>
60  explicit error_or (ErrorCode const erc) {
61  new (get_error_storage ()) std::error_code (make_error_code (erc));
62  }
63 
64  explicit error_or (std::error_code const erc) {
65  new (get_error_storage ()) std::error_code (erc);
66  }
67 
68  template <typename Other,
69  typename = typename std::enable_if<std::is_convertible<Other, T>::value>::type>
70  explicit error_or (Other && other)
71  : has_error_{false} {
72  new (get_storage ()) storage_type (std::forward<Other> (other));
73  }
74 
75  template <typename... Args>
76  explicit error_or (in_place_t const inp, Args &&... args)
77  : has_error_{false} {
78  (void) inp;
79  new (get_storage ()) storage_type (std::forward<Args> (args)...);
80  }
81 
82  error_or (error_or const & rhs) { copy_construct (rhs); }
83 
84  template <typename Other,
85  typename = typename std::enable_if<std::is_convertible<Other, T>::value>::type>
86  // NOLINTNEXTLINE(hicpp-explicit-conversions)
87  error_or (error_or<Other> const & rhs) {
88  copy_construct (rhs);
89  }
90 
91  error_or (error_or && rhs) noexcept { move_construct (std::move (rhs)); }
92 
93  template <typename Other,
94  typename = typename std::enable_if<std::is_convertible<Other, T>::value>::type>
95  // NOLINTNEXTLINE(hicpp-explicit-conversions)
96  error_or (error_or<Other> && rhs) noexcept {
97  move_construct (std::move (rhs));
98  }
99 
100  ~error_or ();
101 
102 
103  // ****
104  // assignment
105  // ****
106  template <typename ErrorCode,
107  typename = typename std::enable_if<is_error<ErrorCode>::value>::type>
108  error_or & operator= (ErrorCode rhs) {
109  operator=(error_or<T>{rhs});
110  return *this;
111  }
112  error_or & operator= (std::error_code const & rhs) {
113  operator= (error_or<T> (rhs));
114  return *this;
115  }
116  error_or & operator= (error_or const & rhs) {
117  copy_assign (rhs);
118  return *this;
119  }
120  error_or & operator= (error_or && rhs) {
121  move_assign (std::move (rhs));
122  return *this;
123  }
124 
125 
126  // ****
127  // comparison (operator== and operator!=)
128  // ****
129  bool operator== (std::error_code const rhs) const { return get_error () == rhs; }
130  bool operator== (error_or const & rhs);
131  bool operator== (T const & rhs) const {
132  return static_cast<bool> (*this) && this->get () == rhs;
133  }
134  template <typename Error>
135  typename std::enable_if<is_error<Error>::value, bool>::type operator== (Error rhs) const {
136  return get_error () == rhs;
137  }
138 
139  bool operator!= (T const & rhs) const { return !operator== (rhs); }
140  bool operator!= (std::error_code const rhs) const { return !operator== (rhs); }
141  bool operator!= (error_or const & rhs) { return !operator== (rhs); }
142  template <typename Error>
143  typename std::enable_if<is_error<Error>::value, bool>::type operator!= (Error rhs) const {
144  return !operator== (rhs);
145  }
146 
147 
148  // ****
149  // access
150  // ****
151 
153  explicit operator bool () const noexcept { return !has_error_; }
154 
155  std::error_code get_error () const noexcept;
156 
157  reference get () noexcept { return *get_storage (); }
158  const_reference get () const noexcept { return *get_storage (); }
159  pointer operator-> () noexcept { return to_pointer (get_storage ()); }
160  const_pointer operator-> () const noexcept { return to_pointer (get_storage ()); }
161  reference operator* () noexcept { return *get_storage (); }
162  const_reference operator* () const noexcept { return *get_storage (); }
163 
164  private:
165  template <typename Other>
166  void copy_construct (error_or<Other> const & rhs);
167 
168  template <typename T1>
169  static constexpr bool same_object (T1 const & lhs, T1 const & rhs) noexcept {
170  return &lhs == &rhs;
171  }
172 
173  template <typename T1, typename T2>
174  static constexpr bool same_object (T1 const & lhs, T2 const & rhs) noexcept {
175  (void) lhs;
176  (void) rhs;
177  return false;
178  }
179 
180  template <typename Other>
181  error_or & copy_assign (error_or<Other> const & rhs);
182 
183  template <typename Other>
184  void move_construct (error_or<Other> && rhs) noexcept;
185 
186  template <typename Other>
187  error_or & move_assign (error_or<Other> && rhs);
188 
189  pointer to_pointer (wrapper * PSTORE_NONNULL val) noexcept { return &val->get (); }
190  pointer to_pointer (pointer val) noexcept { return val; }
191  const_pointer to_pointer (const_pointer val) const noexcept { return val; }
192  const_pointer to_pointer (wrapper const * PSTORE_NONNULL val) const noexcept {
193  return &val->get ();
194  }
195 
196  template <typename ErrorOr,
197  typename ResultType = typename inherit_const<ErrorOr, storage_type>::type>
198  static ResultType * PSTORE_NONNULL value_storage_impl (ErrorOr && e) noexcept {
199  PSTORE_ASSERT (!e.has_error_);
200  return reinterpret_cast<ResultType *> (&e.storage_);
201  }
202 
203  storage_type * PSTORE_NONNULL get_storage () noexcept { return value_storage_impl (*this); }
204  storage_type const * PSTORE_NONNULL get_storage () const noexcept {
205  return value_storage_impl (*this);
206  }
207 
208  template <typename ErrorOr,
209  typename ResultType = typename inherit_const<ErrorOr, std::error_code>::type>
210  static ResultType * PSTORE_NONNULL error_storage_impl (ErrorOr && e) noexcept {
211  PSTORE_ASSERT (e.has_error_);
212  return reinterpret_cast<ResultType *> (&e.error_storage_);
213  }
214  std::error_code * PSTORE_NONNULL get_error_storage () noexcept;
215  std::error_code const * PSTORE_NONNULL get_error_storage () const noexcept;
216 
217  union {
218  typename std::aligned_storage<sizeof (storage_type), alignof (storage_type)>::type
219  storage_;
220  std::aligned_storage<sizeof (std::error_code), alignof (std::error_code)>::type
221  error_storage_;
222  };
223  bool has_error_ = true;
224  };
225 
226 
227  // dtor
228  // ~~~~
229  template <typename T>
231  if (!has_error_) {
232  get_storage ()->~storage_type ();
233  } else {
234  using error_code = std::error_code;
235  get_error_storage ()->~error_code ();
236  }
237  }
238 
239  // copy_construct
240  // ~~~~~~~~~~~~~~
241  template <typename T>
242  template <typename Other>
243  void error_or<T>::copy_construct (error_or<Other> const & rhs) {
244  has_error_ = rhs.has_error_;
245  if (has_error_) {
246  new (get_error_storage ()) std::error_code (rhs.get_error ());
247  } else {
248  new (get_storage ()) storage_type (*rhs.get_storage ());
249  }
250  }
251 
252  // copy_assign
253  // ~~~~~~~~~~~
254  template <typename T>
255  template <typename Other>
256  auto error_or<T>::copy_assign (error_or<Other> const & rhs) -> error_or & {
257  if (same_object (*this, rhs)) {
258  return *this;
259  }
260 
261  if (has_error_ && rhs.has_error_) {
262  *get_error_storage () = *rhs.get_error_storage ();
263  } else if (!has_error_ && !rhs.has_error_) {
264  *get_storage () = *rhs.get_storage ();
265  } else {
266  this->~error_or ();
267  new (this) error_or (rhs);
268  }
269  return *this;
270  }
271 
272  // move_construct
273  // ~~~~~~~~~~~~~~
274  template <typename T>
275  template <typename Other>
276  void error_or<T>::move_construct (error_or<Other> && rhs) noexcept {
277  has_error_ = rhs.has_error_;
278  if (has_error_) {
279  new (get_error_storage ()) std::error_code (rhs.get_error ());
280  } else {
281  new (get_storage ()) storage_type (std::move (*rhs.get_storage ()));
282  }
283  }
284 
285  // move_assign
286  // ~~~~~~~~~~~
287  template <typename T>
288  template <typename Other>
290  if (same_object (*this, rhs)) {
291  return *this;
292  }
293 
294  if (has_error_ && rhs.has_error_) {
295  *get_error_storage () = std::move (*rhs.get_error_storage ());
296  } else if (!has_error_ && !rhs.has_error_) {
297  *get_storage () = std::move (*rhs.get_storage ());
298  } else {
299  this->~error_or ();
300  new (this) error_or (std::move (rhs));
301  }
302  return *this;
303  }
304 
305  // operator==
306  // ~~~~~~~~~~
307  template <typename T>
308  bool error_or<T>::operator== (error_or const & rhs) {
309  if (has_error_ != rhs.has_error_) {
310  return false;
311  }
312  if (has_error_) {
313  return this->operator== (rhs.get_error ());
314  }
315  return this->operator== (rhs.get ());
316  }
317 
318  // get_error
319  // ~~~~~~~~~
320  template <typename T>
321  std::error_code error_or<T>::get_error () const noexcept {
322  return has_error_ ? *get_error_storage () : std::error_code{};
323  }
324 
325  // get_error_storage
326  // ~~~~~~~~~~~~~~~~~
327  template <typename T>
328  inline std::error_code * PSTORE_NONNULL error_or<T>::get_error_storage () noexcept {
329  return error_storage_impl (*this);
330  }
331  template <typename T>
332  inline std::error_code const * PSTORE_NONNULL error_or<T>::get_error_storage () const noexcept {
333  return error_storage_impl (*this);
334  }
335 
336 
337  // operator>>= (bind)
338  // ~~~~~~~~~~~
345  template <typename T, typename Function>
346  auto operator>>= (error_or<T> && t, Function f) -> decltype ((f (t.get ()))) {
347  if (t) {
348  return f (*t);
349  }
350  return error_or<typename decltype (f (t.get ()))::value_type> (t.get_error ());
351  }
352 
353 
354  template <typename... Args>
355  using error_or_n = error_or<std::tuple<Args...>>;
356 
357  // get
358  // ~~~
359  template <std::size_t I, class... Types>
360  typename std::tuple_element<I, std::tuple<Types...>>::type &
361  get (error_or_n<Types...> & eon) noexcept {
362  return std::get<I> (*eon);
363  }
364 
365  template <std::size_t I, class... Types>
366  typename std::tuple_element<I, std::tuple<Types...>>::type &&
367  get (error_or_n<Types...> && eon) noexcept {
368  return std::get<I> (*eon);
369  }
370 
371  template <std::size_t I, class... Types>
372  typename std::tuple_element<I, std::tuple<Types...>>::type const &
373  get (error_or_n<Types...> const & eon) noexcept {
374  return std::get<I> (*eon);
375  }
376 
377 
378  namespace details {
379 
380  template <std::size_t N>
381  struct applier {
382  template <typename Function, typename Tuple, typename... Args>
383  static auto apply (Function && f, Tuple && t, Args &&... a)
384  -> decltype (applier<N - 1U>::apply (std::forward<Function> (f),
385  std::forward<Tuple> (t),
386  std::get<N - 1U> (std::forward<Tuple> (t)),
387  std::forward<Args> (a)...)) {
388 
389  return applier<N - 1U>::apply (std::forward<Function> (f), std::forward<Tuple> (t),
390  std::get<N - 1U> (std::forward<Tuple> (t)),
391  std::forward<Args> (a)...);
392  }
393  };
394 
395  template <>
396  struct applier<std::size_t{0}> {
397  template <typename Function, typename Tuple, typename... Args>
398  static auto apply (Function && f, Tuple && t, Args &&... a)
399  -> decltype (std::forward<Function> (f) (std::forward<Args> (a)...)) {
400  (void) t;
401  return std::forward<Function> (f) (std::forward<Args> (a)...);
402  }
403  };
404 
405  template <typename Function, typename Tuple>
406  inline auto apply (Function && f, Tuple && t)
407  -> decltype (applier<std::tuple_size<typename std::decay<Tuple>::type>::value>::apply (
408  std::forward<Function> (f), std::forward<Tuple> (t))) {
409 
411  std::forward<Function> (f), std::forward<Tuple> (t));
412  }
413 
414  } // end namespace details
415 
416 
417  template <typename Function, typename... Args>
418  auto operator>>= (error_or_n<Args...> const & t, Function && f)
419  -> decltype (details::apply (f, t.get ())) {
420 
421  if (t) {
422  return details::apply (std::forward<Function> (f), t.get ());
423  }
424  return decltype (details::apply (f, t.get ())){t.get_error ()};
425  }
426 
427 } // end namespace pstore
428 
429 #endif // PSTORE_ADT_ERROR_OR_HPP
Provides definitions needed by the code that are available in C++17 <utility>.
Definition: chunked_sequence.hpp:607
Definition: error_or.hpp:39
Definition: gsl.hpp:589
Definition: error_or.hpp:34
Definition: print.cpp:18
A utility template intended to simplify the writing of the const- and non-const variants of member fu...
Definition: utility.hpp:24
Definition: error_or.hpp:381
Definition: nonpod2.cpp:40
An implementation of the standard assert() macro with the exception that it will, on failure...
typename std::conditional< std::is_const< typename std::remove_reference< T >::type >::value, RC, R >::type type
If T is const, R const otherwise R.
Definition: inherit_const.hpp:112