diff --git a/README.md b/README.md index 7a5dfbe..c15e6c1 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Requires [Catch](https://github.com/philsquared/Catch) for testing. This is bund ### Acknowledgements -Thanks to [Kévin Alexandre Boissonneault](https://github.com/KABoissonneault) for various bug fixes. +Thanks to [Kévin Alexandre Boissonneault](https://github.com/KABoissonneault) and [Björn Fahller](https://github.com/rollbear) for various bug fixes. ---------- diff --git a/tests/extensions.cpp b/tests/extensions.cpp index 12b21b5..8e1c77f 100644 --- a/tests/extensions.cpp +++ b/tests/extensions.cpp @@ -368,6 +368,195 @@ TEST_CASE("And then extensions", "[extensions.and_then]") { } } +TEST_CASE("or_else", "[extensions.or_else]") { + using eptr = std::unique_ptr; + auto succeed = [](int a) { return tl::expected(21 * 2); }; + auto succeedptr = [](eptr e) { return tl::expected(21*2);}; + auto fail = [](int a) { return tl::expected(tl::unexpect, 17);}; + auto efail = [](eptr e) { *e = 17;return tl::expected(tl::unexpect, std::move(e));}; + auto failptr = [](eptr e) { return tl::expected(tl::unexpect, std::move(e));}; + auto failvoid = [](int) {}; + auto failvoidptr = [](const eptr&) { /* don't consume */}; + auto consumeptr = [](eptr) {}; + auto make_u_int = [](int n) { return std::unique_ptr(new int(n));}; + + { + tl::expected e = 21; + auto ret = e.or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + const tl::expected e = 21; + auto ret = e.or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + tl::expected e = 21; + auto ret = std::move(e).or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + tl::expected e = 21; + auto ret = std::move(e).or_else(succeedptr); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + const tl::expected e = 21; + auto ret = std::move(e).or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + tl::expected e = 21; + auto ret = e.or_else(fail); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + const tl::expected e = 21; + auto ret = e.or_else(fail); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + tl::expected e = 21; + auto ret = std::move(e).or_else(fail); + REQUIRE(ret); + REQUIRE(ret == 21); + } + + + { + tl::expected e = 21; + auto ret = std::move(e).or_else(efail); + REQUIRE(ret); + REQUIRE(ret == 21); + } + + { + const tl::expected e = 21; + auto ret = std::move(e).or_else(fail); + REQUIRE(ret); + REQUIRE(*ret == 21); + } + + { + tl::expected e(tl::unexpect, 21); + auto ret = e.or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 42); + } + + { + const tl::expected e(tl::unexpect, 21); + auto ret = e.or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 42); + } + + { + tl::expected e(tl::unexpect, 21); + auto ret = std::move(e).or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 42); + } + + { + tl::expected e(tl::unexpect, make_u_int(21)); + auto ret = std::move(e).or_else(succeedptr); + REQUIRE(ret); + REQUIRE(*ret == 42); + } + + { + const tl::expected e(tl::unexpect, 21); + auto ret = std::move(e).or_else(succeed); + REQUIRE(ret); + REQUIRE(*ret == 42); + } + + { + tl::expected e(tl::unexpect, 21); + auto ret = e.or_else(fail); + REQUIRE(!ret); + REQUIRE(ret.error() == 17); + } + + { + tl::expected e(tl::unexpect, 21); + auto ret = e.or_else(failvoid); + REQUIRE(!ret); + REQUIRE(ret.error() == 21); + } + + { + const tl::expected e(tl::unexpect, 21); + auto ret = e.or_else(fail); + REQUIRE(!ret); + REQUIRE(ret.error() == 17); + } + + { + const tl::expected e(tl::unexpect, 21); + auto ret = e.or_else(failvoid); + REQUIRE(!ret); + REQUIRE(ret.error() == 21); + } + + { + tl::expected e(tl::unexpect, 21); + auto ret = std::move(e).or_else(fail); + REQUIRE(!ret); + REQUIRE(ret.error() == 17); + } + + { + tl::expected e(tl::unexpect, 21); + auto ret = std::move(e).or_else(failvoid); + REQUIRE(!ret); + REQUIRE(ret.error() == 21); + } + + { + tl::expected e(tl::unexpect, make_u_int(21)); + auto ret = std::move(e).or_else(failvoidptr); + REQUIRE(!ret); + REQUIRE(*ret.error() == 21); + } + + { + tl::expected e(tl::unexpect, make_u_int(21)); + auto ret = std::move(e).or_else(consumeptr); + REQUIRE(!ret); + REQUIRE(ret.error() == nullptr); + } + + { + const tl::expected e(tl::unexpect, 21); + auto ret = std::move(e).or_else(fail); + REQUIRE(!ret); + REQUIRE(ret.error() == 17); + } + + { + const tl::expected e(tl::unexpect, 21); + auto ret = std::move(e).or_else(failvoid); + REQUIRE(!ret); + REQUIRE(ret.error() == 21); + } + +} struct S { int x; }; diff --git a/tl/expected.hpp b/tl/expected.hpp index bee8bd7..7db3445 100644 --- a/tl/expected.hpp +++ b/tl/expected.hpp @@ -1369,11 +1369,11 @@ public: #endif /// \brief Calls `f` if the expectd is in the unexpected state - /// \requires `std::invoke_result_t` must be void or convertible to - /// `expcted`. + /// \requires `F` is invokable with `E`, and `std::invoke_result_t` + /// must be void or convertible to `expcted`. /// \effects If `*this` has a value, returns `*this`. - /// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns - /// `std::nullopt`. Otherwise, returns `std::forward(f)()`. + /// Otherwise, if `f` returns `void`, calls `std::forward(f)(E)` and returns + /// `std::nullopt`. Otherwise, returns `std::forward(f)(E)`. /// /// \group or_else template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { @@ -1388,10 +1388,11 @@ public: 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; @@ -2009,31 +2010,51 @@ auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { } #endif +#ifdef TL_EXPECTED_CXX14 template (), - *std::declval())), + std::declval().error())), detail::enable_if_t::value> * = nullptr> -constexpr detail::decay_t or_else_impl(Exp &&exp, F &&f) { - if (exp.has_value()) { - return std::forward(exp); - } - - return detail::invoke(std::forward(f), *std::forward(exp)); +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())), + std::declval().error())), detail::enable_if_t::value> * = nullptr> detail::decay_t or_else_impl(Exp &&exp, F &&f) { - if (exp.has_value()) { - return std::forward(exp); - } - - detail::invoke(std::forward(f), *std::forward(exp)); - return std::forward(exp); + 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