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 <string>
13 #include <type_traits>
14 
15 #include <boost/optional.hpp>
16 #include <boost/variant.hpp>
17 
18 #include "common/visitor.hpp"
19 
20 /*
21  * Result is a type which represents value or an error, and values and errors
22  * are template parametrized. Working with value wrapped in result is done using
23  * match() function, which accepts 2 functions: for value and error cases. No
24  * accessor functions are provided.
25  */
26 
27 namespace iroha {
28  namespace expected {
29 
30  /*
31  * Value and error types can be constructed from any value or error, if
32  * underlying types are constructible. Example:
33  *
34  * @code
35  * Value<std::string> v = Value<const char *>("hello");
36  * @nocode
37  */
38 
39  struct ValueBase {};
40 
41  template <typename T>
42  struct Value : ValueBase {
43  using type = T;
44  template <
45  typename... Args,
46  typename = std::enable_if_t<std::is_constructible<T, Args...>::value>>
47  Value(Args &&... args) : value(std::forward<Args>(args)...) {}
48  T value;
49  template <typename V>
50  operator Value<V>() {
51  return {value};
52  }
53  };
54 
55  template <>
56  struct Value<void> {};
57 
58  struct ErrorBase {};
59 
60  template <typename E>
61  struct Error : ErrorBase {
62  using type = E;
63  template <
64  typename... Args,
65  typename = std::enable_if_t<std::is_constructible<E, Args...>::value>>
66  Error(Args &&... args) : error(std::forward<Args>(args)...) {}
67  E error;
68  template <typename V>
69  operator Error<V>() {
70  return {error};
71  }
72  };
73 
74  template <>
75  struct Error<void> {};
76 
77  class ResultException : public std::runtime_error {
78  using std::runtime_error::runtime_error;
79  };
80 
81  struct ResultBase {};
82 
89  template <typename V, typename E = std::string>
90  class Result : ResultBase, public boost::variant<Value<V>, Error<E>> {
91  template <typename OV, typename OE>
92  friend class Result;
93 
94  using variant_type = boost::variant<Value<V>, Error<E>>;
95  using variant_type::variant_type; // inherit constructors
96 
97  public:
98  using ValueType = Value<V>;
99  using ErrorType = Error<E>;
100 
101  using ValueInnerType = V;
102  using ErrorInnerType = E;
103 
104  Result() = default;
105 
106  template <typename OV, typename OE>
107  Result(Result<OV, OE> r)
108  : Result(visit_in_place(std::move(r),
109  [](Value<OV> &v) -> Result<V, E> {
110  return ValueType{std::move(v.value)};
111  },
112  [](Value<OV> &&v) -> Result<V, E> {
113  return ValueType{std::move(v.value)};
114  },
115  [](Error<OE> &e) -> Result<V, E> {
116  return ErrorType{std::move(e.error)};
117  },
118  [](Error<OE> &&e) -> Result<V, E> {
119  return ErrorType{std::move(e.error)};
120  })) {}
121 
131  template <typename ValueMatch, typename ErrorMatch>
132  constexpr auto match(ValueMatch &&value_func, ErrorMatch &&error_func) & {
133  return visit_in_place(*this,
134  [f = std::forward<ValueMatch>(value_func)](
135  ValueType &v) { return f(v); },
136  [f = std::forward<ErrorMatch>(error_func)](
137  ErrorType &e) { return f(e); });
138  }
139 
143  template <typename ValueMatch, typename ErrorMatch>
144  constexpr auto match(ValueMatch &&value_func,
145  ErrorMatch &&error_func) && {
146  return visit_in_place(*this,
147  [f = std::forward<ValueMatch>(value_func)](
148  ValueType &v) { return f(std::move(v)); },
149  [f = std::forward<ErrorMatch>(error_func)](
150  ErrorType &e) { return f(std::move(e)); });
151  }
152 
156  template <typename ValueMatch, typename ErrorMatch>
157  constexpr auto match(ValueMatch &&value_func,
158  ErrorMatch &&error_func) const & {
159  return visit_in_place(*this,
160  [f = std::forward<ValueMatch>(value_func)](
161  const ValueType &v) { return f(v); },
162  [f = std::forward<ErrorMatch>(error_func)](
163  const ErrorType &e) { return f(e); });
164  }
165 
177  template <typename Value>
178  constexpr Result<Value, E> and_res(const Result<Value, E> &new_res) const
179  noexcept {
180  return visit_in_place(
181  *this,
182  [res = new_res](ValueType) { return res; },
183  [](ErrorType err) -> Result<Value, E> { return err; });
184  }
185 
197  template <typename Value>
198  constexpr Result<Value, E> or_res(const Result<Value, E> &new_res) const
199  noexcept {
200  return visit_in_place(
201  *this,
202  [](ValueType val) -> Result<Value, E> { return val; },
203  [res = new_res](ErrorType) { return res; });
204  }
205 
206  using AssumeValueHelper =
207  std::conditional_t<std::is_void<ValueInnerType>::value,
208  void *,
209  ValueInnerType>;
210 
212  template <typename ReturnType = const AssumeValueHelper &>
213  std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
214  assumeValue() const & {
215  const auto *val = boost::get<ValueType>(this);
216  if (val != nullptr) {
217  return val->value;
218  }
219  throw ResultException("Value expected, but got an Error.");
220  }
221 
223  template <typename ReturnType = AssumeValueHelper &>
224  std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
225  assumeValue() & {
226  auto val = boost::get<ValueType>(this);
227  if (val != nullptr) {
228  return val->value;
229  }
230  throw ResultException("Value expected, but got an Error.");
231  }
232 
234  template <typename ReturnType = AssumeValueHelper &&>
235  std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
236  assumeValue() && {
237  auto val = boost::get<ValueType>(this);
238  if (val != nullptr) {
239  return std::move(val->value);
240  }
241  throw ResultException("Value expected, but got an Error.");
242  }
243 
244  using AssumeErrorHelper =
245  std::conditional_t<std::is_void<ErrorInnerType>::value,
246  void *,
247  ErrorInnerType>;
248 
250  template <typename ReturnType = const AssumeErrorHelper &>
251  std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
252  assumeError() const & {
253  const auto *err = boost::get<ErrorType>(this);
254  if (err != nullptr) {
255  return err->error;
256  }
257  throw ResultException("Error expected, but got a Value.");
258  }
259 
261  template <typename ReturnType = AssumeErrorHelper &>
262  std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
263  assumeError() & {
264  auto err = boost::get<ErrorType>(this);
265  if (err != nullptr) {
266  return err->error;
267  }
268  throw ResultException("Error expected, but got a Value.");
269  }
270 
272  template <typename ReturnType = AssumeErrorHelper &&>
273  std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
274  assumeError() && {
275  auto err = boost::get<ErrorType>(this);
276  if (err != nullptr) {
277  return std::move(err->error);
278  }
279  throw ResultException("Error expected, but got a Value.");
280  }
281  };
282 
283  template <typename ResultType>
284  using ValueOf = typename std::decay_t<ResultType>::ValueType;
285  template <typename ResultType>
286  using ErrorOf = typename std::decay_t<ResultType>::ErrorType;
287 
288  template <typename ResultType>
289  using InnerValueOf = typename std::decay_t<ResultType>::ValueInnerType;
290  template <typename ResultType>
291  using InnerErrorOf = typename std::decay_t<ResultType>::ErrorInnerType;
292 
299  template <typename Err1, typename Err2, typename V, typename Fn>
300  Result<V, Err1> map_error(const Result<V, Err2> &res, Fn &&map) noexcept {
301  return visit_in_place(res,
302  [](Value<V> val) -> Result<V, Err1> { return val; },
303  [map](Error<Err2> err) -> Result<V, Err1> {
304  return Error<Err1>{map(err.error)};
305  });
306  }
307 
308  // Factory methods for avoiding type specification
309  inline Value<void> makeValue() {
310  return Value<void>{};
311  }
312 
313  template <typename T>
314  Value<T> makeValue(T &&value) {
315  return Value<T>{std::forward<T>(value)};
316  }
317 
318  inline Error<void> makeError() {
319  return Error<void>{};
320  }
321 
322  template <typename E>
323  Error<E> makeError(E &&error) {
324  return Error<E>{std::forward<E>(error)};
325  }
326 
327  template <typename T>
328  constexpr bool isResult =
329  std::is_base_of<ResultBase, std::decay_t<T>>::value;
330  template <typename T>
331  constexpr bool isValue = std::is_base_of<ValueBase, std::decay_t<T>>::value;
332  template <typename T>
333  constexpr bool isError = std::is_base_of<ErrorBase, std::decay_t<T>>::value;
334 
343  template <typename Transformed, typename ErrorType, typename = void>
344  struct BindReturnType;
345 
347  template <typename Transformed, typename ErrorType>
348  struct BindReturnType<
349  Transformed,
350  ErrorType,
351  typename std::enable_if_t<
352  not isResult<Transformed> and not isValue<Transformed>>> {
353  using ReturnType = Result<Transformed, ErrorType>;
354  static ReturnType makeValue(Transformed &&result) {
355  return iroha::expected::makeValue(std::move(result));
356  }
357  };
358 
360  template <typename Transformed, typename ErrorType>
361  struct BindReturnType<Transformed,
362  ErrorType,
363  std::enable_if_t<isResult<Transformed>>> {
364  using ReturnType = Transformed;
365  static ReturnType makeValue(Transformed &&result) {
366  return std::move(result);
367  }
368  };
369 
371  template <typename Transformed, typename ErrorType>
372  struct BindReturnType<Transformed,
373  ErrorType,
374  std::enable_if_t<isValue<Transformed>>> {
375  using ReturnType = Result<typename Transformed::type, ErrorType>;
376  static ReturnType makeValue(Transformed &&result) {
377  return std::move(result);
378  }
379  };
380 
382  template <typename ErrorType>
383  struct BindReturnType<void, ErrorType> {
384  using ReturnType = Result<void, ErrorType>;
385  };
386 
387  template <typename ValueTransformer, typename Value, typename Error>
388  using BindReturnTypeHelper = typename std::enable_if_t<
389  not std::is_same<Value, void>::value,
390  BindReturnType<decltype(std::declval<ValueTransformer>()(
391  std::declval<Value>())),
392  Error>>;
393 
402  template <typename V,
404  typename E,
405  typename Transform,
406  typename TypeHelper = BindReturnTypeHelper<Transform, V, E>,
407  typename ReturnType = typename TypeHelper::ReturnType>
408  constexpr auto operator|(const Result<V, E> &r, Transform &&f)
409  -> ReturnType {
410  return r.match(
411  [&f](const auto &v) { return TypeHelper::makeValue(f(v.value)); },
412  [](const auto &e) { return ReturnType(makeError(e.error)); });
413  }
414 
416  template <typename V,
417  typename E,
418  typename Transform,
419  typename TypeHelper = BindReturnTypeHelper<Transform, V, E>,
420  typename ReturnType = typename TypeHelper::ReturnType>
421  constexpr auto operator|(Result<V, E> &&r, Transform &&f) -> ReturnType {
422  static_assert(isResult<ReturnType>, "wrong return_type");
423  return std::move(r).match(
424  [&f](auto &&v) {
425  return TypeHelper::makeValue(f(std::move(v.value)));
426  },
427  [](auto &&e) { return ReturnType(makeError(std::move(e.error))); });
428  }
429 
437  template <typename T,
439  typename E,
440  typename Procedure,
441  typename TypeHelper =
442  BindReturnType<decltype(std::declval<Procedure>()()), E>,
443  typename ReturnType = typename TypeHelper::ReturnType>
444  constexpr auto operator|(const Result<T, E> &r, Procedure f) ->
445  typename std::enable_if<not std::is_same<decltype(f()), void>::value,
446  ReturnType>::type {
447  return r.match(
448  [&f](const Value<T> &v) { return TypeHelper::makeValue(f()); },
449  [](const Error<E> &e) { return ReturnType(makeError(e.error)); });
450  }
451 
453  template <typename V,
454  typename E,
455  typename Procedure,
456  typename TypeHelper =
457  BindReturnType<decltype(std::declval<Procedure>()()), E>,
458  typename ReturnType = typename TypeHelper::ReturnType>
459  constexpr auto operator|(Result<V, E> &&r, Procedure f) ->
460  typename std::enable_if<not std::is_same<decltype(f()), void>::value,
461  ReturnType>::type {
462  return std::move(r).match(
463  [&f](const auto &) { return TypeHelper::makeValue(f()); },
464  [](auto &&e) { return ReturnType(makeError(std::move(e.error))); });
465  }
466 
468  template <typename R, typename F>
469  constexpr auto operator|=(R &r, F &&f) -> decltype(r = r | f) {
470  return r = r | std::forward<F>(f);
471  }
472 
477  template <typename ResultType,
478  typename = std::enable_if_t<isResult<ResultType>>>
479  bool hasValue(const ResultType &result) {
480  return boost::get<ValueOf<ResultType>>(&result);
481  }
482 
483  template <typename ResultType,
484  typename = std::enable_if_t<isResult<ResultType>>>
485  bool hasError(const ResultType &result) {
486  return boost::get<ErrorOf<ResultType>>(&result);
487  }
488 
495  template <typename ResultType,
497  typename = std::enable_if_t<isResult<ResultType>>>
498  boost::optional<InnerValueOf<ResultType>> resultToOptionalValue(
499  ResultType &&res) noexcept {
500  if (hasValue(res)) {
501  return boost::get<ValueOf<ResultType>>(std::forward<ResultType>(res))
502  .value;
503  }
504  return {};
505  }
506 
508  template <typename ResultType,
509  typename = std::enable_if_t<isResult<ResultType>>>
510  boost::optional<InnerErrorOf<ResultType>> resultToOptionalError(
511  ResultType &&res) noexcept {
512  if (hasError(res)) {
513  return boost::get<ErrorOf<ResultType>>(std::forward<ResultType>(res))
514  .error;
515  }
516  return {};
517  }
518 
519  template <typename E, typename V>
520  Result<typename V::value_type, std::decay_t<E>> optionalValueToResult(
521  V &&value, E &&error) {
522  if (value) {
523  return makeValue(std::move(value).value());
524  }
525  return makeError(std::move(error));
526  }
527 
528  template <typename V, typename E>
529  Result<std::decay_t<V>, typename E::value_type> optionalErrorToResult(
530  E &&error, V &&value) {
531  if (error) {
532  return makeError(std::move(error).value());
533  }
534  return makeValue(std::move(value));
535  }
536  } // namespace expected
537 } // namespace iroha
538 #endif // IROHA_RESULT_HPP
Definition: round.cpp:51
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
expected::Result< T, DbError > makeError(uint32_t code, char const *format, Args &&... args)
Definition: rocksdb_common.hpp:471