From 5055e6382ccd5df5bef8c8ecc6a2de3efa458633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fahller?= Date: Mon, 28 May 2018 13:09:43 +0200 Subject: [PATCH 1/2] or_else calls func with error state (#22) * or_else calls func with error state * Hopefully worked around C++11 issues with or_else * or_else with g++-4.8 not supporting const&& overloads --- tests/extensions.cpp | 189 +++++++++++++++++++++++++++++++++++++++++++ tl/expected.hpp | 59 +++++++++----- 2 files changed, 229 insertions(+), 19 deletions(-) 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 f8d2ec2..5052c81 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 From 12334cb13bbb1de89770bfc5819d438bc4fb1802 Mon Sep 17 00:00:00 2001 From: Simon Brand Date: Mon, 28 May 2018 12:06:10 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Thank=20Bj=C3=B6rn?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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. ----------