diff options
Diffstat (limited to '')
-rw-r--r-- | src/base/result.h | 983 |
1 files changed, 983 insertions, 0 deletions
diff --git a/src/base/result.h b/src/base/result.h new file mode 100644 index 0000000..27500de --- /dev/null +++ b/src/base/result.h @@ -0,0 +1,983 @@ +/* + Mathieu Stefani, 03 mai 2016 + + This header provides a Result type that can be used to replace exceptions in code + that has to handle error. + + Result<T, E> can be used to return and propagate an error to the caller. Result<T, E> is an algebraic + data type that can either Ok(T) to represent success or Err(E) to represent an error. +*/ + +#pragma once + +#include <stdio.h> +#include <exception> +#include <functional> +#include <type_traits> + +namespace types { + template<typename T> + struct Ok { + Ok(const T& val) : val(val) { } + Ok(T&& val) : val(std::move(val)) { } + + T val; + }; + + template<> + struct Ok<void> { }; + + template<typename E> + struct Err { + Err(const E& val) : val(val) { } + Err(E&& val) : val(std::move(val)) { } + + E val; + }; + + template<> + struct Err<void> { }; +}; + +template<typename T, typename CleanT = typename std::decay<T>::type> +types::Ok<CleanT> Ok(T&& val) { + return types::Ok<CleanT>(std::forward<T>(val)); +} + +inline types::Ok<void> Ok() { + return {}; +} + +template<typename E, typename CleanE = typename std::decay<E>::type> +types::Err<CleanE> Err(E&& val) { + return types::Err<CleanE>(std::forward<E>(val)); +} + +inline types::Err<void> Err() { + return {}; +} + +template<typename T, typename E> struct Result; + +namespace details { + +template<typename ...> struct void_t { typedef void type; }; + +namespace impl { + template<typename Func> struct result_of; + + template<typename Ret, typename Cls, typename... Args> + struct result_of<Ret (Cls::*)(Args...)> : public result_of<Ret (Args...)> { }; + + template<typename Ret, typename... Args> + struct result_of<std::function<Ret (Args...)>> { + typedef Ret type; + }; +} + +template<typename Func> +struct result_of : public impl::result_of<decltype(&Func::operator())> { }; + +template<typename Ret, typename Cls, typename... Args> +struct result_of<Ret (Cls::*) (Args...) const> { + typedef Ret type; +}; + +template<typename Ret, typename... Args> +struct result_of<Ret (*)(Args...)> { + typedef Ret type; +}; + +template<typename R> +struct ResultOkType { typedef typename std::decay<R>::type type; }; + +template<typename T, typename E> +struct ResultOkType<Result<T, E>> { + typedef T type; +}; + +template<typename R> +struct ResultErrType { typedef R type; }; + +template<typename T, typename E> +struct ResultErrType<Result<T, E>> { + typedef typename std::remove_reference<E>::type type; +}; + +template<typename R> struct IsResult : public std::false_type { }; +template<typename T, typename E> +struct IsResult<Result<T, E>> : public std::true_type { }; + +namespace ok { + +namespace impl { + +template<typename T> struct Map; + +template<typename Ret, typename Cls, typename Arg> +struct Map<Ret (Cls::*)(Arg) const> : public Map<Ret (Arg)> { }; + +template<typename Ret, typename Cls, typename Arg> +struct Map<Ret (Cls::*)(Arg)> : public Map<Ret (Arg)> { }; + +// General implementation +template<typename Ret, typename Arg> +struct Map<Ret (Arg)> { + + static_assert(!IsResult<Ret>::value, + "Can not map a callback returning a Result, use then instead"); + + template<typename T, typename E, typename Func> + static Result<Ret, E> map(const Result<T, E>& result, Func func) { + + static_assert( + std::is_same<T, Arg>::value || + std::is_convertible<T, Arg>::value, + "Incompatible types detected"); + + if (result.isOk()) { + auto res = func(result.storage().template get<T>()); + return types::Ok<Ret>(std::move(res)); + } + + return types::Err<E>(result.storage().template get<E>()); + } +}; + +// Specialization for callback returning void +template<typename Arg> +struct Map<void (Arg)> { + + template<typename T, typename E, typename Func> + static Result<void, E> map(const Result<T, E>& result, Func func) { + + if (result.isOk()) { + func(result.storage().template get<T>()); + return types::Ok<void>(); + } + + return types::Err<E>(result.storage().template get<E>()); + } +}; + +// Specialization for a void Result +template<typename Ret> +struct Map<Ret (void)> { + + template<typename T, typename E, typename Func> + static Result<Ret, E> map(const Result<T, E>& result, Func func) { + static_assert(std::is_same<T, void>::value, + "Can not map a void callback on a non-void Result"); + + if (result.isOk()) { + auto ret = func(); + return types::Ok<Ret>(std::move(ret)); + } + + return types::Err<E>(result.storage().template get<E>()); + } +}; + +// Specialization for callback returning void on a void Result +template<> +struct Map<void (void)> { + + template<typename T, typename E, typename Func> + static Result<void, E> map(const Result<T, E>& result, Func func) { + static_assert(std::is_same<T, void>::value, + "Can not map a void callback on a non-void Result"); + + if (result.isOk()) { + func(); + return types::Ok<void>(); + } + + return types::Err<E>(result.storage().template get<E>()); + } +}; + +// General specialization for a callback returning a Result +template<typename U, typename E, typename Arg> +struct Map<Result<U, E> (Arg)> { + + template<typename T, typename Func> + static Result<U, E> map(const Result<T, E>& result, Func func) { + static_assert( + std::is_same<T, Arg>::value || + std::is_convertible<T, Arg>::value, + "Incompatible types detected"); + + if (result.isOk()) { + auto res = func(result.storage().template get<T>()); + return res; + } + + return types::Err<E>(result.storage().template get<E>()); + } +}; + +// Specialization for a void callback returning a Result +template<typename U, typename E> +struct Map<Result<U, E> (void)> { + + template<typename T, typename Func> + static Result<U, E> map(const Result<T, E>& result, Func func) { + static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result"); + + if (result.isOk()) { + auto res = func(); + return res; + } + + return types::Err<E>(result.storage().template get<E>()); + } + +}; + +} // namespace impl + +template<typename Func> struct Map; + +template<typename Ret, typename... Args> +struct Map<Ret (*) (Args...)> : public impl::Map<Ret (Args...)> { }; + +template<typename Ret, typename... Args> +struct Map<Ret (Args...)> : public impl::Map<Ret (Args...)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Map<Ret (Cls::*) (Args...)> : public impl::Map<Ret (Args...)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Map<Ret (Cls::*) (Args...) const> : public impl::Map<Ret (Args...)> { }; + +template<typename Ret, typename... Args> +struct Map<std::function<Ret (Args...)>> : public impl::Map<Ret (Args...)> { }; + +} // namespace ok + + +namespace err { + +namespace impl { + +template<typename T> struct Map; + +template<typename Ret, typename Cls, typename Arg> +struct Map<Ret (Cls::*)(Arg) const> { + + static_assert(!IsResult<Ret>::value, + "Can not map a callback returning a Result, use orElse instead"); + + template<typename T, typename E, typename Func> + static Result<T, Ret> map(const Result<T, E>& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get<E>()); + return types::Err<Ret>(res); + } + + return types::Ok<T>(result.storage().template get<T>()); + } + + template<typename E, typename Func> + static Result<void, Ret> map(const Result<void, E>& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get<E>()); + return types::Err<Ret>(res); + } + + return types::Ok<void>(); + } + + +}; + +} // namespace impl + +template<typename Func> struct Map : public impl::Map<decltype(&Func::operator())> { }; + +} // namespace err; + +namespace And { + +namespace impl { + + template<typename Func> struct Then; + + template<typename Ret, typename... Args> + struct Then<Ret (*)(Args...)> : public Then<Ret (Args...)> { }; + + template<typename Ret, typename Cls, typename... Args> + struct Then<Ret (Cls::*)(Args...)> : public Then<Ret (Args...)> { }; + + template<typename Ret, typename Cls, typename... Args> + struct Then<Ret (Cls::*)(Args...) const> : public Then<Ret (Args...)> { }; + + template<typename Ret, typename Arg> + struct Then<Ret (Arg)> { + static_assert(std::is_same<Ret, void>::value, + "then() should not return anything, use map() instead"); + + template<typename T, typename E, typename Func> + static Result<T, E> then(const Result<T, E>& result, Func func) { + if (result.isOk()) { + func(result.storage().template get<T>()); + } + return result; + } + }; + + template<typename Ret> + struct Then<Ret (void)> { + static_assert(std::is_same<Ret, void>::value, + "then() should not return anything, use map() instead"); + + template<typename T, typename E, typename Func> + static Result<T, E> then(const Result<T, E>& result, Func func) { + static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result"); + + if (result.isOk()) { + func(); + } + + return result; + } + }; + + +} // namespace impl + +template<typename Func> +struct Then : public impl::Then<decltype(&Func::operator())> { }; + +template<typename Ret, typename... Args> +struct Then<Ret (*) (Args...)> : public impl::Then<Ret (Args...)> { }; + +template<typename Ret, typename Arg> +struct Then<Ret (Arg)> : public impl::Then<Ret (Arg)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Then<Ret (Cls::*)(Args...)> : public impl::Then<Ret (Args...)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Then<Ret (Cls::*)(Args...) const> : public impl::Then<Ret (Args...)> { }; + +template<typename Ret, typename... Args> +struct Then<std::function<Ret (Args...)>> : public impl::Then<Ret (Args...)> { }; + +} // namespace And + +namespace Or { + +namespace impl { + + template<typename Func> struct Else; + + template<typename Ret, typename... Args> + struct Else<Ret (*)(Args...)> : public Else<Ret (Args...)> { }; + + template<typename Ret, typename Cls, typename... Args> + struct Else<Ret (Cls::*)(Args...)> : public Else<Ret (Args...)> { }; + + template<typename Ret, typename Cls, typename... Args> + struct Else<Ret (Cls::*)(Args...) const> : public Else<Ret (Args...)> { }; + + template<typename T, typename F, typename Arg> + struct Else<Result<T, F> (Arg)> { + + template<typename E, typename Func> + static Result<T, F> orElse(const Result<T, E>& result, Func func) { + static_assert( + std::is_same<E, Arg>::value || + std::is_convertible<E, Arg>::value, + "Incompatible types detected"); + + if (result.isErr()) { + auto res = func(result.storage().template get<E>()); + return res; + } + + return types::Ok<T>(result.storage().template get<T>()); + } + + template<typename E, typename Func> + static Result<void, F> orElse(const Result<void, E>& result, Func func) { + if (result.isErr()) { + auto res = func(result.storage().template get<E>()); + return res; + } + + return types::Ok<void>(); + } + + }; + + template<typename T, typename F> + struct Else<Result<T, F> (void)> { + + template<typename E, typename Func> + static Result<T, F> orElse(const Result<T, E>& result, Func func) { + static_assert(std::is_same<T, void>::value, + "Can not call a void-callback on a non-void Result"); + + if (result.isErr()) { + auto res = func(); + return res; + } + + return types::Ok<T>(result.storage().template get<T>()); + } + + template<typename E, typename Func> + static Result<void, F> orElse(const Result<void, E>& result, Func func) { + if (result.isErr()) { + auto res = func(); + return res; + } + + return types::Ok<void>(); + } + + }; + +} // namespace impl + +template<typename Func> +struct Else : public impl::Else<decltype(&Func::operator())> { }; + +template<typename Ret, typename... Args> +struct Else<Ret (*) (Args...)> : public impl::Else<Ret (Args...)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Else<Ret (Cls::*)(Args...)> : public impl::Else<Ret (Args...)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Else<Ret (Cls::*)(Args...) const> : public impl::Else<Ret (Args...)> { }; + +} // namespace Or + +namespace Other { + +namespace impl { + + template<typename Func> struct Wise; + + template<typename Ret, typename... Args> + struct Wise<Ret (*)(Args...)> : public Wise<Ret (Args...)> { }; + + template<typename Ret, typename Cls, typename... Args> + struct Wise<Ret (Cls::*)(Args...)> : public Wise<Ret (Args...)> { }; + + template<typename Ret, typename Cls, typename... Args> + struct Wise<Ret (Cls::*)(Args...) const> : public Wise<Ret (Args...)> { }; + + template<typename Ret, typename Arg> + struct Wise<Ret (Arg)> { + + template<typename T, typename E, typename Func> + static Result<T, E> otherwise(const Result<T, E>& result, Func func) { + static_assert( + std::is_same<E, Arg>::value || + std::is_convertible<E, Arg>::value, + "Incompatible types detected"); + + static_assert(std::is_same<Ret, void>::value, + "callback should not return anything, use mapError() for that"); + + if (result.isErr()) { + func(result.storage().template get<E>()); + } + return result; + } + + }; + +} // namespace impl + +template<typename Func> +struct Wise : public impl::Wise<decltype(&Func::operator())> { }; + +template<typename Ret, typename... Args> +struct Wise<Ret (*) (Args...)> : public impl::Wise<Ret (Args...)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Wise<Ret (Cls::*)(Args...)> : public impl::Wise<Ret (Args...)> { }; + +template<typename Ret, typename Cls, typename... Args> +struct Wise<Ret (Cls::*)(Args...) const> : public impl::Wise<Ret (Args...)> { }; + +} // namespace Other + +template<typename T, typename E, typename Func> +decltype(auto) map(const Result<T, E>& result, Func func) { + return ok::Map<Func>::map(result, func); +} + +template<typename T, typename E, typename Func, + typename Ret = + Result<T, + typename details::ResultErrType< + typename details::result_of<Func>::type + >::type + > + > +Ret mapError(const Result<T, E>& result, Func func) { + return err::Map<Func>::map(result, func); +} + +template<typename T, typename E, typename Func> +Result<T, E> then(const Result<T, E>& result, Func func) { + return And::Then<Func>::then(result, func); +} + +template<typename T, typename E, typename Func> +Result<T, E> otherwise(const Result<T, E>& result, Func func) { + return Other::Wise<Func>::otherwise(result, func); +} + +template<typename T, typename E, typename Func, + typename Ret = + Result<T, + typename details::ResultErrType< + typename details::result_of<Func>::type + >::type + > +> +Ret orElse(const Result<T, E>& result, Func func) { + return Or::Else<Func>::orElse(result, func); +} + +struct ok_tag { }; +struct err_tag { }; + +template<typename T, typename E> +struct Storage { + static constexpr size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E); + static constexpr size_t Align = sizeof(T) > sizeof(E) ? alignof(T) : alignof(E); + + typedef typename std::aligned_storage<Size, Align>::type type; + + Storage() + : initialized_(false) + { } + + void construct(types::Ok<T> ok) + { + new (&storage_) T(std::move(ok.val)); + initialized_ = true; + } + void construct(types::Err<E> err) + { + new (&storage_) E(err.val); + initialized_ = true; + } + + template<typename U> + void rawConstruct(U&& val) { + typedef typename std::decay<U>::type CleanU; + + new (&storage_) CleanU(std::forward<U>(val)); + initialized_ = true; + } + + template<typename U> + const U& get() const { + return *reinterpret_cast<const U *>(&storage_); + } + + template<typename U> + U& get() { + return *reinterpret_cast<U *>(&storage_); + } + + void destroy(ok_tag) { + if (initialized_) { + get<T>().~T(); + initialized_ = false; + } + } + + void destroy(err_tag) { + if (initialized_) { + get<E>().~E(); + initialized_ = false; + } + } + + type storage_; + bool initialized_; +}; + +template<typename E> +struct Storage<void, E> { + typedef typename std::aligned_storage<sizeof(E), alignof(E)>::type type; + + void construct(types::Ok<void>) + { + initialized_ = true; + } + + void construct(types::Err<E> err) + { + new (&storage_) E(err.val); + initialized_ = true; + } + + template<typename U> + void rawConstruct(U&& val) { + typedef typename std::decay<U>::type CleanU; + + new (&storage_) CleanU(std::forward<U>(val)); + initialized_ = true; + } + + void destroy(ok_tag) { initialized_ = false; } + void destroy(err_tag) { + if (initialized_) { + get<E>().~E(); initialized_ = false; + } + } + + template<typename U, + typename = std::enable_if_t<!std::is_same<U, void>::value>> + const U& get() const { + return *reinterpret_cast<const U *>(&storage_); + } + + template<typename U, + typename = std::enable_if_t<!std::is_same<U, void>::value>> + typename std::add_lvalue_reference<U>::type get() { + return *reinterpret_cast<U *>(&storage_); + } + + template<typename U, + typename = std::enable_if_t<std::is_same<U, void>::value>> + void get() {} + + type storage_; + bool initialized_; +}; + +template<typename T, typename E> +struct Constructor { + + static void move(Storage<T, E>&& src, Storage<T, E>& dst, ok_tag) { + dst.rawConstruct(std::move(src.template get<T>())); + src.destroy(ok_tag()); + } + + static void copy(const Storage<T, E>& src, Storage<T, E>& dst, ok_tag) { + dst.rawConstruct(src.template get<T>()); + } + + static void move(Storage<T, E>&& src, Storage<T, E>& dst, err_tag) { + dst.rawConstruct(std::move(src.template get<E>())); + src.destroy(err_tag()); + } + + static void copy(const Storage<T, E>& src, Storage<T, E>& dst, err_tag) { + dst.rawConstruct(src.template get<E>()); + } +}; + +template<typename E> +struct Constructor<void, E> { + static void move(Storage<void, E>&& src, Storage<void, E>& dst, ok_tag) { + } + + static void copy(const Storage<void, E>& src, Storage<void, E>& dst, ok_tag) { + } + + static void move(Storage<void, E>&& src, Storage<void, E>& dst, err_tag) { + dst.rawConstruct(std::move(src.template get<E>())); + src.destroy(err_tag()); + } + + static void copy(const Storage<void, E>& src, Storage<void, E>& dst, err_tag) { + dst.rawConstruct(src.template get<E>()); + } +}; + +} // namespace details + +namespace concept { + +template<typename T, typename = void> struct EqualityComparable : std::false_type { }; + +template<typename T> +struct EqualityComparable<T, +typename std::enable_if< + true, + typename details::void_t<decltype(std::declval<T>() == std::declval<T>())>::type + >::type +> : std::true_type +{ +}; + + +} // namespace concept + +template<typename T, typename E> +struct Result { + + static_assert(!std::is_same<E, void>::value, "void error type is not allowed"); + + typedef details::Storage<T, E> storage_type; + + Result(types::Ok<T> ok) + : ok_(true) + { + storage_.construct(std::move(ok)); + } + + Result(types::Err<E> err) + : ok_(false) + { + storage_.construct(std::move(err)); + } + + Result(Result&& other) { + if (other.isOk()) { + details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::ok_tag()); + ok_ = true; + } else { + details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::err_tag()); + ok_ = false; + } + } + + Result(const Result& other) { + if (other.isOk()) { + details::Constructor<T, E>::copy(other.storage_, storage_, details::ok_tag()); + ok_ = true; + } else { + details::Constructor<T, E>::copy(other.storage_, storage_, details::err_tag()); + ok_ = false; + } + } + + ~Result() { + if (ok_) + storage_.destroy(details::ok_tag()); + else + storage_.destroy(details::err_tag()); + } + + bool isOk() const { + return ok_; + } + + bool isErr() const { + return !ok_; + } + + T expect(const char* str) + { + if (!isOk()) { + ::fprintf(stderr, "%s\n", str); + abort(); + } + return expect_impl(std::is_same<T, void>()); + } + + template<typename Func> + auto map(Func func) + { + using return_type = decltype(func(T{})); + + if (this->isOk()) { + auto value = std::move(this->storage().template get<T>()); + auto res = func(std::move(value)); + return Result<return_type, E>( + types::Ok<return_type>(std::move(res))); + } + + return Result<return_type, E>( + types::Err<E>(this->storage().template get<E>())); + } + + template<typename Func, + typename Ret = + Result<T, + typename details::ResultErrType< + typename details::result_of<Func>::type + >::type + > + > + Ret mapError(Func func) const { + return details::mapError(*this, func); + } + + template<typename Func> + Result<void, E> then(Func func) { + if (this->isOk()) { + func(std::move(this->storage().template get<T>())); + + return Ok(); + } + + return Err(std::move(this->storage().template get<E>())); + } + + template<typename Func> + Result<typename std::result_of<Func>::type, E> then(Func func) { + if (this->isOk()) { + return Ok(func(std::move(this->storage().template get<T>()))); + } + + return Err(std::move(this->storage().template get<E>())); + } + + template<typename Func> + void otherwise(Func func) { + if (this->isOk()) { + return; + } + + func(std::move(this->storage().template get<E>())); + } + + template<typename Func, + typename Ret = + Result<T, + typename details::ResultErrType< + typename details::result_of<Func>::type + >::type + > + > + Ret orElse(Func func) const { + return details::orElse(*this, func); + } + + storage_type& storage() { + return storage_; + } + + const storage_type& storage() const { + return storage_; + } + + template<typename U = T> + typename std::enable_if< + !std::is_same<U, void>::value, + T + >::type + unwrapOr(const U& defaultValue) const { + if (isOk()) { + return storage().template get<T>(); + } + return defaultValue; + } + + template<typename Func> + auto unwrapOrElse(Func func) const { + if (isOk()) { + return storage().template get<T>(); + } + return func(this->storage().template get<E>()); + } + + template<typename U = T> + typename std::enable_if< + !std::is_same<U, void>::value, + U + >::type + unwrap() const { + if (isOk()) { + return std::move(storage().template get<U>()); + } + + ::fprintf(stderr, "Attempting to unwrap an error Result\n"); + abort(); + } + + template<typename U = T> + typename std::enable_if< + !std::is_same<U, void>::value, + U + >::type + unwrap() { + if (isOk()) { + return std::move(storage().template get<U>()); + } + + ::fprintf(stderr, "Attempting to unwrap an error Result\n"); + abort(); + } + + template<typename U = T> + typename std::enable_if<std::is_same<U, void>::value, U>::type unwrap() + const + { + if (isOk()) { + return; + } + + ::fprintf(stderr, "Attempting to unwrap an error Result\n"); + abort(); + } + + E unwrapErr() const + { + if (isErr()) { + return storage().template get<E>(); + } + + ::fprintf(stderr, "Attempting to unwrapErr an ok Result\n"); + abort(); + } + +private: + T expect_impl(std::true_type) const {} + T expect_impl(std::false_type) + { + return std::move(storage_.template get<T>()); + } + + bool ok_; + storage_type storage_; +}; + +template<typename T, typename E> +bool operator==(const Result<T, E>& lhs, const Result<T, E>& rhs) { + static_assert(concept::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable"); + static_assert(concept::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable"); + + if (lhs.isOk() && rhs.isOk()) { + return lhs.storage().template get<T>() == rhs.storage().template get<T>(); + } + if (lhs.isErr() && rhs.isErr()) { + return lhs.storage().template get<E>() == rhs.storage().template get<E>(); + } +} + +template<typename T, typename E> +bool operator==(const Result<T, E>& lhs, types::Ok<T> ok) { + static_assert(concept::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable"); + + if (!lhs.isOk()) return false; + + return lhs.storage().template get<T>() == ok.val; +} + +template<typename E> +bool operator==(const Result<void, E>& lhs, types::Ok<void>) { + return lhs.isOk(); +} + +template<typename T, typename E> +bool operator==(const Result<T, E>& lhs, types::Err<E> err) { + static_assert(concept::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable"); + if (!lhs.isErr()) return false; + + return lhs.storage().template get<E>() == err.val; +} + +#define TRY(...) \ + ({ \ + auto res = __VA_ARGS__; \ + if (!res.isOk()) { \ + typedef typename ::details::ResultErrType<decltype(res)>::type E; \ + return ::types::Err<E>(res.storage().template get<E>()); \ + } \ + res.unwrap(); \ + }) |