hyperledger/iroha
Iroha - A simple, decentralized ledger http://iroha.tech
result.hpp
Go to the documentation of this file.
1 
6 #ifndef IROHA_RESULT_HPP
7 #define IROHA_RESULT_HPP
8 
9 #include "common/result_fwd.hpp"
10 
11 #include <ciso646>
12 #include <type_traits>
13 
14 #include <boost/optional.hpp>
15 #include <boost/variant.hpp>
16 
17 #include "common/visitor.hpp"
18 
19 /*
20  * Result is a type which represents value or an error, and values and errors
21  * are template parametrized. Working with value wrapped in result is done using
22  * match() function, which accepts 2 functions: for value and error cases. No
23  * accessor functions are provided.
24  */
25 
26 namespace iroha {
27  namespace expected {
28 
29  /*
30  * Value and error types can be constructed from any value or error, if
31  * underlying types are constructible. Example:
32  *
33  * @code
34  * Value<std::string> v = Value<const char *>("hello");
35  * @nocode
36  */
37 
38  struct ValueBase {};
39 
40  template <typename T>
41  struct Value : ValueBase {
42  using type = T;
43  template <
44  typename... Args,
45  typename = std::enable_if_t<std::is_constructible<T, Args...>::value>>
46  Value(Args &&... args) : value(std::forward<Args>(args)...) {}
47  T value;
48  template <typename V>
49  operator Value<V>() {
50  return {value};
51  }
52  };
53 
54  template <>
55  struct Value<void> {};
56 
57  struct ErrorBase {};
58 
59  template <typename E>
60  struct Error : ErrorBase {
61  using type = E;
62  template <
63  typename... Args,
64  typename = std::enable_if_t<std::is_constructible<E, Args...>::value>>
65  Error(Args &&... args) : error(std::forward<Args>(args)...) {}
66  E error;
67  template <typename V>
68  operator Error<V>() {
69  return {error};
70  }
71  };
72 
73  template <>
74  struct Error<void> {};
75 
76  class ResultException : public std::runtime_error {
77  using std::runtime_error::runtime_error;
78  };
79 
80  struct ResultBase {};
81 
88  template <typename V, typename E>
89  class Result : ResultBase, public boost::variant<Value<V>, Error<E>> {
90  template <typename OV, typename OE>
91  friend class Result;
92 
93  using variant_type = boost::variant<Value<V>, Error<E>>;
94  using variant_type::variant_type; // inherit constructors
95 
96  public:
97  using ValueType = Value<V>;
98  using ErrorType = Error<E>;
99 
100  using ValueInnerType = V;
101  using ErrorInnerType = E;
102 
103  Result() = default;
104 
105  template <typename OV, typename OE>
106  Result(Result<OV, OE> r)
107  : Result(visit_in_place(std::move(r),
108  [](Value<OV> &v) -> Result<V, E> {
109  return ValueType{std::move(v.value)};
110  },
111  [](Value<OV> &&v) -> Result<V, E> {
112  return ValueType{std::move(v.value)};
113  },
114  [](Error<OE> &e) -> Result<V, E> {
115  return ErrorType{std::move(e.error)};
116  },
117  [](Error<OE> &&e) -> Result<V, E> {
118  return ErrorType{std::move(e.error)};
119  })) {}
120 
130  template <typename ValueMatch, typename ErrorMatch>
131  constexpr auto match(ValueMatch &&value_func, ErrorMatch &&error_func) & {
132  return visit_in_place(*this,
133  [f = std::forward<ValueMatch>(value_func)](
134  ValueType &v) { return f(v); },
135  [f = std::forward<ErrorMatch>(error_func)](
136  ErrorType &e) { return f(e); });
137  }
138 
142  template <typename ValueMatch, typename ErrorMatch>
143  constexpr auto match(ValueMatch &&value_func,
144  ErrorMatch &&error_func) && {
145  return visit_in_place(*this,
146  [f = std::forward<ValueMatch>(value_func)](
147  ValueType &v) { return f(std::move(v)); },
148  [f = std::forward<ErrorMatch>(error_func)](
149  ErrorType &e) { return f(std::move(e)); });
150  }
151 
155  template <typename ValueMatch, typename ErrorMatch>
156  constexpr auto match(ValueMatch &&value_func,
157  ErrorMatch &&error_func) const & {
158  return visit_in_place(*this,
159  [f = std::forward<ValueMatch>(value_func)](
160  const ValueType &v) { return f(v); },
161  [f = std::forward<ErrorMatch>(error_func)](
162  const ErrorType &e) { return f(e); });
163  }
164 
176  template <typename Value>
177  constexpr Result<Value, E> and_res(const Result<Value, E> &new_res) const
178  noexcept {
179  return visit_in_place(
180  *this,
181  [res = new_res](ValueType) { return res; },
182  [](ErrorType err) -> Result<Value, E> { return err; });
183  }
184 
196  template <typename Value>
197  constexpr Result<Value, E> or_res(const Result<Value, E> &new_res) const
198  noexcept {
199  return visit_in_place(
200  *this,
201  [](ValueType val) -> Result<Value, E> { return val; },
202  [res = new_res](ErrorType) { return res; });
203  }
204 
205  using AssumeValueHelper =
206  std::conditional_t<std::is_void<ValueInnerType>::value,
207  void *,
208  ValueInnerType>;
209 
211  template <typename ReturnType = const AssumeValueHelper &>
212  std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
213  assumeValue() const & {
214  const auto *val = boost::get<ValueType>(this);
215  if (val != nullptr) {
216  return val->value;
217  }
218  throw ResultException("Value expected, but got an Error.");
219  }
220 
222  template <typename ReturnType = AssumeValueHelper &>
223  std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
224  assumeValue() & {
225  auto val = boost::get<ValueType>(this);
226  if (val != nullptr) {
227  return val->value;
228  }
229  throw ResultException("Value expected, but got an Error.");
230  }
231 
233  template <typename ReturnType = AssumeValueHelper &&>
234  std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
235  assumeValue() && {
236  auto val = boost::get<ValueType>(this);
237  if (val != nullptr) {
238  return std::move(val->value);
239  }
240  throw ResultException("Value expected, but got an Error.");
241  }
242 
243  using AssumeErrorHelper =
244  std::conditional_t<std::is_void<ErrorInnerType>::value,
245  void *,
246  ErrorInnerType>;
247 
249  template <typename ReturnType = const AssumeErrorHelper &>
250  std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
251  assumeError() const & {
252  const auto *err = boost::get<ErrorType>(this);
253  if (err != nullptr) {
254  return err->error;
255  }
256  throw ResultException("Error expected, but got a Value.");
257  }
258 
260  template <typename ReturnType = AssumeErrorHelper &>
261  std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
262  assumeError() & {
263  auto err = boost::get<ErrorType>(this);
264  if (err != nullptr) {
265  return err->error;
266  }
267  throw ResultException("Error expected, but got a Value.");
268  }
269 
271  template <typename ReturnType = AssumeErrorHelper &&>
272  std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
273  assumeError() && {
274  auto err = boost::get<ErrorType>(this);
275  if (err != nullptr) {
276  return std::move(err->error);
277  }
278  throw ResultException("Error expected, but got a Value.");
279  }
280  };
281 
282  template <typename ResultType>
283  using ValueOf = typename std::decay_t<ResultType>::ValueType;
284  template <typename ResultType>
285  using ErrorOf = typename std::decay_t<ResultType>::ErrorType;
286 
293  template <typename Err1, typename Err2, typename V, typename Fn>
294  Result<V, Err1> map_error(const Result<V, Err2> &res, Fn &&map) noexcept {
295  return visit_in_place(res,
296  [](Value<V> val) -> Result<V, Err1> { return val; },
297  [map](Error<Err2> err) -> Result<V, Err1> {
298  return Error<Err1>{map(err.error)};
299  });
300  }
301 
302  // Factory methods for avoiding type specification
303  inline Value<void> makeValue() {
304  return Value<void>{};
305  }
306 
307  template <typename T>
308  Value<T> makeValue(T &&value) {
309  return Value<T>{std::forward<T>(value)};
310  }
311 
312  inline Error<void> makeError() {
313  return Error<void>{};
314  }
315 
316  template <typename E>
317  Error<E> makeError(E &&error) {
318  return Error<E>{std::forward<E>(error)};
319  }
320 
321  template <typename T>
322  constexpr bool isResult =
323  std::is_base_of<ResultBase, std::decay_t<T>>::value;
324  template <typename T>
325  constexpr bool isValue = std::is_base_of<ValueBase, std::decay_t<T>>::value;
326  template <typename T>
327  constexpr bool isError = std::is_base_of<ErrorBase, std::decay_t<T>>::value;
328 
337  template <typename Transformed, typename ErrorType, typename = void>
338  struct BindReturnType;
339 
341  template <typename Transformed, typename ErrorType>
342  struct BindReturnType<
343  Transformed,
344  ErrorType,
345  typename std::enable_if_t<
346  not isResult<Transformed> and not isValue<Transformed>>> {
347  using ReturnType = Result<Transformed, ErrorType>;
348  static ReturnType makeValue(Transformed &&result) {
349  return iroha::expected::makeValue(std::move(result));
350  }
351  };
352 
354  template <typename Transformed, typename ErrorType>
355  struct BindReturnType<Transformed,
356  ErrorType,
357  std::enable_if_t<isResult<Transformed>>> {
358  using ReturnType = Transformed;
359  static ReturnType makeValue(Transformed &&result) {
360  return std::move(result);
361  }
362  };
363 
365  template <typename Transformed, typename ErrorType>
366  struct BindReturnType<Transformed,
367  ErrorType,
368  std::enable_if_t<isValue<Transformed>>> {
369  using ReturnType = Result<typename Transformed::type, ErrorType>;
370  static ReturnType makeValue(Transformed &&result) {
371  return std::move(result);
372  }
373  };
374 
376  template <typename ErrorType>
377  struct BindReturnType<void, ErrorType> {
378  using ReturnType = Result<void, ErrorType>;
379  };
380 
381  template <typename ValueTransformer, typename Value, typename Error>
382  using BindReturnTypeHelper = typename std::enable_if_t<
383  not std::is_same<Value, void>::value,
384  BindReturnType<decltype(std::declval<ValueTransformer>()(
385  std::declval<Value>())),
386  Error>>;
387 
396  template <typename V,
398  typename E,
399  typename Transform,
400  typename TypeHelper = BindReturnTypeHelper<Transform, V, E>,
401  typename ReturnType = typename TypeHelper::ReturnType>
402  constexpr auto operator|(const Result<V, E> &r, Transform &&f)
403  -> ReturnType {
404  return r.match(
405  [&f](const auto &v) { return TypeHelper::makeValue(f(v.value)); },
406  [](const auto &e) { return ReturnType(makeError(e.error)); });
407  }
408 
410  template <typename V,
411  typename E,
412  typename Transform,
413  typename TypeHelper = BindReturnTypeHelper<Transform, V, E>,
414  typename ReturnType = typename TypeHelper::ReturnType>
415  constexpr auto operator|(Result<V, E> &&r, Transform &&f) -> ReturnType {
416  static_assert(isResult<ReturnType>, "wrong return_type");
417  return std::move(r).match(
418  [&f](auto &&v) {
419  return TypeHelper::makeValue(f(std::move(v.value)));
420  },
421  [](auto &&e) { return ReturnType(makeError(std::move(e.error))); });
422  }
423 
431  template <typename T,
433  typename E,
434  typename Procedure,
435  typename TypeHelper =
436  BindReturnType<decltype(std::declval<Procedure>()()), E>,
437  typename ReturnType = typename TypeHelper::ReturnType>
438  constexpr auto operator|(const Result<T, E> &r, Procedure f) ->
439  typename std::enable_if<not std::is_same<decltype(f()), void>::value,
440  ReturnType>::type {
441  return r.match(
442  [&f](const Value<T> &v) { return TypeHelper::makeValue(f()); },
443  [](const Error<E> &e) { return ReturnType(makeError(e.error)); });
444  }
445 
447  template <typename V,
448  typename E,
449  typename Procedure,
450  typename TypeHelper =
451  BindReturnType<decltype(std::declval<Procedure>()()), E>,
452  typename ReturnType = typename TypeHelper::ReturnType>
453  constexpr auto operator|(Result<V, E> &&r, Procedure f) ->
454  typename std::enable_if<not std::is_same<decltype(f()), void>::value,
455  ReturnType>::type {
456  return std::move(r).match(
457  [&f](const auto &) { return TypeHelper::makeValue(f()); },
458  [](auto &&e) { return ReturnType(makeError(std::move(e.error))); });
459  }
460 
462  template <typename R, typename F>
463  constexpr auto operator|=(R &r, F &&f) -> decltype(r = r | f) {
464  return r = r | std::forward<F>(f);
465  }
466 
471  template <typename ResultType,
472  typename = std::enable_if_t<isResult<ResultType>>>
473  bool hasValue(const ResultType &result) {
474  return boost::get<ValueOf<ResultType>>(&result);
475  }
476 
477  template <typename ResultType,
478  typename = std::enable_if_t<isResult<ResultType>>>
479  bool hasError(const ResultType &result) {
480  return boost::get<ErrorOf<ResultType>>(&result);
481  }
482 
489  template <typename ResultType,
491  typename = std::enable_if_t<isResult<ResultType>>>
492  boost::optional<typename std::decay_t<ResultType>::ValueInnerType>
493  resultToOptionalValue(ResultType &&res) noexcept {
494  if (hasValue(res)) {
495  return boost::get<ValueOf<ResultType>>(std::forward<ResultType>(res))
496  .value;
497  }
498  return {};
499  }
500 
502  template <typename ResultType,
503  typename = std::enable_if_t<isResult<ResultType>>>
504  boost::optional<typename std::decay_t<ResultType>::ErrorInnerType>
505  resultToOptionalError(ResultType &&res) noexcept {
506  if (hasError(res)) {
507  return boost::get<ErrorOf<ResultType>>(std::forward<ResultType>(res))
508  .error;
509  }
510  return {};
511  }
512 
513  template <typename E, typename V>
514  Result<typename V::value_type, std::decay_t<E>> optionalValueToResult(
515  V &&value, E &&error) {
516  if (value) {
517  return makeValue(std::move(value).value());
518  }
519  return makeError(std::move(error));
520  }
521 
522  template <typename V, typename E>
523  Result<std::decay_t<V>, typename E::value_type> optionalErrorToResult(
524  E &&error, V &&value) {
525  if (error) {
526  return makeError(std::move(error).value());
527  }
528  return makeValue(std::move(value));
529  }
530  } // namespace expected
531 } // namespace iroha
532 #endif // IROHA_RESULT_HPP
Definition: peer.hpp:48
auto operator|(T &&t, Transform &&f) -> std::enable_if_t< not std::is_same< decltype(std::forward< Transform >(f)(*std::forward< T >(t))), void >::value, decltype(std::forward< Transform >(f)(*std::forward< T >(t)))>
Definition: bind.hpp:42
Definition: block_query.hpp:15