Maybe Result
 All Classes Functions
result.hpp
1 /*
2  * Copyright 2016 TRAFI
3  *
4  * Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
5  * http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
6  * http://opensource.org/licenses/MIT>, at your option. This file may not be
7  * copied, modified, or distributed except according to those terms.
8  *
9  */
10 
11 #pragma once
12 
13 #include "result.fwd.hpp"
14 
15 #include <experimental/optional>
16 #include <string>
17 
18 namespace maybe {
19  namespace internal {
20  struct placeholder {
21  };
22  }
23 
24  class bad_result_access : public std::logic_error
25  {
26  public:
27  // XXX Should not be inline
28  explicit bad_result_access(const std::string& __arg) : std::logic_error(__arg) { }
29 
30  explicit bad_result_access(const char* __arg) : std::logic_error(__arg) { }
31 
32  virtual ~bad_result_access() noexcept = default;
33  };
34 
35  template <typename T, typename E>
36  class result final {
37  private:
38  std::experimental::optional<T> var_ok;
39  std::experimental::optional<E> var_err;
40 
41  public:
42  typedef T ok_type;
43  typedef E err_type;
44 
45  result()
46  {
47  }
48 
49  result(T&& value, internal::placeholder) : var_ok(std::forward<T>(value))
50  {
51  }
52 
53  result(internal::placeholder, E&& value) : var_err(std::forward<E>(value))
54  {
55  }
56 
57  result(const T& value, internal::placeholder) : var_ok(value)
58  {
59  }
60 
61  result(internal::placeholder, const E& value) : var_err(value)
62  {
63  }
64 
65  /**
66  * Create a new ok value.
67  *
68  * @param T value
69  * @return result<T, E>
70  */
71  constexpr static result<T, E> ok(T&& value) noexcept
72  {
73  return std::forward<result<T, E>>(
74  result<T, E>(std::forward<T>(value), internal::placeholder{}));
75  }
76 
77  constexpr static result<T, E> ok(const T& value) noexcept
78  {
79  return std::forward<result<T, E>>(result<T, E>(value, internal::placeholder{}));
80  }
81 
82  /**
83  * Create a new err value.
84  *
85  * @param E value
86  * @return result<T, E>
87  */
88  constexpr static result<T, E> err(E&& value) noexcept
89  {
90  return std::forward<result<T, E>>(
91  result<T, E>(internal::placeholder{}, std::forward<E>(value)));
92  }
93 
94  constexpr static result<T, E> err(const E& value) noexcept
95  {
96  return std::forward<result<T, E>>(result<T, E>(internal::placeholder{}, value));
97  }
98 
99  /**
100  * Create a new ok value using `T()` constructor.
101  *
102  * @return result<T, E>
103  */
104  constexpr static result<T, E> default_ok() noexcept
105  {
106  return std::move(result<T, E>(T(), internal::placeholder{}));
107  }
108 
109  /**
110  * Create a new err value using `E()` constructor.
111  *
112  * @return result<T, E>
113  */
114  constexpr static result<T, E> default_err() noexcept
115  {
116  return std::move(result<T, E>(internal::placeholder{}, E()));
117  }
118 
119  // Inspection.
120 
121  /**
122  * Check if result contains ok value.
123  *
124  * @return bool
125  */
126  inline bool is_ok() const noexcept
127  {
128  return !!var_ok;
129  }
130 
131  /**
132  * Check if result contains err value.
133  *
134  * @return bool
135  */
136  inline bool is_err() const noexcept
137  {
138  return !!var_err;
139  }
140 
141  explicit inline operator bool() const noexcept
142  {
143  return is_ok();
144  }
145 
146  constexpr T const& ok_value() const&
147  {
148  if (!is_ok()) {
149  throw new bad_result_access("Attempted to ok from err");
150  }
151  return *var_ok;
152  }
153 
154  constexpr T& ok_value() &
155  {
156  if (!is_ok()) {
157  throw new bad_result_access("Attempted to ok from err");
158  }
159  return *var_ok;
160  }
161 
162  constexpr T&& ok_value() &&
163  {
164  if (!is_ok()) {
165  throw new bad_result_access("Attempted to ok from err");
166  }
167  return std::move(*var_ok);
168  }
169 
170  template <class V>
171  constexpr T ok_value_or(V&& v) const&
172  {
173  return var_ok.value_or(std::forward<V>(v));
174  }
175 
176  template <class V>
177  constexpr T ok_value_or(V&& v) &&
178  {
179  return var_ok.value_or(std::forward<V>(v));
180  }
181 
182  constexpr E const& err_value() const&
183  {
184  if (!is_err()) {
185  throw new bad_result_access("Attempted to err from ok");
186  }
187  return *var_err;
188  }
189 
190  constexpr E& err_value() &
191  {
192  if (!is_err()) {
193  throw new bad_result_access("Attempted to err from ok");
194  }
195  return *var_err;
196  }
197 
198  constexpr E&& err_value() &&
199  {
200  if (!is_err()) {
201  throw new bad_result_access("Attempted to err from ok");
202  }
203  return std::move(*var_err);
204  }
205 
206  template <class V>
207  constexpr E err_value_or(V&& v) const&
208  {
209  return var_err.value_or(std::forward<V>(v));
210  }
211 
212  template <class V>
213  constexpr E err_value_or(V&& v) &&
214  {
215  return var_err.value_or(std::forward<V>(v));
216  }
217 
218  // Functional helpers.
219 
220  /**
221  * Maps a result<T, E> to result<U, E> (where U is return value of F(T)) by applying a
222  * function F to a
223  * contained ok value, leaving an err value untouched.
224  *
225  * This function can be used to compose the results of two functions.
226  *
227  * @param f F(T) -> U
228  * @return maybe::result<U, E>
229  */
230  template <typename F, typename R = typename std::result_of<F(T)>::type>
231  inline auto map(F f) noexcept -> maybe::result<R, E>;
232 
233  /**
234  * Maps a result<T, E> to result<void, E>, leaving an err value untouched.
235  *
236  * @return maybe::result<void, E>
237  */
238  inline auto map_void() noexcept -> maybe::result<void, E>;
239 
240  /**
241  * Maps a result<T, E> to result<U, E> by always returning provided U value on success,
242  * leaving an err value untouched.
243  *
244  * This function can be used to compose the results of two functions.
245  *
246  * @param value U
247  * @return maybe::result<U, E>
248  */
249  template <typename U>
250  inline auto map_value(U value) noexcept -> maybe::result<U, E>;
251 
252  /**
253  * Maps a result<T, E> to result<T, U> (where U is return value of F(E)) by applying a
254  * function to a
255  * function to a
256  * contained err value, leaving an ok value untouched.
257  *
258  * This function can be used to pass through a successful result while changing an error.
259  *
260  * @param f F(E) -> U
261  * @return maybe::result<T, U>
262  */
263  template <typename F>
264  inline auto map_err(F f) noexcept -> maybe::result<T, typename std::result_of<F(E)>::type>;
265 
266  /**
267  * Maps a result<T, E> to result<T, U> by always returning provided U value on error,
268  * leaving an ok value untouched.
269  *
270  * This function can be used to compose the results of two functions.
271  *
272  * @param value U
273  * @return maybe::result<T, U>
274  */
275  template <typename U>
276  inline auto map_err_value(U value) noexcept -> maybe::result<T, U>;
277 
278  /**
279  * Calls op if the result is ok, otherwise returns the err value of self.
280  *
281  * This function can be used for control flow based on result values.
282  *
283  * @param f F(T) -> maybe::result<U, E>
284  * @return maybe::result<U, E>
285  */
286  template <typename F>
287  inline auto and_then(F op) noexcept -> typename std::result_of<F(T)>::type;
288 
289  /**
290  * Converts into another result with different ok type `U` and forwards the same error.
291  *
292  * The ok type `U` must have `U()` constructor in case the result does not contain an err.
293  *
294  * @return maybe::result<U, E>
295  */
296  template <typename U>
297  inline auto into_err() noexcept -> maybe::result<U, E> const;
298  };
299 
300  template <typename T, typename E>
301  constexpr bool operator==(const result<T, E>& x, const result<T, E>& y)
302  {
303  return x.is_ok() && y.is_ok()
304  ? x.ok_value() == y.ok_value()
305  : (x.is_err() && y.is_err() ? x.err_value() == y.err_value() : false);
306  }
307 
308  template <typename T, typename E>
309  constexpr bool operator!=(const result<T, E>& x, const result<T, E>& y)
310  {
311  return !(x == y);
312  }
313 
314  template <typename E>
315  class result<void, E> final {
316  private:
317  std::experimental::optional<E> var_err;
318 
319  public:
320  typedef void ok_type;
321  typedef E err_type;
322 
323  result()
324  {
325  }
326 
327  result(internal::placeholder, E&& value) : var_err(std::forward<E>(value))
328  {
329  }
330 
331  result(internal::placeholder, const E& value) : var_err(value)
332  {
333  }
334 
335  result(E&& value) : var_err(std::forward<E>(value))
336  {
337  }
338 
339  result(const E& value) : var_err(value)
340  {
341  }
342 
343  /**
344  * Create a new ok value.
345  *
346  * @param void value
347  * @return result<void, E>
348  */
349  constexpr static result<void, E> ok() noexcept
350  {
351  return std::forward<result<void, E>>(result<void, E>());
352  }
353 
354  /**
355  * Create a new err value.
356  *
357  * @param E value
358  * @return result<void, E>
359  */
360  constexpr static result<void, E> err(E&& value) noexcept
361  {
362  return std::forward<result<void, E>>(result<void, E>(std::forward<E>(value)));
363  }
364 
365  constexpr static result<void, E> err(const E& value) noexcept
366  {
367  return std::forward<result<void, E>>(result<void, E>(value));
368  }
369 
370  /**
371  * Create a new ok value using `void()` constructor.
372  *
373  * @return result<void, E>
374  */
375  constexpr static result<void, E> default_ok() noexcept
376  {
377  return std::move(result<void, E>());
378  }
379 
380  /**
381  * Create a new err value using `E()` constructor.
382  *
383  * @return result<void, E>
384  */
385  constexpr static result<void, E> default_err() noexcept
386  {
387  return std::move(result<void, E>(E()));
388  }
389 
390  // Inspection.
391 
392  /**
393  * Check if result contains ok value.
394  *
395  * @return bool
396  */
397  inline bool is_ok() const noexcept
398  {
399  return !var_err;
400  }
401 
402  /**
403  * Check if result contains err value.
404  *
405  * @return bool
406  */
407  inline bool is_err() const noexcept
408  {
409  return !!var_err;
410  }
411 
412  explicit inline operator bool() const noexcept
413  {
414  return is_ok();
415  }
416 
417  void ok_value()
418  {
419  if (is_err()) {
420  throw new bad_result_access("Attempted to get ok from err");
421  }
422  }
423 
424  constexpr E const& err_value() const&
425  {
426  if (!is_err()) {
427  throw new bad_result_access("Attempted to err from ok");
428  }
429  return *var_err;
430  }
431 
432  constexpr E& err_value() &
433  {
434  if (!is_err()) {
435  throw new bad_result_access("Attempted to err from ok");
436  }
437  return *var_err;
438  }
439 
440  constexpr E&& err_value() &&
441  {
442  if (!is_err()) {
443  throw new bad_result_access("Attempted to err from ok");
444  }
445  return std::move(*var_err);
446  }
447 
448  template <class V>
449  constexpr E err_value_or(V&& v) const&
450  {
451  return var_err.value_or(std::forward<V>(v));
452  }
453 
454  template <class V>
455  constexpr E err_value_or(V&& v) &&
456  {
457  return var_err.value_or(std::forward<V>(v));
458  }
459 
460  // Functional helpers.
461 
462  /**
463  * Maps a result<void, E> to result<U, E> (where U is return value of F(void)) by applying a
464  * function F to a
465  * contained ok value, leaving an err value untouched.
466  *
467  * This function can be used to compose the results of two functions.
468  *
469  * @param f F() -> U
470  * @return maybe::result<U, E>
471  */
472  template <typename F>
473  inline auto map(F f) noexcept -> maybe::result<typename std::result_of<F()>::type, E>;
474 
475  /**
476  * Maps a result<void, E> to result<U, E> by always returning provided U value on success,
477  * leaving an err value untouched.
478  *
479  * This function can be used to compose the results of two functions.
480  *
481  * @param value U
482  * @return maybe::result<U, E>
483  */
484  template <typename U>
485  inline auto map_value(U value) noexcept -> maybe::result<U, E>;
486 
487  /**
488  * Maps a result<void, E> to result<void, U> (where U is return value of F(E)) by applying a
489  * function to a
490  * function to a
491  * contained err value, leaving an ok value untouched.
492  *
493  * This function can be used to pass through a successful result while changing an error.
494  *
495  * @param f F(E) -> U
496  * @return maybe::result<void, U>
497  */
498  template <typename F>
499  inline auto map_err(F f) noexcept
501 
502  /**
503  * Maps a result<void, E> to result<void, U> by always returning provided U value on error,
504  * leaving an ok value untouched.
505  *
506  * This function can be used to compose the results of two functions.
507  *
508  * @param value U
509  * @return maybe::result<void, U>
510  */
511  template <typename U>
512  inline auto map_err_value(U value) noexcept -> maybe::result<void, U>;
513 
514  /**
515  * Calls op if the result is ok, otherwise returns the err value of self.
516  *
517  * This function can be used for control flow based on result values.
518  *
519  * @param f F() -> maybe::result<U, E>
520  * @return maybe::result<U, E>
521  */
522  template <typename F>
523  inline auto and_then(F op) noexcept -> typename std::result_of<F()>::type;
524 
525  /**
526  * Converts into another result with ok type void and forwards the same error.
527  *
528  * @return maybe::result<void, E>
529  */
530  inline auto into_err() noexcept -> maybe::result<void, E>;
531  };
532 
533  template <typename E>
534  constexpr bool operator==(const result<void, E>& x, const result<void, E>& y)
535  {
536  return x.is_ok() && y.is_ok()
537  ? true
538  : (x.is_err() && y.is_err() ? x.err_value() == y.err_value() : false);
539  }
540 
541  template <typename E>
542  constexpr bool operator!=(const result<void, E>& x, const result<void, E>& y)
543  {
544  return !(x == y);
545  }
546 }
547 
548 #include "result.inline.hpp"
auto and_then(F op) noexcept-> typename std::result_of< F(T)>::type
auto map_void() noexcept-> maybe::result< void, E >
bool is_ok() const noexcept
Definition: result.hpp:126
bool is_err() const noexcept
Definition: result.hpp:407
auto map_err_value(U value) noexcept-> maybe::result< T, U >
static constexpr result< T, E > ok(T &&value) noexcept
Definition: result.hpp:71
STL namespace.
static constexpr result< T, E > default_ok() noexcept
Definition: result.hpp:104
static constexpr result< T, E > default_err() noexcept
Definition: result.hpp:114
auto into_err() noexcept-> maybe::result< U, E > const
auto map_err(F f) noexcept-> maybe::result< T, typename std::result_of< F(E)>::type >
static constexpr result< T, E > err(E &&value) noexcept
Definition: result.hpp:88
static constexpr result< void, E > ok() noexcept
Definition: result.hpp:349
static constexpr result< void, E > err(E &&value) noexcept
Definition: result.hpp:360
auto map(F f) noexcept-> maybe::result< R, E >
bool is_err() const noexcept
Definition: result.hpp:136
bool is_ok() const noexcept
Definition: result.hpp:397
auto map_value(U value) noexcept-> maybe::result< U, E >
static constexpr result< void, E > default_ok() noexcept
Definition: result.hpp:375
static constexpr result< void, E > default_err() noexcept
Definition: result.hpp:385