diff --git a/optional.hpp b/optional.hpp index f90b3c9..493eb2e 100644 --- a/optional.hpp +++ b/optional.hpp @@ -113,24 +113,24 @@ using invoke_result_t = typename invoke_result::type; template using fixup_void = conditional_t::value, monostate, U>; -template struct get_invoke_optional_ret { +template struct get_invoke_optional_ret { using type = invoke_result_t< conditional_t::value, typename remove_reference_t::value_type &, typename remove_reference_t::value_type &&>, - U>; + U...>; }; -template -using get_invoke_ret = - typename conditional_t::value, get_invoke_optional_ret, - invoke_result>::type; +template +using get_invoke_ret = typename conditional_t::value, + get_invoke_optional_ret, + invoke_result>::type; template using get_map_return = optional>>; -template -using returns_void = std::is_void>; +template +using returns_void = std::is_void>; template using disable_if_optional = enable_if_t::value>; @@ -138,11 +138,11 @@ using disable_if_optional = enable_if_t::value>; template using enable_if_optional = enable_if_t::value>; -template -using enable_if_ret_void = enable_if_t::value>; +template +using enable_if_ret_void = enable_if_t::value>; -template -using disable_if_ret_void = enable_if_t::value>; +template +using disable_if_ret_void = enable_if_t::value>; } struct in_place_t { @@ -766,7 +766,7 @@ public: } template - TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t bind(F &&f) & { + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -776,7 +776,7 @@ public: } template - TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t bind(F &&f) && { + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -786,7 +786,7 @@ public: } template - constexpr detail::invoke_result_t bind(F &&f) const & { + constexpr detail::invoke_result_t and_then(F &&f) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -796,7 +796,7 @@ public: } template - constexpr detail::invoke_result_t bind(F &&f) const && { + constexpr detail::invoke_result_t and_then(F &&f) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -805,69 +805,6 @@ public: : result(nullopt); } - template - detail::invoke_result_t bind(F &&f, E &&e) & - noexcept(noexcept(detail::invoke(std::forward(f), - std::declval())) && - noexcept(std::forward(e)())) { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - if (!has_value()) - return result(nullopt); - - auto ret = detail::invoke(std::forward(f), **this); - if (!ret) - detail::invoke(e); - return ret; - } - - template - detail::invoke_result_t bind(F &&f, E &&e) && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - if (!has_value()) - return result(nullopt); - - auto ret = detail::invoke(std::forward(f), std::move(**this)); - if (!ret) - detail::invoke(e); - return ret; - } - - template - detail::invoke_result_t bind(F &&f, E &&e) const & { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - if (!has_value()) - return result(nullopt); - - auto ret = detail::invoke(std::forward(f), **this); - if (!ret) - detail::invoke(e); - return ret; - } - - template - detail::invoke_result_t bind(F &&f, E &&e) const && { - using result = detail::invoke_result_t; - static_assert(detail::is_optional::value, - "F must return an optional"); - - if (!has_value()) - return result(nullopt); - - auto ret = detail::invoke(std::forward(f), std::move(**this)); - if (!ret) - detail::invoke(e); - return ret; - } - template * = nullptr, detail::disable_if_ret_void * = nullptr> detail::get_map_return map(F &&f) & @@ -1019,6 +956,62 @@ public: return monostate{}; } + template * = nullptr> + optional constexpr or_else(F &&f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + template * = nullptr> + optional constexpr or_else(F &&f) & { + return has_value() ? *this : std::forward(f)(); + } + + template * = nullptr> + optional or_else(F &&f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + template * = nullptr> + optional constexpr or_else(F &&f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + template * = nullptr> + optional or_else(F &&f) const & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + template * = nullptr> + optional constexpr or_else(F &&f) const & { + return has_value() ? *this : std::forward(f)(); + } + + template * = nullptr> + optional or_else(F &&f) const && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + template * = nullptr> + optional or_else(F &&f) const && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + template U map_or(F &&f, U &&u) & { return has_value() ? detail::invoke(std::forward(f), **this) : std::forward(u); diff --git a/tests/monadic.cpp b/tests/monadic.cpp index 7936a87..8e51133 100644 --- a/tests/monadic.cpp +++ b/tests/monadic.cpp @@ -7,10 +7,9 @@ constexpr bool TOKENPASTE2(rqure, __LINE__) = e; \ REQUIRE(e); - constexpr int get_int(int) { return 42; } +constexpr int get_int(int) { return 42; } constexpr tl::optional get_opt_int(int) { return 42; } - // What is Clang Format up to?! TEST_CASE("Monadic operations", "[monadic]"){SECTION("map"){// lhs is empty @@ -26,7 +25,7 @@ STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o2r.value() == 42); struct rval_call_map { - double operator()(int) && { return 42.0; }; + double operator()(int) && { return 42.0; }; }; // ensure that function object is forwarded @@ -58,340 +57,285 @@ REQUIRE(o6r.value() == 42); tl::optional o7 = 40; auto f7 = tl::make_optional([](const int &i) { return; }); auto o7r = o7.map(f7); -STATIC_REQUIRE((std::is_same>::value)); +STATIC_REQUIRE( + (std::is_same>::value)); REQUIRE(o6r.has_value()); // test each overload in turn tl::optional o8 = 42; -auto o8r = o8.map([](int){return 42;}); +auto o8r = o8.map([](int) { return 42; }); REQUIRE(*o8r == 42); tl::optional o9 = 42; -auto o9r = o9.map([](int){return;}); +auto o9r = o9.map([](int) { return; }); REQUIRE(o9r); tl::optional o10 = 42; -auto o10r = o10.map(tl::make_optional([](int){return 42;})); +auto o10r = o10.map(tl::make_optional([](int) { return 42; })); REQUIRE(*o10r == 42); tl::optional o11 = 42; -auto o11r = o11.map(tl::make_optional([](int){return;})); +auto o11r = o11.map(tl::make_optional([](int) { return; })); REQUIRE(o11r); tl::optional o12 = 42; -auto o12r = std::move(o12).map([](int){return 42;}); +auto o12r = std::move(o12).map([](int) { return 42; }); REQUIRE(*o12r == 42); tl::optional o13 = 42; -auto o13r = std::move(o13).map([](int){return;}); +auto o13r = std::move(o13).map([](int) { return; }); REQUIRE(o13r); tl::optional o14 = 42; -auto o14r = std::move(o14).map(tl::make_optional([](int){return 42;})); +auto o14r = std::move(o14).map(tl::make_optional([](int) { return 42; })); REQUIRE(*o14r == 42); tl::optional o15 = 42; -auto o15r = std::move(o15).map(tl::make_optional([](int){return;})); +auto o15r = std::move(o15).map(tl::make_optional([](int) { return; })); REQUIRE(o15r); const tl::optional o16 = 42; -auto o16r = o16.map([](int){return 42;}); +auto o16r = o16.map([](int) { return 42; }); REQUIRE(*o16r == 42); const tl::optional o17 = 42; -auto o17r = o17.map([](int){return;}); +auto o17r = o17.map([](int) { return; }); REQUIRE(o17r); const tl::optional o18 = 42; -auto o18r = o18.map(tl::make_optional([](int){return 42;})); +auto o18r = o18.map(tl::make_optional([](int) { return 42; })); REQUIRE(*o18r == 42); const tl::optional o19 = 42; -auto o19r = o19.map(tl::make_optional([](int){return;})); +auto o19r = o19.map(tl::make_optional([](int) { return; })); REQUIRE(o19r); const tl::optional o20 = 42; -auto o20r = std::move(o20).map([](int){return 42;}); +auto o20r = std::move(o20).map([](int) { return 42; }); REQUIRE(*o20r == 42); const tl::optional o21 = 42; -auto o21r = std::move(o21).map([](int){return;}); +auto o21r = std::move(o21).map([](int) { return; }); REQUIRE(o21r); const tl::optional o22 = 42; -auto o22r = std::move(o22).map(tl::make_optional([](int){return 42;})); +auto o22r = std::move(o22).map(tl::make_optional([](int) { return 42; })); REQUIRE(*o22r == 42); const tl::optional o23 = 42; -auto o23r = std::move(o23).map(tl::make_optional([](int){return;})); +auto o23r = std::move(o23).map(tl::make_optional([](int) { return; })); REQUIRE(o23r); tl::optional o24 = tl::nullopt; -auto o24r = o24.map([](int){return 42;}); +auto o24r = o24.map([](int) { return 42; }); REQUIRE(!o24r); tl::optional o25 = tl::nullopt; -auto o25r = o25.map([](int){return;}); +auto o25r = o25.map([](int) { return; }); REQUIRE(!o25r); tl::optional o26 = tl::nullopt; -auto o26r = o26.map(tl::make_optional([](int){return 42;})); +auto o26r = o26.map(tl::make_optional([](int) { return 42; })); REQUIRE(!o26r); tl::optional o27 = tl::nullopt; -auto o27r = o27.map(tl::make_optional([](int){return;})); +auto o27r = o27.map(tl::make_optional([](int) { return; })); REQUIRE(!o27r); tl::optional o28 = tl::nullopt; -auto o28r = std::move(o28).map([](int){return 42;}); +auto o28r = std::move(o28).map([](int) { return 42; }); REQUIRE(!o28r); tl::optional o29 = tl::nullopt; -auto o29r = std::move(o29).map([](int){return;}); +auto o29r = std::move(o29).map([](int) { return; }); REQUIRE(!o29r); tl::optional o30 = tl::nullopt; -auto o30r = std::move(o30).map(tl::make_optional([](int){return 42;})); +auto o30r = std::move(o30).map(tl::make_optional([](int) { return 42; })); REQUIRE(!o30r); tl::optional o31 = tl::nullopt; -auto o31r = std::move(o31).map(tl::make_optional([](int){return;})); +auto o31r = std::move(o31).map(tl::make_optional([](int) { return; })); REQUIRE(!o31r); const tl::optional o32 = tl::nullopt; -auto o32r = o32.map([](int){return 42;}); +auto o32r = o32.map([](int) { return 42; }); REQUIRE(!o32r); const tl::optional o33 = tl::nullopt; -auto o33r = o33.map([](int){return;}); +auto o33r = o33.map([](int) { return; }); REQUIRE(!o33r); const tl::optional o34 = tl::nullopt; -auto o34r = o34.map(tl::make_optional([](int){return 42;})); +auto o34r = o34.map(tl::make_optional([](int) { return 42; })); REQUIRE(!o34r); const tl::optional o35 = tl::nullopt; -auto o35r = o35.map(tl::make_optional([](int){return;})); +auto o35r = o35.map(tl::make_optional([](int) { return; })); REQUIRE(!o35r); const tl::optional o36 = tl::nullopt; -auto o36r = std::move(o36).map([](int){return 42;}); +auto o36r = std::move(o36).map([](int) { return 42; }); REQUIRE(!o36r); const tl::optional o37 = tl::nullopt; -auto o37r = std::move(o37).map([](int){return;}); +auto o37r = std::move(o37).map([](int) { return; }); REQUIRE(!o37r); const tl::optional o38 = tl::nullopt; -auto o38r = std::move(o38).map(tl::make_optional([](int){return 42;})); +auto o38r = std::move(o38).map(tl::make_optional([](int) { return 42; })); REQUIRE(!o38r); const tl::optional o39 = tl::nullopt; -auto o39r = std::move(o39).map(tl::make_optional([](int){return;})); +auto o39r = std::move(o39).map(tl::make_optional([](int) { return; })); REQUIRE(!o39r); } - SECTION("map constexpr") { -// test each overload in turn -constexpr tl::optional o16 = 42; -constexpr auto o16r = o16.map(get_int); -STATIC_REQUIRE(*o16r == 42); + // test each overload in turn + constexpr tl::optional o16 = 42; + constexpr auto o16r = o16.map(get_int); + STATIC_REQUIRE(*o16r == 42); -constexpr tl::optional o18 = 42; -constexpr auto opt_int = tl::make_optional(get_int); -constexpr auto o18r = o18.map(opt_int); -STATIC_REQUIRE(*o18r == 42); + constexpr tl::optional o18 = 42; + constexpr auto opt_int = tl::make_optional(get_int); + constexpr auto o18r = o18.map(opt_int); + STATIC_REQUIRE(*o18r == 42); -constexpr tl::optional o20 = 42; -constexpr auto o20r = std::move(o20).map(get_int); -STATIC_REQUIRE(*o20r == 42); + constexpr tl::optional o20 = 42; + constexpr auto o20r = std::move(o20).map(get_int); + STATIC_REQUIRE(*o20r == 42); -constexpr tl::optional o22 = 42; -constexpr auto o22r = std::move(o22).map(opt_int); -STATIC_REQUIRE(*o22r == 42); + constexpr tl::optional o22 = 42; + constexpr auto o22r = std::move(o22).map(opt_int); + STATIC_REQUIRE(*o22r == 42); -constexpr tl::optional o32 = tl::nullopt; -constexpr auto o32r = o32.map(get_int); -STATIC_REQUIRE(!o32r); + constexpr tl::optional o32 = tl::nullopt; + constexpr auto o32r = o32.map(get_int); + STATIC_REQUIRE(!o32r); -constexpr tl::optional o34 = tl::nullopt; -constexpr auto o34r = o34.map(opt_int); -STATIC_REQUIRE(!o34r); + constexpr tl::optional o34 = tl::nullopt; + constexpr auto o34r = o34.map(opt_int); + STATIC_REQUIRE(!o34r); -constexpr tl::optional o36 = tl::nullopt; -constexpr auto o36r = std::move(o36).map(get_int); -STATIC_REQUIRE(!o36r); + constexpr tl::optional o36 = tl::nullopt; + constexpr auto o36r = std::move(o36).map(get_int); + STATIC_REQUIRE(!o36r); -constexpr tl::optional o38 = tl::nullopt; -constexpr auto o38r = std::move(o38).map(opt_int); -STATIC_REQUIRE(!o38r); - } + constexpr tl::optional o38 = tl::nullopt; + constexpr auto o38r = std::move(o38).map(opt_int); + STATIC_REQUIRE(!o38r); +} -SECTION("bind") { +SECTION("and_then") { // lhs is empty tl::optional o1; - auto o1r = o1.bind([](int i) { return tl::optional{42}; }); + auto o1r = o1.and_then([](int i) { return tl::optional{42}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o1r); // lhs has value tl::optional o2 = 12; - auto o2r = o2.bind([](int i) { return tl::optional{42}; }); + auto o2r = o2.and_then([](int i) { return tl::optional{42}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o2r.value() == 42.f); // lhs is empty, rhs returns empty tl::optional o3; - auto o3r = o3.bind([](int i) { return tl::optional{}; }); + auto o3r = o3.and_then([](int i) { return tl::optional{}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o3r); // rhs returns empty tl::optional o4 = 12; - auto o4r = o4.bind([](int i) { return tl::optional{}; }); + auto o4r = o4.and_then([](int i) { return tl::optional{}; }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(!o4r); - struct rval_call_bind { - tl::optional operator()(int) && { return tl::optional(42.0); }; + struct rval_call_and_then { + tl::optional operator()(int) && { + return tl::optional(42.0); + }; }; // ensure that function object is forwarded tl::optional o5 = 42; - auto o5r = o5.bind(rval_call_bind{}); + auto o5r = o5.and_then(rval_call_and_then{}); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o5r.value() == 42); // ensure that lhs is forwarded tl::optional o6 = 42; auto o6r = - std::move(o6).bind([](int &&i) { return tl::optional(i); }); + std::move(o6).and_then([](int &&i) { return tl::optional(i); }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o6r.value() == 42); // ensure that function object is const-propagated const tl::optional o7 = 42; - auto o7r = o7.bind([](const int &i) { return tl::optional(i); }); + auto o7r = o7.and_then([](const int &i) { return tl::optional(i); }); STATIC_REQUIRE((std::is_same>::value)); REQUIRE(o7r.value() == 42); // test each overload in turn tl::optional o8 = 42; - auto o8r = o8.bind([](int i) { return tl::make_optional(42); }); + auto o8r = o8.and_then([](int i) { return tl::make_optional(42); }); REQUIRE(*o8r == 42); tl::optional o9 = 42; - auto o9r = std::move(o9).bind([](int i) { return tl::make_optional(42); }); + auto o9r = + std::move(o9).and_then([](int i) { return tl::make_optional(42); }); REQUIRE(*o9r == 42); const tl::optional o10 = 42; - auto o10r = o10.bind([](int i) { return tl::make_optional(42); }); + auto o10r = o10.and_then([](int i) { return tl::make_optional(42); }); REQUIRE(*o10r == 42); const tl::optional o11 = 42; - auto o11r = std::move(o11).bind([](int i) { return tl::make_optional(42); }); + auto o11r = + std::move(o11).and_then([](int i) { return tl::make_optional(42); }); REQUIRE(*o11r == 42); - tl::optional o12 = 42; - auto o12r = o12.bind([](int i) { return tl::make_optional(42); }, []{return;}); - REQUIRE(*o12r == 42); - - tl::optional o13 = 42; - auto o13r = std::move(o13).bind([](int i) { return tl::make_optional(42); }, []{return;}); - REQUIRE(*o13r == 42); - - const tl::optional o14 = 42; - auto o14r = o14.bind([](int i) { return tl::make_optional(42); }, []{return;}); - REQUIRE(*o14r == 42); - - const tl::optional o15 = 42; - auto o15r = std::move(o15).bind([](int i) { return tl::make_optional(42); }, []{return;}); - REQUIRE(*o15r == 42); - tl::optional o16 = tl::nullopt; - auto o16r = o16.bind([](int i) { return tl::make_optional(42); }); + auto o16r = o16.and_then([](int i) { return tl::make_optional(42); }); REQUIRE(!o16r); tl::optional o17 = tl::nullopt; - auto o17r = std::move(o17).bind([](int i) { return tl::make_optional(42); }); + auto o17r = + std::move(o17).and_then([](int i) { return tl::make_optional(42); }); REQUIRE(!o17r); const tl::optional o18 = tl::nullopt; - auto o18r = o18.bind([](int i) { return tl::make_optional(42); }); + auto o18r = o18.and_then([](int i) { return tl::make_optional(42); }); REQUIRE(!o18r); const tl::optional o19 = tl::nullopt; - auto o19r = std::move(o19).bind([](int i) { return tl::make_optional(42); }); + auto o19r = + std::move(o19).and_then([](int i) { return tl::make_optional(42); }); REQUIRE(!o19r); - - tl::optional o20 = tl::nullopt; - bool c20 = false; - auto o20r = o20.bind([](int i) { return tl::make_optional(42); }, [&]{c20 = true;}); - REQUIRE(!o20r); - REQUIRE(!c20); - - tl::optional o21 = tl::nullopt; - bool c21 = false; - auto o21r = std::move(o21).bind([](int i) { return tl::make_optional(42); }, [&]{c21 = true;}); - REQUIRE(!o21r); - REQUIRE(!c21); - - const tl::optional o22 = tl::nullopt; - bool c22 = false; - auto o22r = o22.bind([](int i) { return tl::make_optional(42); }, [&]{c22 = true;}); - REQUIRE(!o22r); - REQUIRE(!c22); - - const tl::optional o23 = tl::nullopt; - bool c23 = false; - auto o23r = std::move(o23).bind([](int i) { return tl::make_optional(42); }, [&]{c23 = true;}); - REQUIRE(!o23r); - REQUIRE(!c23); - - tl::optional o24 = 42; - bool c24 = false; - auto o24r = o24.bind([](int i) { return tl::optional{tl::nullopt}; }, [&]{c24 = true;}); - REQUIRE(!o24r); - REQUIRE(c24); - - tl::optional o25 = 42; - bool c25 = false; - auto o25r = std::move(o25).bind([](int i) { return tl::optional{tl::nullopt}; }, [&]{c25 = true;}); - REQUIRE(!o25r); - REQUIRE(c25); - - const tl::optional o26 = 42; - bool c26 = false; - auto o26r = o26.bind([](int i) { return tl::optional{tl::nullopt};}, [&]{c26 = true;}); - REQUIRE(!o26r); - REQUIRE(c26); - - const tl::optional o27 = 42; - bool c27 = false; - auto o27r = std::move(o27).bind([](int i) { return tl::optional{tl::nullopt};}, [&]{c27 = true;}); - REQUIRE(!o27r); - REQUIRE(c27); - } -SECTION("constexpr bind") { - constexpr tl::optional o10 = 42; - constexpr auto o10r = o10.bind(get_opt_int); +SECTION("constexpr and_then") { + constexpr tl::optional o10 = 42; + constexpr auto o10r = o10.and_then(get_opt_int); REQUIRE(*o10r == 42); constexpr tl::optional o11 = 42; - constexpr auto o11r = std::move(o11).bind(get_opt_int); + constexpr auto o11r = std::move(o11).and_then(get_opt_int); REQUIRE(*o11r == 42); constexpr tl::optional o18 = tl::nullopt; - constexpr auto o18r = o18.bind(get_opt_int); + constexpr auto o18r = o18.and_then(get_opt_int); REQUIRE(!o18r); constexpr tl::optional o19 = tl::nullopt; - constexpr auto o19r = std::move(o19).bind(get_opt_int); + constexpr auto o19r = std::move(o19).and_then(get_opt_int); REQUIRE(!o19r); } + +SECTION("or else") { + //TODO +} } ;