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 
287  template <typename ResultType>
288  using InnerValueOf = typename std::decay_t<ResultType>::ValueInnerType;
289  template <typename ResultType>
290  using InnerErrorOf = typename std::decay_t<ResultType>::ErrorInnerType;
291 
298  template <typename Err1, typename Err2, typename V, typename Fn>
299  Result<V, Err1> map_error(const Result<V, Err2> &res, Fn &&map) noexcept {
300  return visit_in_place(res,
301  [](Value<V> val) -> Result<V, Err1> { return val; },
302  [map](Error<Err2> err) -> Result<V, Err1> {
303  return Error<Err1>{map(err.error)};
304  });
305  }
306 
307  // Factory methods for avoiding type specification
308  inline Value<void> makeValue() {
309  return Value<void>{};
310  }
311 
312  template <typename T>
313  Value<T> makeValue(T &&value) {
314  return Value<T>{std::forward<T>(value)};
315  }
316 
317  inline Error<void> makeError() {
318  return Error<void>{};
319  }
320 
321  template <typename E>
322  Error<E> makeError(E &&error) {
323  return Error<E>{std::forward<E>(error)};
324  }
325 
326  template <typename T>
327  constexpr bool isResult =
328  std::is_base_of<ResultBase, std::decay_t<T>>::value;
329  template <typename T>
330  constexpr bool isValue = std::is_base_of<ValueBase, std::decay_t<T>>::value;
331  template <typename T>
332  constexpr bool isError = std::is_base_of<ErrorBase, std::decay_t<T>>::value;
333 
342  template <typename Transformed, typename ErrorType, typename = void>
343  struct BindReturnType;
344 
346  template <typename Transformed, typename ErrorType>
347  struct BindReturnType<
348  Transformed,
349  ErrorType,
350  typename std::enable_if_t<
351  not isResult<Transformed> and not isValue<Transformed>>> {
352  using ReturnType = Result<Transformed, ErrorType>;
353  static ReturnType makeValue(Transformed &&result) {
354  return iroha::expected::makeValue(std::move(result));
355  }
356  };
357 
359  template <typename Transformed, typename ErrorType>
360  struct BindReturnType<Transformed,
361  ErrorType,
362  std::enable_if_t<isResult<Transformed>>> {
363  using ReturnType = Transformed;
364  static ReturnType makeValue(Transformed &&result) {
365  return std::move(result);
366  }
367  };
368 
370  template <typename Transformed, typename ErrorType>
371  struct BindReturnType<Transformed,
372  ErrorType,
373  std::enable_if_t<isValue<Transformed>>> {
374  using ReturnType = Result<typename Transformed::type, ErrorType>;
375  static ReturnType makeValue(Transformed &&result) {
376  return std::move(result);
377  }
378  };
379 
381  template <typename ErrorType>
382  struct BindReturnType<void, ErrorType> {
383  using ReturnType = Result<void, ErrorType>;
384  };
385 
386  template <typename ValueTransformer, typename Value, typename Error>
387  using BindReturnTypeHelper = typename std::enable_if_t<
388  not std::is_same<Value, void>::value,
389  BindReturnType<decltype(std::declval<ValueTransformer>()(
390  std::declval<Value>())),
391  Error>>;
392 
401  template <typename V,
403  typename E,
404  typename Transform,
405  typename TypeHelper = BindReturnTypeHelper<Transform, V, E>,
406  typename ReturnType = typename TypeHelper::ReturnType>
407  constexpr auto operator|(const Result<V, E> &r, Transform &&f)
408  -> ReturnType {
409  return r.match(
410  [&f](const auto &v) { return TypeHelper::makeValue(f(v.value)); },
411  [](const auto &e) { return ReturnType(makeError(e.error)); });
412  }
413 
415  template <typename V,
416  typename E,
417  typename Transform,
418  typename TypeHelper = BindReturnTypeHelper<Transform, V, E>,
419  typename ReturnType = typename TypeHelper::ReturnType>
420  constexpr auto operator|(Result<V, E> &&r, Transform &&f) -> ReturnType {
421  static_assert(isResult<ReturnType>, "wrong return_type");
422  return std::move(r).match(
423  [&f](auto &&v) {
424  return TypeHelper::makeValue(f(std::move(v.value)));
425  },
426  [](auto &&e) { return ReturnType(makeError(std::move(e.error))); });
427  }
428 
436  template <typename T,
438  typename E,
439  typename Procedure,
440  typename TypeHelper =
441  BindReturnType<decltype(std::declval<Procedure>()()), E>,
442  typename ReturnType = typename TypeHelper::ReturnType>
443  constexpr auto operator|(const Result<T, E> &r, Procedure f) ->
444  typename std::enable_if<not std::is_same<decltype(f()), void>::value,
445  ReturnType>::type {
446  return r.match(
447  [&f](const Value<T> &v) { return TypeHelper::makeValue(f()); },
448  [](const Error<E> &e) { return ReturnType(makeError(e.error)); });
449  }
450 
452  template <typename V,
453  typename E,
454  typename Procedure,
455  typename TypeHelper =
456  BindReturnType<decltype(std::declval<Procedure>()()), E>,
457  typename ReturnType = typename TypeHelper::ReturnType>
458  constexpr auto operator|(Result<V, E> &&r, Procedure f) ->
459  typename std::enable_if<not std::is_same<decltype(f()), void>::value,
460  ReturnType>::type {
461  return std::move(r).match(
462  [&f](const auto &) { return TypeHelper::makeValue(f()); },
463  [](auto &&e) { return ReturnType(makeError(std::move(e.error))); });
464  }
465 
467  template <typename R, typename F>
468  constexpr auto operator|=(R &r, F &&f) -> decltype(r = r | f) {
469  return r = r | std::forward<F>(f);
470  }
471 
476  template <typename ResultType,
477  typename = std::enable_if_t<isResult<ResultType>>>
478  bool hasValue(const ResultType &result) {
479  return boost::get<ValueOf<ResultType>>(&result);
480  }
481 
482  template <typename ResultType,
483  typename = std::enable_if_t<isResult<ResultType>>>
484  bool hasError(const ResultType &result) {
485  return boost::get<ErrorOf<ResultType>>(&result);
486  }
487 
494  template <typename ResultType,
496  typename = std::enable_if_t<isResult<ResultType>>>
497  boost::optional<InnerValueOf<ResultType>> resultToOptionalValue(
498  ResultType &&res) noexcept {
499  if (hasValue(res)) {
500  return boost::get<ValueOf<ResultType>>(std::forward<ResultType>(res))
501  .value;
502  }
503  return {};
504  }
505 
507  template <typename ResultType,
508  typename = std::enable_if_t<isResult<ResultType>>>
509  boost::optional<InnerErrorOf<ResultType>> resultToOptionalError(
510  ResultType &&res) noexcept {
511  if (hasError(res)) {
512  return boost::get<ErrorOf<ResultType>>(std::forward<ResultType>(res))
513  .error;
514  }
515  return {};
516  }
517 
518  template <typename E, typename V>
519  Result<typename V::value_type, std::decay_t<E>> optionalValueToResult(
520  V &&value, E &&error) {
521  if (value) {
522  return makeValue(std::move(value).value());
523  }
524  return makeError(std::move(error));
525  }
526 
527  template <typename V, typename E>
528  Result<std::decay_t<V>, typename E::value_type> optionalErrorToResult(
529  E &&error, V &&value) {
530  if (error) {
531  return makeError(std::move(error).value());
532  }
533  return makeValue(std::move(value));
534  }
535  } // namespace expected
536 } // namespace iroha
537 #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