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_trailing_return_types
|
||||||
cxx_return_type_deduction)
|
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)
|
if (CTI_CONTINUABLE_WITH_EXPERIMENTAL_COROUTINE)
|
||||||
target_compile_options(continuable-base
|
target_compile_options(continuable-base
|
||||||
INTERFACE
|
INTERFACE
|
||||||
|
|||||||
@ -50,11 +50,7 @@ target_compile_options(continuable-features-warnings
|
|||||||
INTERFACE
|
INTERFACE
|
||||||
/W4)
|
/W4)
|
||||||
|
|
||||||
if (CTI_CONTINUABLE_WITH_CPP_LATEST)
|
if (NOT CTI_CONTINUABLE_WITH_CPP_LATEST)
|
||||||
target_compile_options(continuable-features-flags
|
|
||||||
INTERFACE
|
|
||||||
/std:c++latest)
|
|
||||||
else()
|
|
||||||
target_compile_options(continuable-features-flags
|
target_compile_options(continuable-features-flags
|
||||||
INTERFACE
|
INTERFACE
|
||||||
/std:c++14)
|
/std:c++14)
|
||||||
|
|||||||
@ -101,9 +101,7 @@ public:
|
|||||||
/// \throws This method never throws an exception.
|
/// \throws This method never throws an exception.
|
||||||
///
|
///
|
||||||
/// \since 2.0.0
|
/// \since 2.0.0
|
||||||
void operator()(exception_arg_t tag,
|
void operator()(exception_arg_t tag, exception_t exception) && noexcept {
|
||||||
exception_t exception) &&
|
|
||||||
noexcept {
|
|
||||||
std::move(data_)(tag, std::move(exception));
|
std::move(data_)(tag, std::move(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -197,6 +197,19 @@ private:
|
|||||||
detail::container::flat_variant<surrogate_t, exception_t> variant_;
|
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() {
|
inline result<> make_result() {
|
||||||
result<> result;
|
result<> result;
|
||||||
result.set_value();
|
result.set_value();
|
||||||
@ -209,4 +222,23 @@ auto make_result(T&&... values) {
|
|||||||
/// \}
|
/// \}
|
||||||
} // namespace cti
|
} // 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
|
#endif // CONTINUABLE_RESULT_HPP_INCLUDED
|
||||||
|
|||||||
@ -146,6 +146,22 @@ public:
|
|||||||
#define CONTINUABLE_BLOCK_TRY_END }
|
#define CONTINUABLE_BLOCK_TRY_END }
|
||||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
#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
|
/// Invokes the given callable object with the given arguments while
|
||||||
/// marking the operation as non exceptional.
|
/// marking the operation as non exceptional.
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
@ -173,8 +189,8 @@ invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
|
|||||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
auto continuation_ =
|
auto continuation_ =
|
||||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
|
|
||||||
invoke_continuation(
|
invoke_continuation(
|
||||||
std::move(continuation_),
|
std::move(continuation_),
|
||||||
@ -191,8 +207,8 @@ constexpr auto invoker_of(traits::identity<T>) {
|
|||||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
auto result =
|
auto result =
|
||||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
|
|
||||||
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
|
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
|
||||||
std::move(result));
|
std::move(result));
|
||||||
@ -206,8 +222,8 @@ inline auto invoker_of(traits::identity<void>) {
|
|||||||
return make_invoker(
|
return make_invoker(
|
||||||
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
invoke_no_except(
|
invoke_no_except(
|
||||||
std::forward<decltype(next_callback)>(next_callback));
|
std::forward<decltype(next_callback)>(next_callback));
|
||||||
CONTINUABLE_BLOCK_TRY_END
|
CONTINUABLE_BLOCK_TRY_END
|
||||||
@ -222,8 +238,8 @@ inline auto invoker_of(traits::identity<empty_result>) {
|
|||||||
(void)next_callback;
|
(void)next_callback;
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
empty_result result =
|
empty_result result =
|
||||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
|
|
||||||
// Don't invoke anything here since returning an empty result
|
// Don't invoke anything here since returning an empty result
|
||||||
// cancels the asynchronous chain effectively.
|
// cancels the asynchronous chain effectively.
|
||||||
@ -240,8 +256,8 @@ inline auto invoker_of(traits::identity<exceptional_result>) {
|
|||||||
util::unused(callback, next_callback, args...);
|
util::unused(callback, next_callback, args...);
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
exceptional_result result =
|
exceptional_result result =
|
||||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
|
|
||||||
// Forward the exception to the next available handler
|
// Forward the exception to the next available handler
|
||||||
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
|
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) {
|
[](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
result<Args...> result =
|
result<Args...> result =
|
||||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
if (result.is_value()) {
|
if (result.is_value()) {
|
||||||
// Workaround for MSVC not capturing the reference
|
// Workaround for MSVC not capturing the reference
|
||||||
// correctly inside the lambda.
|
// correctly inside the lambda.
|
||||||
using Next = decltype(next_callback);
|
using Next = decltype(next_callback);
|
||||||
|
|
||||||
result_trait<Args...>::visit(
|
traits::unpack(
|
||||||
std::move(result), //
|
|
||||||
[&](auto&&... values) {
|
[&](auto&&... values) {
|
||||||
invoke_no_except(std::forward<Next>(next_callback),
|
invoke_no_except(std::forward<Next>(next_callback),
|
||||||
std::forward<decltype(values)>(values)...);
|
std::forward<decltype(values)>(values)...);
|
||||||
});
|
},
|
||||||
|
std::move(result));
|
||||||
|
|
||||||
} else if (result.is_exception()) {
|
} else if (result.is_exception()) {
|
||||||
// Forward the exception to the next available handler
|
// Forward the exception to the next available handler
|
||||||
@ -292,9 +308,8 @@ auto invoker_of(traits::identity<result<Args...>>) {
|
|||||||
inline auto sequenced_unpack_invoker() {
|
inline auto sequenced_unpack_invoker() {
|
||||||
return [](auto&& callback, auto&& next_callback, auto&&... args) {
|
return [](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
auto result =
|
auto result = invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
util::partial_invoke(std::forward<decltype(callback)>(callback),
|
std::forward<decltype(args)>(args)...);
|
||||||
std::forward<decltype(args)>(args)...);
|
|
||||||
|
|
||||||
// Workaround for MSVC not capturing the reference
|
// Workaround for MSVC not capturing the reference
|
||||||
// correctly inside the lambda.
|
// 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 {
|
inline auto exception_invoker_of(traits::identity<void>) noexcept {
|
||||||
return [](auto&& callback, auto&& next_callback, auto&&... args) {
|
return [](auto&& callback, auto&& next_callback, auto&&... args) {
|
||||||
CONTINUABLE_BLOCK_TRY_BEGIN
|
CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
util::invoke(std::forward<decltype(callback)>(callback),
|
invoke_callback(std::forward<decltype(callback)>(callback),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
|
|
||||||
// The legacy behaviour is not to proceed the chain
|
// The legacy behaviour is not to proceed the chain
|
||||||
// on the first invoked failure handler
|
// 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 {
|
auto exception_invoker_of(traits::identity<result<Args...>> id) noexcept {
|
||||||
return invoker_of(id);
|
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
|
#undef CONTINUABLE_BLOCK_TRY_BEGIN
|
||||||
@ -482,8 +432,10 @@ struct result_handler_base<handle_results::yes, Base,
|
|||||||
void operator()(Args... args) && {
|
void operator()(Args... args) && {
|
||||||
// In order to retrieve the correct decorator we must know what the
|
// In order to retrieve the correct decorator we must know what the
|
||||||
// result type is.
|
// result type is.
|
||||||
constexpr auto result = traits::identify<decltype(util::partial_invoke(
|
constexpr auto result =
|
||||||
std::move(static_cast<Base*>(this)->callback_), std::move(args)...))>{};
|
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
|
// Pick the correct invoker that handles decorating of the result
|
||||||
auto invoker = decoration::invoker_of(result);
|
auto invoker = decoration::invoker_of(result);
|
||||||
@ -512,9 +464,10 @@ template <typename Base>
|
|||||||
struct error_handler_base<handle_errors::forward, Base> {
|
struct error_handler_base<handle_errors::forward, Base> {
|
||||||
/// The operator which is called when an error occurred
|
/// The operator which is called when an error occurred
|
||||||
void operator()(exception_arg_t, exception_t exception) && {
|
void operator()(exception_arg_t, exception_t exception) && {
|
||||||
constexpr auto result = traits::identify<decltype(
|
constexpr auto result =
|
||||||
util::partial_invoke(std::move(static_cast<Base*>(this)->callback_),
|
traits::identify<decltype(decoration::invoke_callback(
|
||||||
exception_arg_t{}, std::move(exception)))>{};
|
std::move(static_cast<Base*>(this)->callback_), exception_arg_t{},
|
||||||
|
std::move(exception)))>{};
|
||||||
|
|
||||||
auto invoker = decoration::exception_invoker_of(result);
|
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*/) {
|
hints::signature_hint_tag<Args...> /*current*/) {
|
||||||
// Partial Invoke the given callback
|
// Partial Invoke the given callback
|
||||||
using Result = decltype(
|
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 the hint of thr given invoker
|
||||||
return decltype(decoration::invoker_of(traits::identify<Result>{}).hint()){};
|
return decltype(decoration::invoker_of(traits::identify<Result>{}).hint()){};
|
||||||
|
|||||||
@ -53,13 +53,6 @@ struct result_trait<> {
|
|||||||
|
|
||||||
static constexpr void unwrap(surrogate_t) {
|
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>
|
template <typename T>
|
||||||
struct result_trait<T> {
|
struct result_trait<T> {
|
||||||
@ -75,10 +68,9 @@ struct result_trait<T> {
|
|||||||
return std::forward<R>(unwrap);
|
return std::forward<R>(unwrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Result, typename Mapper>
|
template <std::size_t I, typename Result>
|
||||||
static auto visit(Result&& result, Mapper&& mapper) {
|
static decltype(auto) get(Result&& result) {
|
||||||
return util::invoke(std::forward<Mapper>(mapper),
|
return std::forward<Result>(result).get_value();
|
||||||
std::forward<Result>(result).get_value());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
template <typename First, typename Second, typename... Rest>
|
template <typename First, typename Second, typename... Rest>
|
||||||
@ -96,10 +88,9 @@ struct result_trait<First, Second, Rest...> {
|
|||||||
return std::forward<R>(unwrap);
|
return std::forward<R>(unwrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Result, typename Mapper>
|
template <std::size_t I, typename Result>
|
||||||
static auto visit(Result&& result, Mapper&& mapper) {
|
static decltype(auto) get(Result&& result) {
|
||||||
return traits::unpack(std::forward<Mapper>(mapper),
|
return std::get<I>(std::forward<Result>(result).get_value());
|
||||||
std::forward<Result>(result).get_value());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|||||||
@ -98,18 +98,20 @@ template <typename... T>
|
|||||||
using void_t = typename detail::deduce_to_void<T...>::type;
|
using void_t = typename detail::deduce_to_void<T...>::type;
|
||||||
#endif // CONTINUABLE_HAS_CXX17_VOID_T
|
#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
|
/// Calls the given unpacker with the content of the given sequenceable
|
||||||
template <typename U, typename F, std::size_t... I>
|
template <typename U, typename F, std::size_t... I>
|
||||||
constexpr auto unpack_impl(U&& unpacker, F&& first_sequenceable,
|
constexpr auto unpack_impl(U&& unpacker, F&& first_sequenceable,
|
||||||
std::integer_sequence<std::size_t, I...>)
|
std::integer_sequence<std::size_t, I...>)
|
||||||
-> decltype(std::forward<U>(unpacker)(
|
-> decltype(std::forward<U>(unpacker)(
|
||||||
std::get<I>(std::forward<F>(first_sequenceable))...)) {
|
get<I>(std::forward<F>(first_sequenceable))...)) {
|
||||||
(void)first_sequenceable;
|
(void)first_sequenceable;
|
||||||
return std::forward<U>(unpacker)(
|
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
|
/// 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<
|
typename Sequence = std::make_index_sequence<
|
||||||
std::tuple_size<std::decay_t<TupleLike>>::value>>
|
std::tuple_size<std::decay_t<TupleLike>>::value>>
|
||||||
constexpr auto unpack(Callable&& obj, TupleLike&& tuple_like)
|
constexpr auto unpack(Callable&& obj, TupleLike&& tuple_like)
|
||||||
-> decltype(detail::unpack_impl(std::forward<Callable>(obj),
|
-> decltype(detail_unpack::unpack_impl(std::forward<Callable>(obj),
|
||||||
std::forward<TupleLike>(tuple_like),
|
std::forward<TupleLike>(tuple_like),
|
||||||
Sequence{})) {
|
Sequence{})) {
|
||||||
|
|
||||||
return detail::unpack_impl(std::forward<Callable>(obj),
|
return detail_unpack::unpack_impl(std::forward<Callable>(obj),
|
||||||
std::forward<TupleLike>(tuple_like), Sequence{});
|
std::forward<TupleLike>(tuple_like),
|
||||||
|
Sequence{});
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|||||||
@ -126,15 +126,17 @@ struct invocation_env {
|
|||||||
///
|
///
|
||||||
/// \note This function will assert statically if there is no way to call the
|
/// \note This function will assert statically if there is no way to call the
|
||||||
/// given object with less arguments.
|
/// given object with less arguments.
|
||||||
template <typename T, typename... Args>
|
template <std::size_t KeepArgs, typename T, typename... Args>
|
||||||
/*keep this inline*/ inline auto partial_invoke(T&& callable, Args&&... 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.
|
// Test whether we are able to call the function with the given arguments.
|
||||||
traits::is_invokable_from_tuple<decltype(callable), std::tuple<Args...>>
|
traits::is_invokable_from_tuple<decltype(callable), std::tuple<Args...>>
|
||||||
is_invokable;
|
is_invokable;
|
||||||
|
|
||||||
// The implementation is done in a shortcut way so there are less
|
// The implementation is done in a shortcut way so there are less
|
||||||
// type instantiations needed to call the callable with its full signature.
|
// 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(
|
return env::partial_invoke_impl_shortcut(
|
||||||
is_invokable, std::forward<T>(callable), std::forward<Args>(args)...);
|
is_invokable, std::forward<T>(callable), std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
#include <test-continuable.hpp>
|
#include <test-continuable.hpp>
|
||||||
|
|
||||||
|
using namespace cti;
|
||||||
|
|
||||||
TYPED_TEST(single_dimension_tests, are_completing_errors) {
|
TYPED_TEST(single_dimension_tests, are_completing_errors) {
|
||||||
ASSERT_ASYNC_EXCEPTION_COMPLETION(
|
ASSERT_ASYNC_EXCEPTION_COMPLETION(
|
||||||
this->supply_exception(supply_test_exception()));
|
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_ASYNC_INCOMPLETION(std::move(continuation));
|
||||||
ASSERT_TRUE(*handled);
|
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);
|
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