mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Make the failure handler partial applyable
* Make result C++17 destructible * Add unit tests
This commit is contained in:
parent
bb7112eec2
commit
70c716bb28
@ -100,6 +100,13 @@ target_compile_features(continuable-base
|
||||
cxx_trailing_return_types
|
||||
cxx_return_type_deduction)
|
||||
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||
target_compile_features(continuable-base
|
||||
INTERFACE
|
||||
cxx_std_17)
|
||||
endif()
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
|
||||
target_compile_options(continuable-base
|
||||
INTERFACE
|
||||
|
||||
@ -50,11 +50,7 @@ target_compile_options(continuable-features-warnings
|
||||
INTERFACE
|
||||
/W4)
|
||||
|
||||
if (CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||
target_compile_options(continuable-features-flags
|
||||
INTERFACE
|
||||
/std:c++latest)
|
||||
else()
|
||||
if (NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||
target_compile_options(continuable-features-flags
|
||||
INTERFACE
|
||||
/std:c++14)
|
||||
|
||||
@ -101,9 +101,7 @@ public:
|
||||
/// \throws This method never throws an exception.
|
||||
///
|
||||
/// \since 2.0.0
|
||||
void operator()(exception_arg_t tag,
|
||||
exception_t exception) &&
|
||||
noexcept {
|
||||
void operator()(exception_arg_t tag, exception_t exception) && noexcept {
|
||||
std::move(data_)(tag, std::move(exception));
|
||||
}
|
||||
|
||||
|
||||
@ -197,6 +197,19 @@ private:
|
||||
detail::container::flat_variant<surrogate_t, exception_t> variant_;
|
||||
};
|
||||
|
||||
template <std::size_t I, typename... T>
|
||||
decltype(auto) get(result<T...>& result) {
|
||||
return detail::result_trait<T...>::template get<I>(result);
|
||||
}
|
||||
template <std::size_t I, typename... T>
|
||||
decltype(auto) get(result<T...> const& result) {
|
||||
return detail::result_trait<T...>::template get<I>(result);
|
||||
}
|
||||
template <std::size_t I, typename... T>
|
||||
decltype(auto) get(result<T...>&& result) {
|
||||
return detail::result_trait<T...>::template get<I>(std::move(result));
|
||||
}
|
||||
|
||||
inline result<> make_result() {
|
||||
result<> result;
|
||||
result.set_value();
|
||||
@ -209,4 +222,23 @@ auto make_result(T&&... values) {
|
||||
/// \}
|
||||
} // namespace cti
|
||||
|
||||
namespace std {
|
||||
// The GCC standard library defines tuple_size as class and struct which
|
||||
// triggers a warning here.
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
template <typename... Args>
|
||||
struct tuple_size<cti::result<Args...>>
|
||||
: std::integral_constant<size_t, sizeof...(Args)> {};
|
||||
|
||||
template <std::size_t I, typename... Args>
|
||||
struct tuple_element<I, cti::result<Args...>>
|
||||
: tuple_element<I, tuple<Args...>> {};
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
} // namespace std
|
||||
|
||||
#endif // CONTINUABLE_RESULT_HPP_INCLUDED
|
||||
|
||||
@ -146,6 +146,22 @@ public:
|
||||
#define CONTINUABLE_BLOCK_TRY_END }
|
||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||
|
||||
/// Invokes the callback partially, keeps the exception_arg_t such that
|
||||
/// we don't jump accidentally from the exception path to the result path.
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto invoke_callback(T&& callable, exception_arg_t exception_arg,
|
||||
Args&&... args) noexcept {
|
||||
return util::partial_invoke(std::integral_constant<std::size_t, 01>{},
|
||||
std::forward<T>(callable), exception_arg,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
template <typename T, typename... Args>
|
||||
constexpr auto invoke_callback(T&& callable, Args&&... args) noexcept {
|
||||
return util::partial_invoke(std::integral_constant<std::size_t, 0U>{},
|
||||
std::forward<T>(callable),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/// Invokes the given callable object with the given arguments while
|
||||
/// marking the operation as non exceptional.
|
||||
template <typename T, typename... Args>
|
||||
@ -173,8 +189,8 @@ invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
auto continuation_ =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
invoke_continuation(
|
||||
std::move(continuation_),
|
||||
@ -191,8 +207,8 @@ constexpr auto invoker_of(traits::identity<T>) {
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
auto result =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
|
||||
std::move(result));
|
||||
@ -206,8 +222,8 @@ inline auto invoker_of(traits::identity<void>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_no_except(
|
||||
std::forward<decltype(next_callback)>(next_callback));
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
@ -222,8 +238,8 @@ inline auto invoker_of(traits::identity<empty_result>) {
|
||||
(void)next_callback;
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
empty_result result =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Don't invoke anything here since returning an empty result
|
||||
// cancels the asynchronous chain effectively.
|
||||
@ -240,8 +256,8 @@ inline auto invoker_of(traits::identity<exceptional_result>) {
|
||||
util::unused(callback, next_callback, args...);
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
exceptional_result result =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Forward the exception to the next available handler
|
||||
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
|
||||
@ -259,19 +275,19 @@ auto invoker_of(traits::identity<result<Args...>>) {
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
result<Args...> result =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
if (result.is_value()) {
|
||||
// Workaround for MSVC not capturing the reference
|
||||
// correctly inside the lambda.
|
||||
using Next = decltype(next_callback);
|
||||
|
||||
result_trait<Args...>::visit(
|
||||
std::move(result), //
|
||||
traits::unpack(
|
||||
[&](auto&&... values) {
|
||||
invoke_no_except(std::forward<Next>(next_callback),
|
||||
std::forward<decltype(values)>(values)...);
|
||||
});
|
||||
},
|
||||
std::move(result));
|
||||
|
||||
} else if (result.is_exception()) {
|
||||
// Forward the exception to the next available handler
|
||||
@ -292,9 +308,8 @@ auto invoker_of(traits::identity<result<Args...>>) {
|
||||
inline auto sequenced_unpack_invoker() {
|
||||
return [](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
auto result =
|
||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
auto result = invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Workaround for MSVC not capturing the reference
|
||||
// correctly inside the lambda.
|
||||
@ -326,8 +341,8 @@ constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
|
||||
inline auto exception_invoker_of(traits::identity<void>) noexcept {
|
||||
return [](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
util::invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
// The legacy behaviour is not to proceed the chain
|
||||
// on the first invoked failure handler
|
||||
@ -336,81 +351,16 @@ inline auto exception_invoker_of(traits::identity<void>) noexcept {
|
||||
};
|
||||
}
|
||||
|
||||
/*template <typename... Args>
|
||||
inline auto exception_invoker_of(traits::identity<empty_result> id) noexcept {
|
||||
return invoker_of(id);
|
||||
}
|
||||
inline auto
|
||||
exception_invoker_of(traits::identity<exceptional_result> id) noexcept {
|
||||
return invoker_of(id);
|
||||
}
|
||||
template <typename... Args>
|
||||
auto exception_invoker_of(traits::identity<result<Args...>> id) noexcept {
|
||||
return invoker_of(id);
|
||||
}*/
|
||||
|
||||
/// - empty_result -> <cancel>
|
||||
inline auto exception_invoker_of(traits::identity<empty_result>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
(void)next_callback;
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
empty_result result =
|
||||
util::invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Don't invoke anything here since returning an empty result
|
||||
// cancels the asynchronous chain effectively.
|
||||
(void)result;
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
},
|
||||
traits::identity<>{});
|
||||
}
|
||||
|
||||
/// - exceptional_result -> <throw>
|
||||
inline auto exception_invoker_of(traits::identity<exceptional_result>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
util::unused(callback, next_callback, args...);
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
exceptional_result result =
|
||||
util::invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
// Forward the exception to the next available handler
|
||||
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
|
||||
exception_arg_t{},
|
||||
std::move(result).get_exception());
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
},
|
||||
traits::identity<>{});
|
||||
}
|
||||
|
||||
/// - result<?...> -> next_callback(?...)
|
||||
template <typename... Args>
|
||||
auto exception_invoker_of(traits::identity<result<Args...>>) {
|
||||
return make_invoker(
|
||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
result<Args...> result =
|
||||
util::invoke(std::forward<decltype(callback)>(callback),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
if (result.is_value()) {
|
||||
// Workaround for MSVC not capturing the reference
|
||||
// correctly inside the lambda.
|
||||
using Next = decltype(next_callback);
|
||||
|
||||
result_trait<Args...>::visit(
|
||||
std::move(result), //
|
||||
[&](auto&&... values) {
|
||||
invoke_no_except(std::forward<Next>(next_callback),
|
||||
std::forward<decltype(values)>(values)...);
|
||||
});
|
||||
|
||||
} else if (result.is_exception()) {
|
||||
// Forward the exception to the next available handler
|
||||
invoke_no_except(
|
||||
std::forward<decltype(next_callback)>(next_callback),
|
||||
exception_arg_t{}, std::move(result).get_exception());
|
||||
}
|
||||
|
||||
// Otherwise the result is empty and we are cancelling our
|
||||
// asynchronous chain.
|
||||
CONTINUABLE_BLOCK_TRY_END
|
||||
},
|
||||
traits::identity<Args...>{});
|
||||
}
|
||||
|
||||
#undef CONTINUABLE_BLOCK_TRY_BEGIN
|
||||
@ -482,8 +432,10 @@ struct result_handler_base<handle_results::yes, Base,
|
||||
void operator()(Args... args) && {
|
||||
// In order to retrieve the correct decorator we must know what the
|
||||
// result type is.
|
||||
constexpr auto result = traits::identify<decltype(util::partial_invoke(
|
||||
std::move(static_cast<Base*>(this)->callback_), std::move(args)...))>{};
|
||||
constexpr auto result =
|
||||
traits::identify<decltype(decoration::invoke_callback(
|
||||
std::move(static_cast<Base*>(this)->callback_),
|
||||
std::move(args)...))>{};
|
||||
|
||||
// Pick the correct invoker that handles decorating of the result
|
||||
auto invoker = decoration::invoker_of(result);
|
||||
@ -512,9 +464,10 @@ template <typename Base>
|
||||
struct error_handler_base<handle_errors::forward, Base> {
|
||||
/// The operator which is called when an error occurred
|
||||
void operator()(exception_arg_t, exception_t exception) && {
|
||||
constexpr auto result = traits::identify<decltype(
|
||||
util::partial_invoke(std::move(static_cast<Base*>(this)->callback_),
|
||||
exception_arg_t{}, std::move(exception)))>{};
|
||||
constexpr auto result =
|
||||
traits::identify<decltype(decoration::invoke_callback(
|
||||
std::move(static_cast<Base*>(this)->callback_), exception_arg_t{},
|
||||
std::move(exception)))>{};
|
||||
|
||||
auto invoker = decoration::exception_invoker_of(result);
|
||||
|
||||
@ -645,7 +598,7 @@ next_hint_of(std::integral_constant<handle_results, handle_results::yes>,
|
||||
hints::signature_hint_tag<Args...> /*current*/) {
|
||||
// Partial Invoke the given callback
|
||||
using Result = decltype(
|
||||
util::partial_invoke(std::declval<T>(), std::declval<Args>()...));
|
||||
decoration::invoke_callback(std::declval<T>(), std::declval<Args>()...));
|
||||
|
||||
// Return the hint of thr given invoker
|
||||
return decltype(decoration::invoker_of(traits::identify<Result>{}).hint()){};
|
||||
|
||||
@ -53,13 +53,6 @@ struct result_trait<> {
|
||||
|
||||
static constexpr void unwrap(surrogate_t) {
|
||||
}
|
||||
|
||||
template <typename Result, typename Mapper>
|
||||
static auto visit(Result&& result, Mapper&& mapper) {
|
||||
assert(result.is_value());
|
||||
(void)result;
|
||||
return util::invoke(std::forward<Mapper>(mapper));
|
||||
}
|
||||
};
|
||||
template <typename T>
|
||||
struct result_trait<T> {
|
||||
@ -75,10 +68,9 @@ struct result_trait<T> {
|
||||
return std::forward<R>(unwrap);
|
||||
}
|
||||
|
||||
template <typename Result, typename Mapper>
|
||||
static auto visit(Result&& result, Mapper&& mapper) {
|
||||
return util::invoke(std::forward<Mapper>(mapper),
|
||||
std::forward<Result>(result).get_value());
|
||||
template <std::size_t I, typename Result>
|
||||
static decltype(auto) get(Result&& result) {
|
||||
return std::forward<Result>(result).get_value();
|
||||
}
|
||||
};
|
||||
template <typename First, typename Second, typename... Rest>
|
||||
@ -96,10 +88,9 @@ struct result_trait<First, Second, Rest...> {
|
||||
return std::forward<R>(unwrap);
|
||||
}
|
||||
|
||||
template <typename Result, typename Mapper>
|
||||
static auto visit(Result&& result, Mapper&& mapper) {
|
||||
return traits::unpack(std::forward<Mapper>(mapper),
|
||||
std::forward<Result>(result).get_value());
|
||||
template <std::size_t I, typename Result>
|
||||
static decltype(auto) get(Result&& result) {
|
||||
return std::get<I>(std::forward<Result>(result).get_value());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
@ -98,18 +98,20 @@ template <typename... T>
|
||||
using void_t = typename detail::deduce_to_void<T...>::type;
|
||||
#endif // CONTINUABLE_HAS_CXX17_VOID_T
|
||||
|
||||
namespace detail {
|
||||
namespace detail_unpack {
|
||||
using std::get;
|
||||
|
||||
/// Calls the given unpacker with the content of the given sequenceable
|
||||
template <typename U, typename F, std::size_t... I>
|
||||
constexpr auto unpack_impl(U&& unpacker, F&& first_sequenceable,
|
||||
std::integer_sequence<std::size_t, I...>)
|
||||
-> decltype(std::forward<U>(unpacker)(
|
||||
std::get<I>(std::forward<F>(first_sequenceable))...)) {
|
||||
get<I>(std::forward<F>(first_sequenceable))...)) {
|
||||
(void)first_sequenceable;
|
||||
return std::forward<U>(unpacker)(
|
||||
std::get<I>(std::forward<F>(first_sequenceable))...);
|
||||
get<I>(std::forward<F>(first_sequenceable))...);
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace detail_unpack
|
||||
|
||||
/// Calls the given callable object with the content of the given sequenceable
|
||||
///
|
||||
@ -119,12 +121,13 @@ template <typename Callable, typename TupleLike,
|
||||
typename Sequence = std::make_index_sequence<
|
||||
std::tuple_size<std::decay_t<TupleLike>>::value>>
|
||||
constexpr auto unpack(Callable&& obj, TupleLike&& tuple_like)
|
||||
-> decltype(detail::unpack_impl(std::forward<Callable>(obj),
|
||||
std::forward<TupleLike>(tuple_like),
|
||||
Sequence{})) {
|
||||
-> decltype(detail_unpack::unpack_impl(std::forward<Callable>(obj),
|
||||
std::forward<TupleLike>(tuple_like),
|
||||
Sequence{})) {
|
||||
|
||||
return detail::unpack_impl(std::forward<Callable>(obj),
|
||||
std::forward<TupleLike>(tuple_like), Sequence{});
|
||||
return detail_unpack::unpack_impl(std::forward<Callable>(obj),
|
||||
std::forward<TupleLike>(tuple_like),
|
||||
Sequence{});
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
@ -126,15 +126,17 @@ struct invocation_env {
|
||||
///
|
||||
/// \note This function will assert statically if there is no way to call the
|
||||
/// given object with less arguments.
|
||||
template <typename T, typename... Args>
|
||||
/*keep this inline*/ inline auto partial_invoke(T&& callable, Args&&... args) {
|
||||
template <std::size_t KeepArgs, typename T, typename... Args>
|
||||
/*keep this inline*/ inline auto
|
||||
partial_invoke(std::integral_constant<std::size_t, KeepArgs>, T&& callable,
|
||||
Args&&... args) {
|
||||
// Test whether we are able to call the function with the given arguments.
|
||||
traits::is_invokable_from_tuple<decltype(callable), std::tuple<Args...>>
|
||||
is_invokable;
|
||||
|
||||
// The implementation is done in a shortcut way so there are less
|
||||
// type instantiations needed to call the callable with its full signature.
|
||||
using env = detail::invocation_env<0U>;
|
||||
using env = detail::invocation_env<KeepArgs>;
|
||||
return env::partial_invoke_impl_shortcut(
|
||||
is_invokable, std::forward<T>(callable), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
|
||||
#include <test-continuable.hpp>
|
||||
|
||||
using namespace cti;
|
||||
|
||||
TYPED_TEST(single_dimension_tests, are_completing_errors) {
|
||||
ASSERT_ASYNC_EXCEPTION_COMPLETION(
|
||||
this->supply_exception(supply_test_exception()));
|
||||
@ -110,3 +112,34 @@ TYPED_TEST(single_dimension_tests, are_flow_error_accepting) {
|
||||
ASSERT_ASYNC_INCOMPLETION(std::move(continuation));
|
||||
ASSERT_TRUE(*handled);
|
||||
}
|
||||
|
||||
TYPED_TEST(single_dimension_tests, are_exceptions_partial_applyable) {
|
||||
bool handled = false;
|
||||
ASSERT_ASYNC_INCOMPLETION(
|
||||
this->supply_exception(supply_test_exception()).fail([&]() -> void {
|
||||
EXPECT_FALSE(handled);
|
||||
handled = true;
|
||||
}));
|
||||
ASSERT_TRUE(handled);
|
||||
|
||||
handled = false;
|
||||
ASSERT_ASYNC_INCOMPLETION(this->supply_exception(supply_test_exception())
|
||||
.fail([&]() -> empty_result {
|
||||
EXPECT_FALSE(handled);
|
||||
handled = true;
|
||||
return cancel();
|
||||
}));
|
||||
ASSERT_TRUE(handled);
|
||||
|
||||
handled = false;
|
||||
ASSERT_ASYNC_INCOMPLETION(
|
||||
this->supply_exception(supply_test_exception(),
|
||||
detail::traits::identity<int, int>{})
|
||||
.fail([&]() -> result<int, int> {
|
||||
EXPECT_FALSE(handled);
|
||||
handled = true;
|
||||
return cancel();
|
||||
}));
|
||||
|
||||
ASSERT_TRUE(handled);
|
||||
}
|
||||
|
||||
@ -140,3 +140,36 @@ TYPED_TEST(single_dimension_tests, multipath_exception_is_autocanceled) {
|
||||
}));
|
||||
ASSERT_TRUE(caught);
|
||||
}
|
||||
|
||||
#if !defined(CONTINUABLE_WITH_NO_EXCEPTIONS)
|
||||
// Enable this test only if we support exceptions
|
||||
TYPED_TEST(single_dimension_tests, multipath_exception_can_rethrow) {
|
||||
|
||||
ASSERT_ASYNC_EXCEPTION_COMPLETION(
|
||||
this->supply_exception(supply_test_exception()).fail([](exception_t) {
|
||||
// Throw an exception from inside the exception handler
|
||||
throw test_exception();
|
||||
}));
|
||||
|
||||
ASSERT_ASYNC_EXCEPTION_COMPLETION(
|
||||
this->supply_exception(supply_test_exception())
|
||||
.fail([](exception_t) -> empty_result {
|
||||
// Throw an exception from inside the exception handler
|
||||
throw test_exception();
|
||||
}));
|
||||
|
||||
ASSERT_ASYNC_EXCEPTION_COMPLETION(
|
||||
this->supply_exception(supply_test_exception())
|
||||
.fail([](exception_t) -> exceptional_result {
|
||||
// Throw an exception from inside the exception handler
|
||||
throw test_exception();
|
||||
}));
|
||||
|
||||
ASSERT_ASYNC_EXCEPTION_COMPLETION(
|
||||
this->supply_exception(supply_test_exception(), identity<int>{})
|
||||
.fail([](exception_t) -> result<int> {
|
||||
// Throw an exception from inside the exception handler
|
||||
throw test_exception();
|
||||
}));
|
||||
}
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user