diff --git a/CMakeLists.txt b/CMakeLists.txt index f98d579..fc7626b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,4 +19,4 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp add_executable(tests ${TEST_SOURCES}) target_link_libraries(tests Catch) -set_property(TARGET tests PROPERTY CXX_STANDARD 17) +set_property(TARGET tests PROPERTY CXX_STANDARD 14) diff --git a/optional.hpp b/optional.hpp index c4d3de3..ae08c6e 100644 --- a/optional.hpp +++ b/optional.hpp @@ -106,12 +106,12 @@ template using invoke_result_t = typename invoke_result::type; template -using fixup_void = conditional_t, monostate, U>; +using fixup_void = conditional_t::value, monostate, U>; // TODO check if we need std::invoke_result here template struct get_invoke_optional_ret { using type = result_of_t< - conditional_t, + conditional_t::value, typename std::remove_reference_t::value_type &, typename std::remove_reference_t::value_type &&>(U)>; }; @@ -126,6 +126,18 @@ using get_map_return = optional>>; template using returns_void = std::is_void>; + +template +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 disable_if_ret_void = enable_if_t::value>; } struct in_place_t { @@ -744,7 +756,7 @@ public: } } - template constexpr auto bind(F &&f) & { + template constexpr detail::invoke_result_t bind(F &&f) & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -754,7 +766,7 @@ public: return detail::invoke(std::forward(f), value()); } - template constexpr auto bind(F &&f) && { + template constexpr detail::invoke_result_t bind(F &&f) && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -765,7 +777,8 @@ public: return detail::invoke(std::forward(f), std::move(value())); } - template constexpr auto bind(F &&f) const & { + template + constexpr detail::invoke_result_t bind(F &&f) const & { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -776,7 +789,8 @@ public: return detail::invoke(std::forward(f), value()); } - template constexpr auto bind(F &&f) const && { + template + constexpr detail::invoke_result_t bind(F &&f) const && { using result = detail::invoke_result_t; static_assert(detail::is_optional::value, "F must return an optional"); @@ -787,7 +801,8 @@ public: return detail::invoke(std::forward(f), std::move(value())); } - template constexpr auto bind(F &&f, E &&e) & { + template + constexpr 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"); @@ -801,7 +816,8 @@ public: return ret; } - template constexpr auto bind(F &&f, E &&e) && { + template + constexpr 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"); @@ -815,7 +831,8 @@ public: return ret; } - template constexpr auto bind(F &&f, E &&e) const & { + template + constexpr 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"); @@ -829,7 +846,8 @@ public: return ret; } - template constexpr auto bind(F &&f, E &&e) const && { + template + constexpr 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"); @@ -843,128 +861,154 @@ public: return ret; } - template constexpr detail::get_map_return map(F &&f) & { - if - constexpr(detail::is_optional::value) { - if (!f.has_value() || !has_value()) - return nullopt; - - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f).value(), value()); - return nullopt; - } - else { - return detail::invoke(std::forward(f).value(), value()); - } - } - else { - if (!has_value()) - return nullopt; - - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f), value()); - return {}; - } - else { - return detail::invoke(std::forward(f), value()); - } - } + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) & { + if (has_value()) + return detail::invoke(std::forward(f), value()); + return nullopt; } - template constexpr detail::get_map_return map(F &&f) && { - if - constexpr(detail::is_optional::value) { - if (!f.has_value() || !has_value()) - return nullopt; + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) & { + if (!has_value()) + return nullopt; - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f).value(), std::move(value())); - return {}; - } - else { - return detail::invoke(std::forward(f).value(), std::move(value())); - } - } - else { - if (!has_value()) - return nullopt; - - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f), std::move(value())); - return {}; - } - else { - return detail::invoke(std::forward(f), std::move(value())); - } - } + detail::invoke(std::forward(f), value()); + return {}; } - template - constexpr detail::get_map_return map(F &&f) const & { - if - constexpr(detail::is_optional::value) { - if (!f.has_value() || !has_value()) - return nullopt; + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) & { + if (!f.has_value() || !has_value()) + return nullopt; - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f).value(), value()); - return {}; - } - else { - return detail::invoke(std::forward(f).value(), value()); - } - } - else { - if (!has_value()) - return nullopt; - - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f), value()); - return {}; - } - else { - return detail::invoke(std::forward(f), value()); - } - } + return detail::invoke(std::forward(f).value(), value()); } - template - constexpr detail::get_map_return map(F &&f) const && { - if - constexpr(detail::is_optional::value) { - if (!f.has_value() || !has_value()) - return nullopt; + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) & { + if (!f.has_value() || !has_value()) + return nullopt; - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f).value(), std::move(value())); - return {}; - } - else { - return detail::invoke(std::forward(f).value(), std::move(value())); - } - } - else { - if (!has_value()) - return nullopt; + detail::invoke(std::forward(f).value(), value()); + return {}; + } - if - constexpr(detail::returns_void::value) { - detail::invoke(std::forward(f), std::move(value())); - return {}; - } - else { - return detail::invoke(std::forward(f), std::move(value())); - } - } + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) && { + if (has_value()) + return detail::invoke(std::forward(f), std::move(value())); + return {}; + } + + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) && { + if (!has_value()) + return nullopt; + + detail::invoke(std::forward(f), std::move(value())); + return {}; + } + + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) && { + if (!f.has_value() || !has_value()) + return nullopt; + + return detail::invoke(std::forward(f).value(), std::move(value())); + } + + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) && { + if (!f.has_value() || !has_value()) + return nullopt; + + detail::invoke(std::forward(f).value(), std::move(value())); + return {}; + } + + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const & { + if (has_value()) + return detail::invoke(std::forward(f), value()); + return nullopt; + } + + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const & { + if (!has_value()) + return nullopt; + + detail::invoke(std::forward(f), value()); + return {}; + } + + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const & { + if (!f.has_value() || !has_value()) + return nullopt; + + return detail::invoke(std::forward(f).value(), value()); + } + + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const & { + if (!f.has_value() || !has_value()) + return nullopt; + + detail::invoke(std::forward(f).value(), value()); + return {}; + } + + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const && { + if (has_value()) + return detail::invoke(std::forward(f), std::move(value())); + return nullopt; + } + + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const && { + if (!has_value()) + return nullopt; + + detail::invoke(std::forward(f), std::move(value())); + return {}; + } + + template * = nullptr, + detail::disable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const && { + if (!f.has_value() || !has_value()) + return nullopt; + + return detail::invoke(std::forward(f).value(), std::move(value())); + } + + template * = nullptr, + detail::enable_if_ret_void * = nullptr> + constexpr detail::get_map_return map(F &&f) const && { + if (!f.has_value() || !has_value()) + return nullopt; + + detail::invoke(std::forward(f).value(), std::move(value())); + return {}; } }; - template optional(T) -> optional; +//template optional(T)->optional; } diff --git a/tests/monadic.cpp b/tests/monadic.cpp index 3813df2..9f10f9e 100644 --- a/tests/monadic.cpp +++ b/tests/monadic.cpp @@ -1,18 +1,18 @@ #include "catch.hpp" #include "optional.hpp" - +template struct TC; // What is Clang Format up to?! TEST_CASE("Monadic operations", "[monadic]"){SECTION("map"){// lhs is empty tl::optional o1; auto o1r = o1.map([](int i) { return i + 2; }); -static_assert(std::is_same_v>); +static_assert(std::is_same>::value); REQUIRE(!o1r); // lhs has value tl::optional o2 = 40; auto o2r = o2.map([](int i) { return i + 2; }); -static_assert(std::is_same_v>); +static_assert(std::is_same>::value); REQUIRE(o2r.value() == 42); struct rval_call_map { @@ -22,33 +22,33 @@ struct rval_call_map { // ensure that function object is forwarded tl::optional o3 = 42; auto o3r = o3.map(rval_call_map{}); -static_assert(std::is_same_v>); +static_assert(std::is_same>::value); REQUIRE(o3r.value() == 42); // ensure that lhs is forwarded tl::optional o4 = 40; auto o4r = std::move(o4).map([](int &&i) { return i + 2; }); -static_assert(std::is_same_v>); +static_assert(std::is_same>::value); REQUIRE(o4r.value() == 42); // ensure that lhs is const-propagated const tl::optional o5 = 40; auto o5r = o5.map([](const int &i) { return i + 2; }); -static_assert(std::is_same_v>); +static_assert(std::is_same>::value); REQUIRE(o5r.value() == 42); // test applicative functor tl::optional o6 = 40; -auto f6 = tl::optional{[](const int &i) { return i + 2; }}; +auto f6 = tl::make_optional([](const int &i) { return i + 2; }); auto o6r = o6.map(f6); -static_assert(std::is_same_v>); +static_assert(std::is_same>::value); REQUIRE(o6r.value() == 42); // test void return tl::optional o7 = 40; -auto f7 = tl::optional{[](const int &i) { return; }}; +auto f7 = tl::make_optional([](const int &i) { return; }); auto o7r = o7.map(f7); -static_assert(std::is_same_v>); +static_assert(std::is_same>::value); REQUIRE(o6r.has_value()); } @@ -57,25 +57,25 @@ SECTION("bind") { // lhs is empty tl::optional o1; auto o1r = o1.bind([](int i) { return tl::optional{42}; }); - static_assert(std::is_same_v>); + static_assert(std::is_same>::value); REQUIRE(!o1r); // lhs has value tl::optional o2 = 12; auto o2r = o2.bind([](int i) { return tl::optional{42}; }); - static_assert(std::is_same_v>); + static_assert(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{}; }); - static_assert(std::is_same_v>); + static_assert(std::is_same>::value); REQUIRE(!o3r); // rhs returns empty tl::optional o4 = 12; auto o4r = o4.bind([](int i) { return tl::optional{}; }); - static_assert(std::is_same_v>); + static_assert(std::is_same>::value); REQUIRE(!o4r); struct rval_call_bind { @@ -85,20 +85,20 @@ SECTION("bind") { // ensure that function object is forwarded tl::optional o5 = 42; auto o5r = o5.bind(rval_call_bind{}); - static_assert(std::is_same_v>); + static_assert(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); }); - static_assert(std::is_same_v>); + static_assert(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); }); - static_assert(std::is_same_v>); + static_assert(std::is_same>::value); REQUIRE(o7r.value() == 42); } }