Silicium
error_or.hpp
Go to the documentation of this file.
1 #ifndef SILICIUM_ERROR_OR_HPP
2 #define SILICIUM_ERROR_OR_HPP
3 
4 #include <silicium/optional.hpp>
5 #include <boost/system/system_error.hpp>
6 #include <boost/throw_exception.hpp>
7 #include <boost/functional/hash.hpp>
8 #include <boost/mpl/bool.hpp>
9 #include <system_error>
10 #include <array>
11 
12 namespace Si
13 {
14  inline SILICIUM_NORETURN void throw_error(boost::system::error_code error)
15  {
16  boost::throw_exception(boost::system::system_error(error));
18  }
19 
20  inline SILICIUM_NORETURN void throw_error(std::error_code error)
21  {
22  boost::throw_exception(std::system_error(error));
24  }
25 
26  namespace detail
27  {
28  template <class To, class From>
29  From &&convert_if_necessary(From &&from, std::true_type)
30  {
31  return std::forward<From>(from);
32  }
33 
34  template <class To, class From>
35  To convert_if_necessary(From &&from, std::false_type)
36  {
37  return To(std::forward<From>(from));
38  }
39 
40  template <class ErrorCode>
41  struct category
42  {
43  typedef typename std::decay<decltype(std::declval<ErrorCode>().category())>::type type;
44  };
45  }
46 
47  template <class Value, class Error = boost::system::error_code>
48  struct error_or
49  {
50  typedef Value value_type;
51 
52  error_or() BOOST_NOEXCEPT
53  : code(0)
54 #if SILICIUM_COMPILER_HAS_CXX11_UNION
55  , value_(Value()) //initialize so that reading the value does not have undefined behaviour
56 #endif
57  {
58 #if !SILICIUM_COMPILER_HAS_CXX11_UNION
59  new (value_ptr()) Value();
60 #endif
61  }
62 
63  template <class ConvertibleToValue, class = typename std::enable_if<std::is_convertible<ConvertibleToValue, Value>::value, void>::type>
64  error_or(ConvertibleToValue &&value) BOOST_NOEXCEPT
65  : code(0)
66 #if SILICIUM_COMPILER_HAS_CXX11_UNION
67  , value_(std::forward<ConvertibleToValue>(value))
68 #endif
69  {
70 #if !SILICIUM_COMPILER_HAS_CXX11_UNION
71  new (value_ptr()) Value(std::forward<ConvertibleToValue>(value));
72 #endif
73  }
74 
75  error_or(Value &&value) BOOST_NOEXCEPT
76  : code(0)
77 #if SILICIUM_COMPILER_HAS_CXX11_UNION
78  , value_(std::move(value))
79 #endif
80  {
81 #if !SILICIUM_COMPILER_HAS_CXX11_UNION
82  new (value_ptr()) Value(std::move(value));
83 #endif
84  }
85 
86  error_or(Value const &value)
87  : code(0)
89  , value_(value)
90 #endif
91  {
92 #if !SILICIUM_COMPILER_HAS_CXX11_UNION
93  new (value_ptr()) Value(value);
94 #endif
95  }
96 
97  error_or(Error error) BOOST_NOEXCEPT
98  : code(error.value())
99  , category(&error.category())
100  {
101  }
102 
103  error_or(error_or const &other)
104  : code(other.code)
105  {
106  if (other.is_error())
107  {
108  category = other.category;
109  }
110  else
111  {
112  new (value_ptr()) Value(*other.value_ptr());
113  }
114  }
115 
116  error_or(error_or &&other) BOOST_NOEXCEPT
117  : code(other.code)
118  {
119  if (other.is_error())
120  {
121  category = other.category;
122  }
123  else
124  {
125  new (value_ptr()) Value(std::move(*other.value_ptr()));
126  }
127  }
128 
129  error_or &operator = (error_or &&other) BOOST_NOEXCEPT
130  {
131  if (is_error())
132  {
133  code = other.code;
134  if (other.is_error())
135  {
136  category = other.category;
137  }
138  else
139  {
140  new (value_ptr()) Value(std::move(*other.value_ptr()));
141  }
142  }
143  else
144  {
145  if (other.is_error())
146  {
147  value_ptr()->~Value();
148  code = other.code;
149  category = other.category;
150  }
151  else
152  {
153  *value_ptr() = std::move(*other.value_ptr());
154  }
155  }
156  return *this;
157  }
158 
160  {
161  error_or copy(other);
162  *this = std::move(copy);
163  return *this;
164  }
165 
166  error_or &operator = (Value &&other) BOOST_NOEXCEPT
167  {
168  if (is_error())
169  {
170  code = 0;
171  new (value_ptr()) Value(std::move(other));
172  }
173  else
174  {
175  *value_ptr() = std::move(other);
176  }
177  return *this;
178  }
179 
180  error_or &operator = (Value const &other)
181  {
182  if (is_error())
183  {
184  code = 0;
185  new (value_ptr()) Value(other);
186  }
187  else
188  {
189  *value_ptr() = other;
190  }
191  return *this;
192  }
193 
194  ~error_or() BOOST_NOEXCEPT
195  {
196  if (!is_error())
197  {
198  value_ptr()->~Value();
199  }
200  }
201 
202  bool is_error() const BOOST_NOEXCEPT
203  {
204  return code != 0;
205  }
206 
207  Error error() const BOOST_NOEXCEPT
208  {
209  if (is_error())
210  {
211  return Error(code, *category);
212  }
213  return {};
214  }
215 
216  void throw_if_error() const
217  {
218  if (is_error())
219  {
220  throw_error(error());
221  }
222  }
223 
224  Value &get()
226  &
227 #endif
228  {
229  throw_if_error();
230  return *value_ptr();
231  }
232 
233 #if SILICIUM_COMPILER_HAS_RVALUE_THIS_QUALIFIER
234  Value &&get() &&
235  {
236  throw_if_error();
237  return std::move(*value_ptr());
238  }
239 #endif
240 
241  Value const &get() const
243  &
244 #endif
245  {
246  throw_if_error();
247  return *value_ptr();
248  }
249 
250  Value &&move_value()
251  {
252  throw_if_error();
253  return std::move(*value_ptr());
254  }
255 
256  Value *get_ptr() BOOST_NOEXCEPT
257  {
258  if (is_error())
259  {
260  return nullptr;
261  }
262  return value_ptr();
263  }
264 
265  Value const *get_ptr() const BOOST_NOEXCEPT
266  {
267  if (is_error())
268  {
269  return nullptr;
270  }
271  return value_ptr();
272  }
273 
275 #if SILICIUM_COMPILER_HAS_RVALUE_THIS_QUALIFIER
276  &&
277 #endif
278  {
279  auto *value = get_ptr();
280  if (!value)
281  {
282  return none;
283  }
284  return std::move(*value);
285  }
286 
287  template <class ComparableToValue>
289  {
290  if (is_error() != other.is_error())
291  {
292  return false;
293  }
294  if (is_error())
295  {
296  return error() == other.error();
297  }
298  return get() == other.get();
299  }
300 
301  template <class ConvertibleToValue, class = typename std::enable_if<std::is_convertible<ConvertibleToValue, Value>::value, void>::type>
302  bool equals(ConvertibleToValue const &right) const
303  {
304  if (is_error())
305  {
306  return false;
307  }
308  return get() == detail::convert_if_necessary<Value>(right, std::is_same<typename std::decay<Value>::type, typename std::decay<ConvertibleToValue>::type>());
309  }
310 
311  bool equals(Error const &right) const
312  {
313  if (!is_error())
314  {
315  return false;
316  }
317  return error() == right;
318  }
319 
320  private:
321 
322  typedef typename detail::category<Error>::type category_type;
323 
324  int code;
325  union
326  {
327 #if SILICIUM_COMPILER_HAS_CXX11_UNION
328  Value value_;
329 #else
330  std::array<char, sizeof(Value)> value_;
331 #endif
332  category_type const *category;
333  };
334 
335  Value *value_ptr()
336  {
337 #if SILICIUM_COMPILER_HAS_CXX11_UNION
338  return &value_;
339 #else
340  return reinterpret_cast<Value *>(value_.data());
341 #endif
342  }
343 
344  Value const *value_ptr() const
345  {
346 #if SILICIUM_COMPILER_HAS_CXX11_UNION
347  return &value_;
348 #else
349  return reinterpret_cast<Value const *>(value_.data());
350 #endif
351  }
352  };
353 
354  BOOST_STATIC_ASSERT(is_handle<error_or<nothing>>::value);
355  BOOST_STATIC_ASSERT(is_handle<error_or<int>>::value);
356  BOOST_STATIC_ASSERT(is_handle<error_or<std::unique_ptr<int>>>::value);
357 
358  template <class T>
359  struct is_error_or : std::false_type
360  {
361  };
362 
363  template <class Value, class Error>
364  struct is_error_or<error_or<Value, Error>> : std::true_type
365  {
366  };
367 
368  template <class Value, class Error, class Anything>
369  bool operator == (error_or<Value, Error> const &left, Anything const &right)
370  {
371  return left.equals(right);
372  }
373 
374  template <class Anything, class Value, class Error, class = typename std::enable_if<!is_error_or<Anything>::value, void>::type>
375  bool operator == (Anything const &left, error_or<Value, Error> const &right)
376  {
377  return right.equals(left);
378  }
379 
380  template <class Anything, class Value, class Error>
381  bool operator != (Anything const &left, error_or<Value, Error> const &right)
382  {
383  return !(left == right);
384  }
385 
386  template <class Anything, class Value, class Error>
387  bool operator != (error_or<Value, Error> const &left, Anything const &right)
388  {
389  return !(left == right);
390  }
391 
392  template <class Value, class Error>
393  bool operator < (error_or<Value, Error> const &left, error_or<Value, Error> const &right)
394  {
395  if (left.is_error())
396  {
397  if (right.is_error())
398  {
399  return left.error() < right.error();
400  }
401  else
402  {
403  return true;
404  }
405  }
406  else
407  {
408  if (right.is_error())
409  {
410  return false;
411  }
412  else
413  {
414  return left.get() < right.get();
415  }
416  }
417  }
418 
419  //TODO: overload all operators
420 
421  template <class Value, class Error>
422  std::size_t hash_value(error_or<Value, Error> const &value)
423  {
424  if (value.is_error())
425  {
426  return boost::hash<Error>()(value.error());
427  }
428  else
429  {
430  return boost::hash<Value>()(value.get());
431  }
432  }
433 
434  template <class Value, class Error>
435  std::ostream &operator << (std::ostream &out, error_or<Value, Error> const &value)
436  {
437  if (value.is_error())
438  {
439  return out << value.error();
440  }
441  return out << value.get();
442  }
443 
444  namespace detail
445  {
446  template <class T>
447  typename std::remove_reference<T>::type &&move_if(boost::mpl::true_, T &&ref)
448  {
449  return std::move(ref);
450  }
451 
452  template <class T>
453  T &move_if(boost::mpl::false_, T &&ref)
454  {
455  return ref;
456  }
457  }
458 
459  template <class ErrorOr, class OnValue, class CleanErrorOr = typename std::decay<ErrorOr>::type, class = typename std::enable_if<is_error_or<CleanErrorOr>::value, void>::type>
460  auto map(ErrorOr &&maybe, OnValue &&on_value)
461  -> error_or<decltype(std::forward<OnValue>(on_value)(std::forward<ErrorOr>(maybe).get()))>
462  {
463  if (maybe.is_error())
464  {
465  return maybe.error();
466  }
467  return std::forward<OnValue>(on_value)(
469  detail::move_if(boost::mpl::bool_<!boost::is_lvalue_reference<ErrorOr>::value>(),
470 #endif
471  std::forward<ErrorOr>(maybe).get()
473  )
474 #endif
475  );
476  }
477 
478  template <class Value, class Error>
479  Value get(error_or<Value, Error> &&value)
480  {
481  return std::move(value.get());
482  }
483 }
484 
485 namespace std
486 {
487  template <class Value, class Error>
488  struct hash<Si::error_or<Value, Error>>
489  {
490  std::size_t operator()(Si::error_or<Value, Error> const &value) const
491  {
492  return hash_value(value);
493  }
494  };
495 }
496 
497 #endif
std::remove_reference< T >::type && move(T &&ref)
Definition: move.hpp:10
error_or(error_or &&other) BOOST_NOEXCEPT
Definition: error_or.hpp:116
Value value_type
Definition: error_or.hpp:50
Value && move_value()
Definition: error_or.hpp:250
BOOST_STATIC_ASSERT(Si::is_handle< absolute_path >::value)
#define SILICIUM_COMPILER_HAS_RVALUE_THIS_QUALIFIER
Definition: config.hpp:69
auto map(ErrorOr &&maybe, OnValue &&on_value) -> error_or< decltype(std::forward< OnValue >(on_value)(std::forward< ErrorOr >(maybe).get()))>
Definition: error_or.hpp:460
SILICIUM_NORETURN void throw_error(error_code< UnderlyingErrorCode, UnderlyingCategory > error)
Definition: error_code.hpp:69
bool equals(ConvertibleToValue const &right) const
Definition: error_or.hpp:302
Definition: error_or.hpp:359
optional< Value > get_optional()
Definition: error_or.hpp:274
Definition: absolute_path.hpp:352
void throw_if_error() const
Definition: error_or.hpp:216
Definition: absolute_path.hpp:19
error_or(error_or const &other)
Definition: error_or.hpp:103
Error error() const BOOST_NOEXCEPT
Definition: error_or.hpp:207
Definition: optional.hpp:39
#define SILICIUM_COMPILER_HAS_CXX11_UNION
Definition: config.hpp:105
~error_or() BOOST_NOEXCEPT
Definition: error_or.hpp:194
Value const * get_ptr() const BOOST_NOEXCEPT
Definition: error_or.hpp:265
Definition: error_or.hpp:41
std::size_t hash_value(error_or< Value, Error > const &value)
Definition: error_or.hpp:422
std::remove_reference< T >::type && move_if(boost::mpl::true_, T &&ref)
Definition: error_or.hpp:447
SILICIUM_USE_RESULT bool operator==(absolute_path const &left, ComparableToPath const &right)
Definition: absolute_path.hpp:169
#define SILICIUM_UNREACHABLE()
Definition: config.hpp:44
From && convert_if_necessary(From &&from, std::true_type)
Definition: error_or.hpp:29
category_type const * category
Definition: error_or.hpp:332
bool equals(error_or< ComparableToValue, Error > const &other) const
Definition: error_or.hpp:288
error_or(ConvertibleToValue &&value) BOOST_NOEXCEPT
Definition: error_or.hpp:64
Value & get()
Definition: error_or.hpp:224
error_or(Error error) BOOST_NOEXCEPT
Definition: error_or.hpp:97
error_or(Value const &value)
Definition: error_or.hpp:86
error_or() BOOST_NOEXCEPT
Definition: error_or.hpp:52
std::array< char, sizeof(Value)> value_
Definition: error_or.hpp:330
error_or & operator=(error_or &&other) BOOST_NOEXCEPT
Definition: error_or.hpp:129
#define SILICIUM_NORETURN
Definition: config.hpp:63
Definition: error_or.hpp:48
SILICIUM_USE_RESULT std::size_t hash_value(absolute_path const &value)
Definition: absolute_path.hpp:220
SILICIUM_USE_RESULT bool operator!=(absolute_path const &left, ComparableToPath const &right)
Definition: absolute_path.hpp:201
error_or(Value &&value) BOOST_NOEXCEPT
Definition: error_or.hpp:75
bool equals(Error const &right) const
Definition: error_or.hpp:311
Value * get_ptr() BOOST_NOEXCEPT
Definition: error_or.hpp:256
std::size_t operator()(Si::error_or< Value, Error > const &value) const
Definition: error_or.hpp:490
bool is_error() const BOOST_NOEXCEPT
Definition: error_or.hpp:202
std::decay< decltype(std::declval< ErrorCode >).category())>::type type
Definition: error_or.hpp:43