result_combinators: add result_wrap_unpack
Adds a helper combinator utils::result_wrap_unpack which, in contrast to
utils::result_wrap, uses futurize_apply instead of futurize_invoke to
call the wrapped callable.
In short, if utils::result_wrap is used to adapt code like this:
f.then([] {})
->
f_result.then(utils::result_wrap([] {}))
Then utils::result_wrap_unpack works for the following case:
f.then_unpack([] (arg1, arg2) {})
->
f_result.then(utils::result_wrap_unpack([] (arg1, arg2) {}))
This commit is contained in:
@@ -110,6 +110,18 @@ SEASTAR_THREAD_TEST_CASE(test_result_wrap) {
|
||||
|
||||
BOOST_REQUIRE_THROW(fun_int(result<int>(bo::failure(foo_exception()))).get().value(), foo_exception);
|
||||
BOOST_REQUIRE_EQUAL(run_count, 2);
|
||||
|
||||
// T is a tuple, result_wrap_unpack
|
||||
auto fun_tuple = utils::result_wrap_unpack([&run_count] (int x, int y) -> result<int> {
|
||||
++run_count;
|
||||
return bo::success(x + y);
|
||||
});
|
||||
|
||||
BOOST_REQUIRE_EQUAL(fun_tuple(result<std::tuple<int, int>>(bo::success(std::make_tuple(123, 321)))).get().value(), 444);
|
||||
BOOST_REQUIRE_EQUAL(run_count, 3);
|
||||
|
||||
BOOST_REQUIRE_THROW(fun_tuple(result<std::tuple<int, int>>(bo::failure(foo_exception()))).get().value(), foo_exception);
|
||||
BOOST_REQUIRE_EQUAL(run_count, 3);
|
||||
}
|
||||
|
||||
// If T is a future, attaches a continuation and converts it to future<U>
|
||||
|
||||
@@ -59,8 +59,12 @@ rebind_result<void, R> result_discard_value(R&& res) {
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename C, typename Arg>
|
||||
template<typename C, typename Arg, bool Unpack>
|
||||
struct result_wrapped_call_traits {
|
||||
};
|
||||
|
||||
template<typename C, typename Arg>
|
||||
struct result_wrapped_call_traits<C, Arg, false> {
|
||||
static_assert(ExceptionContainerResult<Arg>);
|
||||
using return_type = decltype(seastar::futurize_invoke(std::declval<C>(), std::declval<typename Arg::value_type>()));
|
||||
|
||||
@@ -71,7 +75,7 @@ struct result_wrapped_call_traits {
|
||||
};
|
||||
|
||||
template<typename C, typename... Exs>
|
||||
struct result_wrapped_call_traits<C, result_with_exception<void, Exs...>> {
|
||||
struct result_wrapped_call_traits<C, result_with_exception<void, Exs...>, false> {
|
||||
using return_type = decltype(seastar::futurize_invoke(std::declval<C>()));
|
||||
|
||||
static auto invoke_with_value(C& c, result_with_exception<void, Exs...>&& arg) {
|
||||
@@ -79,7 +83,26 @@ struct result_wrapped_call_traits<C, result_with_exception<void, Exs...>> {
|
||||
}
|
||||
};
|
||||
|
||||
template<typename C>
|
||||
template<typename C, ExceptionContainer ExCont, typename... Args>
|
||||
struct result_wrapped_call_traits<C, bo::result<std::tuple<Args...>, ExCont, exception_container_throw_policy>, true> {
|
||||
private:
|
||||
using result_type = bo::result<std::tuple<Args...>, ExCont, exception_container_throw_policy>;
|
||||
|
||||
public:
|
||||
using return_type = decltype(seastar::futurize_apply(std::declval<C>(), std::declval<std::tuple<Args...>>()));
|
||||
|
||||
static auto invoke_with_value(C& c, result_type&& args) {
|
||||
// Arg must have a value
|
||||
return seastar::futurize_apply(c, std::move(args).value());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename C, typename Arg>
|
||||
struct result_wrapped_call_traits<C, Arg, true> {
|
||||
static_assert(false && sizeof(Arg), "result_wrap_apply must be called with a result<std::tuple<...>> as a second argument");
|
||||
};
|
||||
|
||||
template<typename C, bool Unpack>
|
||||
struct result_wrapper {
|
||||
C c;
|
||||
|
||||
@@ -88,7 +111,7 @@ struct result_wrapper {
|
||||
template<typename InputResult>
|
||||
requires ExceptionContainerResult<InputResult>
|
||||
auto operator()(InputResult arg) {
|
||||
using traits = internal::result_wrapped_call_traits<C, InputResult>;
|
||||
using traits = internal::result_wrapped_call_traits<C, InputResult, Unpack>;
|
||||
using return_type = typename traits::return_type;
|
||||
static_assert(ExceptionContainerResultFuture<return_type>,
|
||||
"the return type of the call must be a future<result<T>> for some T");
|
||||
@@ -122,7 +145,14 @@ struct result_wrapper {
|
||||
// it won't be automatically converted to result<>.
|
||||
template<typename C>
|
||||
auto result_wrap(C&& c) {
|
||||
return internal::result_wrapper<C>(std::move(c));
|
||||
return internal::result_wrapper<C, false>(std::move(c));
|
||||
}
|
||||
|
||||
// Similar to result_wrap, but the resulting callable takes a result<tuple<...>>,
|
||||
// unpacks the tuple and provides each argument separately to the wrapped callable.
|
||||
template<typename C>
|
||||
auto result_wrap_unpack(C&& c) {
|
||||
return internal::result_wrapper<C, true>(std::move(c));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user