diff --git a/include/tl/expected.hpp b/include/tl/expected.hpp index 31b130a..b52a501 100644 --- a/include/tl/expected.hpp +++ b/include/tl/expected.hpp @@ -1203,6 +1203,237 @@ template class bad_expected_access : public std::exception { E m_val; }; +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; + +template >::value> * = nullptr, + class Ret = detail::decay_t(), + *std::declval()))>, + detail::enable_if_t::value && + std::is_convertible().error()), + err_t>::value> * = nullptr> +constexpr Ret and_then_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = detail::decay_t()))>, + detail::enable_if_t::value && + std::is_convertible().error()), + err_t>::value> * = nullptr> +constexpr Ret and_then_impl(Exp &&exp, F &&f) { + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = detail::decay_t(), *std::declval()))>, + detail::enable_if_t::value> * = nullptr, + class Result = expected>> +constexpr Result map_impl(Exp &&exp, F &&f) { + return exp.has_value() ? Result(detail::invoke(std::forward(f), *std::forward(exp))) + : Result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = detail::decay_t(), *std::declval()))>, + detail::enable_if_t::value> * = nullptr, + class Result = expected>> +constexpr Result map_impl(Exp &&exp, F &&f) { + return exp.has_value() ? (detail::invoke(std::forward(f), *std::forward(exp)), Result()) + : Result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = detail::decay_t()))>, + detail::enable_if_t::value> * = nullptr, + class Result = expected>> +constexpr Result map_impl(Exp &&exp, F &&f) { + return exp.has_value() ? Result(detail::invoke(std::forward(f))) + : Result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = detail::decay_t()))>, + detail::enable_if_t::value> * = nullptr, + class Result = expected>> +constexpr Result map_impl(Exp &&exp, F &&f) { + return exp.has_value() ? (detail::invoke(std::forward(f)), Result()) + : Result(unexpect, std::forward(exp).error()); +} + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() + ? std::forward(exp) + : detail::invoke(std::forward(f), std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? std::forward(exp) + : (detail::invoke(std::forward(f), std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() + ? std::forward(exp) + : detail::invoke(std::forward(f), std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? std::forward(exp) + : (detail::invoke(std::forward(f), std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + /// An `expected` object is an object that contains the storage for /// another object and manages the lifetime of this contained object `T`. /// Alternatively it could contain the storage for another unexpected object @@ -1251,198 +1482,121 @@ class expected : private detail::expected_move_assign_base, typedef E error_type; typedef unexpected unexpected_type; -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { - return and_then_impl(*this, std::forward(f)); + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & + -> decltype(detail::and_then_impl(*this, std::forward(f))) { + return detail::and_then_impl(*this, std::forward(f)); } - template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { - return and_then_impl(std::move(*this), std::forward(f)); + template + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && + -> decltype(detail::and_then_impl(std::move(*this), std::forward(f))) { + return detail::and_then_impl(std::move(*this), std::forward(f)); } - template constexpr auto and_then(F &&f) const & { - return and_then_impl(*this, std::forward(f)); + template + constexpr auto and_then(F &&f) const & + -> decltype(detail::and_then_impl(*this, std::forward(f))) { + return detail::and_then_impl(*this, std::forward(f)); } - #ifndef TL_EXPECTED_NO_CONSTRR - template constexpr auto and_then(F &&f) const && { - return and_then_impl(std::move(*this), std::forward(f)); + template + constexpr auto and_then(F &&f) const && + -> decltype(detail::and_then_impl(std::move(*this), std::forward(f))) { + return detail::and_then_impl(std::move(*this), std::forward(f)); } #endif -#else template - TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) & -> decltype(and_then_impl(std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & + -> decltype(detail::map_impl(*this, std::forward(f))) { + return detail::map_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype( - and_then_impl(std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); + TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && + -> decltype(detail::map_impl(std::move(*this), std::forward(f))) { + return detail::map_impl(std::move(*this), std::forward(f)); } template - constexpr auto and_then(F &&f) const & -> decltype( - and_then_impl(std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); + constexpr auto map(F &&f) const & + -> decltype(detail::map_impl(*this, std::forward(f))) { + return detail::map_impl(*this, std::forward(f)); } - #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr auto and_then(F &&f) const && -> decltype( - and_then_impl(std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); + constexpr auto map(F &&f) const && + -> decltype(detail::map_impl(std::move(*this), std::forward(f))) { + return detail::map_impl(std::move(*this), std::forward(f)); } #endif -#endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template constexpr auto map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template constexpr auto map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) - map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & + -> decltype(detail::map_impl(*this, std::forward(f))) { + return detail::map_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) - map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); + TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && + -> decltype(detail::map_impl(std::move(*this), std::forward(f))) { + return detail::map_impl(std::move(*this), std::forward(f)); } template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); + constexpr auto transform(F &&f) const & + -> decltype(detail::map_impl(*this, std::forward(f))) { + return detail::map_impl(*this, std::forward(f)); } - #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); + constexpr auto transform(F &&f) const && + -> decltype(detail::map_impl(std::move(*this), std::forward(f))) { + return detail::map_impl(std::move(*this), std::forward(f)); } #endif -#endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template constexpr auto transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template constexpr auto transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) - transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); + template + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & + -> decltype(detail::map_error_impl(*this, std::forward(f))) { + return detail::map_error_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR decltype( - expected_map_impl(std::declval(), std::declval())) - transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); + TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && + -> decltype(detail::map_error_impl(std::move(*this), std::forward(f))) { + return detail::map_error_impl(std::move(*this), std::forward(f)); } template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); + constexpr auto map_error(F &&f) const & + -> decltype(detail::map_error_impl(*this, std::forward(f))) { + return detail::map_error_impl(*this, std::forward(f)); } - #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); + constexpr auto map_error(F &&f) const && + -> decltype(detail::map_error_impl(std::move(*this), std::forward(f))) { + return detail::map_error_impl(std::move(*this), std::forward(f)); } #endif -#endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template constexpr auto map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - template constexpr auto map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#else template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); + TL_EXPECTED_11_CONSTEXPR auto or_else(F &&f) & + -> decltype(detail::or_else_impl(*this, std::forward(f))) { + return detail::or_else_impl(*this, std::forward(f)); } template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); + TL_EXPECTED_11_CONSTEXPR auto or_else(F &&f) && + -> decltype(detail::or_else_impl(std::move(*this), std::forward(f))) { + return detail::or_else_impl(std::move(*this), std::forward(f)); } template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); + constexpr auto or_else(F &&f) const & + -> decltype(detail::or_else_impl(*this, std::forward(f))) { + return detail::or_else_impl(*this, std::forward(f)); } - #ifndef TL_EXPECTED_NO_CONSTRR template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); + constexpr auto or_else(F &&f) const && + -> decltype(detail::or_else_impl(std::move(*this), std::forward(f))) { + return detail::or_else_impl(std::move(*this), std::forward(f)); } #endif -#endif - template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { - return or_else_impl(*this, std::forward(f)); - } - - template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { - return or_else_impl(std::move(*this), std::forward(f)); - } - template expected constexpr or_else(F &&f) const & { - return or_else_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template expected constexpr or_else(F &&f) const && { - return or_else_impl(std::move(*this), std::forward(f)); - } -#endif constexpr expected() = default; constexpr expected(const expected &rhs) = default; constexpr expected(expected &&rhs) = default; @@ -1937,331 +2091,6 @@ class expected : private detail::expected_move_assign_base, } }; -namespace detail { -template using exp_t = typename detail::decay_t::value_type; -template using err_t = typename detail::decay_t::error_type; -template using ret_t = expected>; - -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#else -template struct TC; -template (), - *std::declval())), - detail::enable_if_t>::value> * = nullptr> -auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} - -template ())), - detail::enable_if_t>::value> * = nullptr> -constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#endif - -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return result(); - } - - return result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return result(); - } - - return result(unexpect, std::forward(exp).error()); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; - - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return {}; - } - - return unexpected>(std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> - -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; - - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> - -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return {}; - } - - return unexpected>(std::forward(exp).error()); -} -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; - - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; - - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#endif - -#ifdef TL_EXPECTED_CXX14 -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto or_else_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); -} - -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), - std::forward(exp)); -} -#else -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto or_else_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() - ? std::forward(exp) - : detail::invoke(std::forward(f), std::forward(exp).error()); -} - -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() - ? std::forward(exp) - : (detail::invoke(std::forward(f), std::forward(exp).error()), - std::forward(exp)); -} -#endif -} // namespace detail - template constexpr bool operator==(const expected &lhs, const expected &rhs) { diff --git a/tests/extensions.cpp b/tests/extensions.cpp index 9670f90..527a978 100644 --- a/tests/extensions.cpp +++ b/tests/extensions.cpp @@ -7,6 +7,86 @@ constexpr bool TOKENPASTE2(rqure, __LINE__) = e; \ REQUIRE(e); +template +constexpr auto hasMap_impl(int, Exp&& e, F&& f) + -> decltype(std::forward(e).map(std::forward(f)), true) { + return true; +} + +template +constexpr bool hasMap_impl(long, Exp&&, F&&) { return false; } + +template +constexpr bool hasMap(Exp&& e, F&& f) { + return hasMap_impl(42, std::forward(e), std::forward(f)); +} + +TEST_CASE("map is SFINAE-friendly", "[extensions.map.sfinae]") { + auto mul2 = [](int a) { return a * 2; }; + auto ret_void = [](int a) {}; + + { + tl::expected e = 21; + STATIC_REQUIRE(hasMap(e, mul2)); + STATIC_REQUIRE(hasMap(std::move(e), mul2)); + STATIC_REQUIRE(hasMap(e, std::move(mul2))); + STATIC_REQUIRE(hasMap(std::move(e), std::move(mul2))); + } + { + const tl::expected ce = 21; + STATIC_REQUIRE(hasMap(ce, mul2)); + STATIC_REQUIRE(hasMap(std::move(ce), mul2)); + STATIC_REQUIRE(hasMap(ce, std::move(mul2))); + STATIC_REQUIRE(hasMap(std::move(ce), std::move(mul2))); + } + { + tl::expected ev; + tl::expected ei = 21; + + STATIC_REQUIRE(!hasMap(ev, 42)); + STATIC_REQUIRE(!hasMap(ei, 42)); + + STATIC_REQUIRE(!hasMap(ev, mul2)); + STATIC_REQUIRE(hasMap(ei, mul2)); + + auto wrongParamType = [](char *) { return 21; }; + STATIC_REQUIRE(!hasMap(ev, wrongParamType)); + STATIC_REQUIRE(!hasMap(ei, wrongParamType)); + + auto voidParamType = []() { return 21; }; + STATIC_REQUIRE(hasMap(ev, voidParamType)); + STATIC_REQUIRE(!hasMap(ei, voidParamType)); + + auto voidReturnVoidParam = []() {}; + STATIC_REQUIRE(hasMap(ev, voidReturnVoidParam)); + STATIC_REQUIRE(!hasMap(ei, voidReturnVoidParam)); + + auto voidReturnIntParam = [](int) {}; + STATIC_REQUIRE(!hasMap(ev, voidReturnIntParam)); + STATIC_REQUIRE(hasMap(ei, voidReturnIntParam)); + + auto voidReturnWrongParam = [](char *) {}; + STATIC_REQUIRE(!hasMap(ev, voidReturnWrongParam)); + STATIC_REQUIRE(!hasMap(ei, voidReturnWrongParam)); + } + { + tl::expected e = 21; + const tl::expected ce = 21; + + auto lvalueParam = [](int&) { return 21; }; + STATIC_REQUIRE(hasMap(e, lvalueParam)); + STATIC_REQUIRE(!hasMap(std::move(e), lvalueParam)); + STATIC_REQUIRE(!hasMap(ce, lvalueParam)); + STATIC_REQUIRE(!hasMap(std::move(ce), lvalueParam)); + + auto rvalueParam = [](int&&) { return 21; }; + STATIC_REQUIRE(!hasMap(e, rvalueParam)); + STATIC_REQUIRE(hasMap(std::move(e), rvalueParam)); + STATIC_REQUIRE(!hasMap(ce, rvalueParam)); + STATIC_REQUIRE(!hasMap(std::move(ce), rvalueParam)); + } +} + TEST_CASE("Map extensions", "[extensions.map]") { auto mul2 = [](int a) { return a * 2; }; auto ret_void = [](int a) {}; @@ -141,6 +221,43 @@ TEST_CASE("Map extensions", "[extensions.map]") { } } +TEST_CASE("Map on expected", "[extensions.map.void]") { + { + tl::expected a; + REQUIRE(a.has_value()); + bool called = false; + tl::expected b = a.map([&](){ called = true; }); + REQUIRE(called); + REQUIRE(b.has_value()); + } + { + tl::expected a = tl::make_unexpected(42); + REQUIRE(!a.has_value()); + bool called = false; + tl::expected b = a.map([&](){ called = true; }); + REQUIRE(!called); + REQUIRE(!b.has_value()); + REQUIRE(b.error() == 42); + } + { + tl::expected a; + REQUIRE(a.has_value()); + bool called = false; + tl::expected b = a.map([&](){ called = true; return 3; }); + REQUIRE(called); + REQUIRE(b.value() == 3); + } + { + tl::expected a = tl::make_unexpected(42); + REQUIRE(!a.has_value()); + bool called = false; + tl::expected b = a.map([&](){ called = true; return 3; }); + REQUIRE(!called); + REQUIRE(!b.has_value()); + REQUIRE(b.error() == 42); + } +} + TEST_CASE("Map error extensions", "[extensions.map_error]") { auto mul2 = [](int a) { return a * 2; }; auto ret_void = [](int a) {}; @@ -251,6 +368,77 @@ TEST_CASE("Map error extensions", "[extensions.map_error]") { } +template +constexpr auto hasAndThen_impl(int, Exp&& e, F&& f) + -> decltype(std::forward(e).and_then(std::forward(f)), true) { + return true; +} + +template +constexpr bool hasAndThen_impl(long, Exp&&, F&&) { return false; } + +template +constexpr bool hasAndThen(Exp&& e, F&& f) { + return hasAndThen_impl(42, std::forward(e), std::forward(f)); +} + +TEST_CASE("and_then is SFINAE-friendly", "[extensions.and_then.sfinae]") { + auto succeed = [](int) { return tl::expected(21 * 2); }; + + { + tl::expected e = 21; + STATIC_REQUIRE(hasAndThen(e, succeed)); + STATIC_REQUIRE(hasAndThen(std::move(e), succeed)); + STATIC_REQUIRE(hasAndThen(e, std::move(succeed))); + STATIC_REQUIRE(hasAndThen(std::move(e), std::move(succeed))); + } + { + const tl::expected ce = 21; + STATIC_REQUIRE(hasAndThen(ce, succeed)); + STATIC_REQUIRE(hasAndThen(std::move(ce), succeed)); + STATIC_REQUIRE(hasAndThen(ce, std::move(succeed))); + STATIC_REQUIRE(hasAndThen(std::move(ce), std::move(succeed))); + } + { + tl::expected e = 21; + STATIC_REQUIRE(!hasAndThen(e, 42)); + + auto wrongParamType = [](char *) { return tl::expected(); }; + STATIC_REQUIRE(!hasAndThen(e, wrongParamType)); + + auto wrongParamType2 = []() { return tl::expected(); }; + STATIC_REQUIRE(!hasAndThen(e, wrongParamType2)); + + auto wrongReturnType = [](int) { return 21; }; + STATIC_REQUIRE(!hasAndThen(e, wrongReturnType)); + + auto wrongReturnType2 = [](int) { return tl::expected(); }; + STATIC_REQUIRE(!hasAndThen(e, wrongReturnType2)); + + auto voidReturnType = [](int) {}; + STATIC_REQUIRE(!hasAndThen(e, voidReturnType)); + + auto voidReturnWrongParam = [](char *) {}; + STATIC_REQUIRE(!hasAndThen(e, voidReturnWrongParam)); + } + { + tl::expected e = 21; + const tl::expected ce = 21; + + auto lvalueParam = [](int&) { return tl::expected(); }; + STATIC_REQUIRE(hasAndThen(e, lvalueParam)); + STATIC_REQUIRE(!hasAndThen(std::move(e), lvalueParam)); + STATIC_REQUIRE(!hasAndThen(ce, lvalueParam)); + STATIC_REQUIRE(!hasAndThen(std::move(ce), lvalueParam)); + + auto rvalueParam = [](int&&) { return tl::expected(); }; + STATIC_REQUIRE(!hasAndThen(e, rvalueParam)); + STATIC_REQUIRE(hasAndThen(std::move(e), rvalueParam)); + STATIC_REQUIRE(!hasAndThen(ce, rvalueParam)); + STATIC_REQUIRE(!hasAndThen(std::move(ce), rvalueParam)); + } +} + TEST_CASE("And then extensions", "[extensions.and_then]") { auto succeed = [](int a) { return tl::expected(21 * 2); }; auto fail = [](int a) { return tl::expected(tl::unexpect, 17); };