6 #ifndef IROHA_RESULT_HPP 7 #define IROHA_RESULT_HPP 12 #include <type_traits> 14 #include <boost/optional.hpp> 15 #include <boost/variant.hpp> 41 struct Value : ValueBase {
45 typename = std::enable_if_t<std::is_constructible<T, Args...>::value>>
46 Value(Args &&... args) : value(
std::forward<Args>(args)...) {}
55 struct Value<void> {};
60 struct Error : ErrorBase {
64 typename = std::enable_if_t<std::is_constructible<E, Args...>::value>>
65 Error(Args &&... args) : error(
std::forward<Args>(args)...) {}
74 struct Error<void> {};
76 class ResultException :
public std::runtime_error {
77 using std::runtime_error::runtime_error;
88 template <
typename V,
typename E>
89 class Result : ResultBase,
public boost::variant<Value<V>, Error<E>> {
90 template <
typename OV,
typename OE>
93 using variant_type = boost::variant<Value<V>, Error<E>>;
94 using variant_type::variant_type;
97 using ValueType = Value<V>;
98 using ErrorType = Error<E>;
100 using ValueInnerType = V;
101 using ErrorInnerType = E;
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)};
111 [](Value<OV> &&v) -> Result<V, E> {
112 return ValueType{std::move(v.value)};
114 [](Error<OE> &e) -> Result<V, E> {
115 return ErrorType{std::move(e.error)};
117 [](Error<OE> &&e) -> Result<V, E> {
118 return ErrorType{std::move(e.error)};
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); });
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)); });
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); });
176 template <
typename Value>
177 constexpr Result<Value, E> and_res(
const Result<Value, E> &new_res)
const 179 return visit_in_place(
181 [res = new_res](ValueType) {
return res; },
182 [](ErrorType err) -> Result<Value, E> {
return err; });
196 template <
typename Value>
197 constexpr Result<Value, E> or_res(
const Result<Value, E> &new_res)
const 199 return visit_in_place(
201 [](ValueType val) -> Result<Value, E> {
return val; },
202 [res = new_res](ErrorType) {
return res; });
205 using AssumeValueHelper =
206 std::conditional_t<std::is_void<ValueInnerType>::value,
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) {
218 throw ResultException(
"Value expected, but got an Error.");
222 template <
typename ReturnType = AssumeValueHelper &>
223 std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
225 auto val = boost::get<ValueType>(
this);
226 if (val !=
nullptr) {
229 throw ResultException(
"Value expected, but got an Error.");
233 template <
typename ReturnType = AssumeValueHelper &&>
234 std::enable_if_t<not std::is_void<ValueInnerType>::value, ReturnType>
236 auto val = boost::get<ValueType>(
this);
237 if (val !=
nullptr) {
238 return std::move(val->value);
240 throw ResultException(
"Value expected, but got an Error.");
243 using AssumeErrorHelper =
244 std::conditional_t<std::is_void<ErrorInnerType>::value,
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) {
256 throw ResultException(
"Error expected, but got a Value.");
260 template <
typename ReturnType = AssumeErrorHelper &>
261 std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
263 auto err = boost::get<ErrorType>(
this);
264 if (err !=
nullptr) {
267 throw ResultException(
"Error expected, but got a Value.");
271 template <
typename ReturnType = AssumeErrorHelper &&>
272 std::enable_if_t<not std::is_void<ErrorInnerType>::value, ReturnType>
274 auto err = boost::get<ErrorType>(
this);
275 if (err !=
nullptr) {
276 return std::move(err->error);
278 throw ResultException(
"Error expected, but got a Value.");
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;
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;
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)};
308 inline Value<void> makeValue() {
309 return Value<void>{};
312 template <
typename T>
313 Value<T> makeValue(T &&value) {
314 return Value<T>{std::forward<T>(value)};
317 inline Error<void> makeError() {
318 return Error<void>{};
321 template <
typename E>
322 Error<E> makeError(E &&error) {
323 return Error<E>{std::forward<E>(error)};
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;
342 template <
typename Transformed,
typename ErrorType,
typename =
void>
343 struct BindReturnType;
346 template <
typename Transformed,
typename ErrorType>
347 struct BindReturnType<
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));
359 template <
typename Transformed,
typename ErrorType>
360 struct BindReturnType<Transformed,
362 std::enable_if_t<isResult<Transformed>>> {
363 using ReturnType = Transformed;
364 static ReturnType makeValue(Transformed &&result) {
365 return std::move(result);
370 template <
typename Transformed,
typename ErrorType>
371 struct BindReturnType<Transformed,
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);
381 template <
typename ErrorType>
382 struct BindReturnType<void, ErrorType> {
383 using ReturnType = Result<void, ErrorType>;
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>())),
401 template <
typename V,
405 typename TypeHelper = BindReturnTypeHelper<Transform, V, E>,
406 typename ReturnType =
typename TypeHelper::ReturnType>
407 constexpr
auto operator|(
const Result<V, E> &r, Transform &&f)
410 [&f](
const auto &v) {
return TypeHelper::makeValue(f(v.value)); },
411 [](
const auto &e) {
return ReturnType(makeError(e.error)); });
415 template <
typename V,
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(
424 return TypeHelper::makeValue(f(std::move(v.value)));
426 [](
auto &&e) {
return ReturnType(makeError(std::move(e.error))); });
436 template <
typename T,
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,
447 [&f](
const Value<T> &v) {
return TypeHelper::makeValue(f()); },
448 [](
const Error<E> &e) {
return ReturnType(makeError(e.error)); });
452 template <
typename V,
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,
461 return std::move(r).match(
462 [&f](
const auto &) {
return TypeHelper::makeValue(f()); },
463 [](
auto &&e) {
return ReturnType(makeError(std::move(e.error))); });
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);
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);
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);
494 template <
typename ResultType,
496 typename = std::enable_if_t<isResult<ResultType>>>
497 boost::optional<InnerValueOf<ResultType>> resultToOptionalValue(
498 ResultType &&res) noexcept {
500 return boost::get<ValueOf<ResultType>>(std::forward<ResultType>(res))
507 template <
typename ResultType,
508 typename = std::enable_if_t<isResult<ResultType>>>
509 boost::optional<InnerErrorOf<ResultType>> resultToOptionalError(
510 ResultType &&res) noexcept {
512 return boost::get<ErrorOf<ResultType>>(std::forward<ResultType>(res))
518 template <
typename E,
typename V>
519 Result<typename V::value_type, std::decay_t<E>> optionalValueToResult(
520 V &&value, E &&error) {
522 return makeValue(std::move(value).value());
524 return makeError(std::move(error));
527 template <
typename V,
typename E>
528 Result<std::decay_t<V>,
typename E::value_type> optionalErrorToResult(
529 E &&error, V &&value) {
531 return makeError(std::move(error).value());
533 return makeValue(std::move(value));
537 #endif // IROHA_RESULT_HPP
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