Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` in [`wrapper.hpp`](./include/rvarago/optional_extras/wrapper.hpp) that lifts an `std::optional<T>`
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>, T -> bool) -> optional<T>`
Expand Down
46 changes: 46 additions & 0 deletions include/rvarago/optional_extras/wrapper.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <optional>
#include <utility>

#include "algorithm.hpp"

namespace rvarago::optional_extras {

template <typename T> struct optional_wrapper {

template <std::predicate<T const &> Predicate>
constexpr auto filter(Predicate &&pred) && noexcept(noexcept(
algorithm::filter(std::declval<std::optional<T> &&>(),
std::declval<Predicate &&>()))) -> optional_wrapper<T> {
return optional_wrapper{
algorithm::filter(std::move(get), std::forward<Predicate>(pred))};
}

template <typename R, std::regular_invocable<T, R> BinaryFunction>
constexpr auto
zip_with(std::optional<R> rhs, BinaryFunction &&merge) && noexcept(
noexcept(algorithm::zip_with(
std::declval<std::optional<T> &&>(),
std::declval<std::optional<R> &&>(),
std::forward<BinaryFunction>(std::declval<BinaryFunction &&>()))))
-> optional_wrapper<std::invoke_result_t<BinaryFunction, T &&, R &&>> {
return optional_wrapper{algorithm::zip_with(
std::move(get), std::move(rhs), std::forward<BinaryFunction>(merge))};
}

template <typename R>
constexpr auto zip(std::optional<R> rhs) && noexcept(
noexcept(algorithm::zip(std::declval<std::optional<T> &&>(),
std::declval<std::optional<R> &&>())))
-> optional_wrapper<std::pair<T, R>> {
return optional_wrapper{algorithm::zip(std::move(get), std::move(rhs))};
}

std::optional<T> get;
};

template <typename T>
constexpr auto lift(std::optional<T> inner) -> optional_wrapper<T> {
return optional_wrapper<T>{std::move(inner)};
}

} // namespace rvarago::optional_extras
2 changes: 1 addition & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
15 changes: 15 additions & 0 deletions test/wrapper_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <catch2/catch_test_macros.hpp>

#include <optional>

#include <rvarago/optional_extras/wrapper.hpp>

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});
}