diff --git a/README.md b/README.md index deda3f8..90fe1c0 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ This is a header-only library whose algorithms are in [`algorithm.hpp`](include/ - Include `rvarago/optional_extras/algorithm.hpp` - Call into the functions inside the `rvarago::optional_extras::algorithm` namespace, perhaps alias it as `namespace optx = rvarago::optional_extras::algorithm;` +For a terser syntax for chaining multiple operations, there's an `optional_wrapper` in [`wrapper.hpp`](./include/rvarago/optional_extras/wrapper.hpp) that lifts an `std::optional` +and with algorithms as member-functions. So, an inside-out `op_2(op_1(lhs, rhs), func)` is equivalent to the left-to-right `lift(lhs).op_1(rhs).op_2(func).get`. + ### Functions - `filter(optional, T -> bool) -> optional` diff --git a/include/rvarago/optional_extras/wrapper.hpp b/include/rvarago/optional_extras/wrapper.hpp new file mode 100644 index 0000000..5dad372 --- /dev/null +++ b/include/rvarago/optional_extras/wrapper.hpp @@ -0,0 +1,46 @@ +#include +#include + +#include "algorithm.hpp" + +namespace rvarago::optional_extras { + +template struct optional_wrapper { + + template Predicate> + constexpr auto filter(Predicate &&pred) && noexcept(noexcept( + algorithm::filter(std::declval &&>(), + std::declval()))) -> optional_wrapper { + return optional_wrapper{ + algorithm::filter(std::move(get), std::forward(pred))}; + } + + template BinaryFunction> + constexpr auto + zip_with(std::optional rhs, BinaryFunction &&merge) && noexcept( + noexcept(algorithm::zip_with( + std::declval &&>(), + std::declval &&>(), + std::forward(std::declval())))) + -> optional_wrapper> { + return optional_wrapper{algorithm::zip_with( + std::move(get), std::move(rhs), std::forward(merge))}; + } + + template + constexpr auto zip(std::optional rhs) && noexcept( + noexcept(algorithm::zip(std::declval &&>(), + std::declval &&>()))) + -> optional_wrapper> { + return optional_wrapper{algorithm::zip(std::move(get), std::move(rhs))}; + } + + std::optional get; +}; + +template +constexpr auto lift(std::optional inner) -> optional_wrapper { + return optional_wrapper{std::move(inner)}; +} + +} // namespace rvarago::optional_extras diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 85d4610..88eff35 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,7 +4,7 @@ include(CTest) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(${PROJECT_NAME} algorithm_test.cpp) +add_executable(${PROJECT_NAME} algorithm_test.cpp wrapper_test.cpp) target_compile_options( ${PROJECT_NAME} diff --git a/test/wrapper_test.cpp b/test/wrapper_test.cpp new file mode 100644 index 0000000..96271fb --- /dev/null +++ b/test/wrapper_test.cpp @@ -0,0 +1,15 @@ +#include + +#include + +#include + +namespace optx = rvarago::optional_extras; + +using std::optional; + +TEST_CASE("The wrapper delegates to already tested algorithms", "[smoke]") { + constexpr auto got = + optx::lift(optional{10}).zip_with(optional{20}, std::plus<>{}).get; + STATIC_REQUIRE(got == optional{30}); +}