Initial work on routing the arguments correctly when using result<...>

This commit is contained in:
Denis Blank 2018-11-26 00:41:15 +01:00
parent 7767ce6fbb
commit ba9ff9fce0
3 changed files with 102 additions and 67 deletions

View File

@ -39,11 +39,23 @@
#include <continuable/detail/utility/traits.hpp> #include <continuable/detail/utility/traits.hpp>
namespace cti { namespace cti {
/// A class which is convertible to any result and that definitly holds no /// \defgroup Result Result
/// value so the real result gets invalidated when /// provides the \ref result class and corresponding utility functions to work
/// this object is passed to it /// with the result of an asynchronous operation which can possibly yield:
/// - *no result*: If the operation didn't finish
/// - *a value*: If the operation finished successfully
/// - *an exception*: If the operation finished with an exception
/// \{
/// A class which is convertible to any \ref result and that definitly holds no
/// value so the real result gets invalidated when this object is passed to it.
struct empty_result {}; struct empty_result {};
/// Returns a new empty result
inline empty_result make_empty_result() {
return {};
}
/// A class which is convertible to any result and that definitly holds /// A class which is convertible to any result and that definitly holds
/// an exception which is then passed to the converted result object. /// an exception which is then passed to the converted result object.
class exceptional_result { class exceptional_result {
@ -68,24 +80,38 @@ public:
return *this; return *this;
} }
/// Sets an exception
void set_exception(exception_t exception) { void set_exception(exception_t exception) {
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg) // NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
exception_ = std::move(exception); exception_ = std::move(exception);
} }
/// Returns the contained exception
exception_t& get_exception() & noexcept { exception_t& get_exception() & noexcept {
return exception_; return exception_;
} }
/// \copydoc get_exception
exception_t const& get_exception() const& noexcept { exception_t const& get_exception() const& noexcept {
return exception_; return exception_;
} }
/// \copydoc get_exception
exception_t&& get_exception() && noexcept { exception_t&& get_exception() && noexcept {
return std::move(exception_); return std::move(exception_);
} }
}; };
/// A class similar to the one in the result proposal, /// Returns a new exceptional result from the given exception
/// however it's capable of carrying an exception_t. // NOLINTNEXTLINE(performance-unnecessary-value-param)
inline exceptional_result make_exceptional_result(exception_t exception) {
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
return exceptional_result{std::move(exception)};
}
/// The result class can carry the three kinds of results an asynchronous
/// operation can return: no result, a value or an exception.
/// - *no result*: If the operation didn't finish
/// - *a value*: If the operation finished successfully
/// - *an exception*: If the operation finished with an exception
template <typename... T> template <typename... T>
class result { class result {
using trait_t = detail::result_trait<T...>; using trait_t = detail::result_trait<T...>;
@ -108,7 +134,8 @@ public:
} }
explicit result(exception_t exception) : variant_(std::move(exception)) { explicit result(exception_t exception) : variant_(std::move(exception)) {
} }
result(empty_result){}; result(empty_result) {
}
result(exceptional_result exceptional_result) result(exceptional_result exceptional_result)
: variant_(std::move(exceptional_result.get_exception())) { : variant_(std::move(exceptional_result.get_exception())) {
} }
@ -146,6 +173,7 @@ public:
return is_value(); return is_value();
} }
/// Returns the
decltype(auto) get_value() & noexcept { decltype(auto) get_value() & noexcept {
return trait_t::unwrap(variant_.template cast<surrogate_t>()); return trait_t::unwrap(variant_.template cast<surrogate_t>());
} }
@ -184,16 +212,7 @@ template <typename... T>
auto make_result(T&&... values) { auto make_result(T&&... values) {
return result<detail::traits::unrefcv_t<T>...>(std::forward<T>(values)...); return result<detail::traits::unrefcv_t<T>...>(std::forward<T>(values)...);
} }
/// \}
// NOLINTNEXTLINE(performance-unnecessary-value-param)
inline exceptional_result make_exceptional_result(exception_t exception) {
// NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg)
return exceptional_result{std::move(exception)};
}
inline empty_result make_empty_result() {
return {};
}
} // namespace cti } // namespace cti
#endif // CONTINUABLE_RESULT_HPP_INCLUDED #endif // CONTINUABLE_RESULT_HPP_INCLUDED

View File

@ -159,9 +159,9 @@ constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag<Args...>) {
} }
/// - continuable<?...> -> result(next_callback); /// - continuable<?...> -> result(next_callback);
template <typename Hint, typename Data, typename Annotation> template <typename Data, typename Annotation>
constexpr auto constexpr auto
invoker_of(Hint, traits::identity<continuable_base<Data, Annotation>>) { invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
/// Get the hint of the unwrapped returned continuable /// Get the hint of the unwrapped returned continuable
using Type = using Type =
decltype(std::declval<continuable_base<Data, Annotation>>().finish()); decltype(std::declval<continuable_base<Data, Annotation>>().finish());
@ -184,8 +184,8 @@ invoker_of(Hint, traits::identity<continuable_base<Data, Annotation>>) {
} }
/// - ? -> next_callback(?) /// - ? -> next_callback(?)
template <typename Hint, typename T> template <typename T>
constexpr auto invoker_of(Hint, traits::identity<T>) { constexpr auto invoker_of(traits::identity<T>) {
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
@ -201,8 +201,7 @@ constexpr auto invoker_of(Hint, traits::identity<T>) {
} }
/// - void -> next_callback() /// - void -> next_callback()
template <typename Hint> inline auto invoker_of(traits::identity<void>) {
auto invoker_of(Hint, 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
@ -216,52 +215,73 @@ auto invoker_of(Hint, traits::identity<void>) {
} }
/// - empty_result -> <cancel> /// - empty_result -> <cancel>
template <typename Hint> inline auto invoker_of(traits::identity<empty_result>) {
auto invoker_of(Hint, traits::identity<empty_result>) {
return make_invoker( return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) { [](auto&& callback, auto&& next_callback, auto&&... args) {
util::unused(callback, next_callback, args...); (void)next_callback;
// TODO CONTINUABLE_BLOCK_TRY_BEGIN
/*CONTINUABLE_BLOCK_TRY_BEGIN empty_result result =
util::partial_invoke(std::forward<decltype(callback)>(callback), util::partial_invoke(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)); // Don't invoke anything here since returning an empty result
CONTINUABLE_BLOCK_TRY_END*/ // cancels the asynchronous chain effectively.
(void)result;
CONTINUABLE_BLOCK_TRY_END
}, },
traits::identity<>{}); traits::identity<>{});
} }
/// - exceptional_result -> Hint /// - exceptional_result -> <throw>
template <typename Hint> inline auto invoker_of(traits::identity<exceptional_result>) {
auto invoker_of(Hint, traits::identity<exceptional_result>) {
return make_invoker( return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) { [](auto&& callback, auto&& next_callback, auto&&... args) {
util::unused(callback, next_callback, args...); util::unused(callback, next_callback, args...);
// TODO CONTINUABLE_BLOCK_TRY_BEGIN
/*CONTINUABLE_BLOCK_TRY_BEGIN exceptional_result result =
util::partial_invoke(std::forward<decltype(callback)>(callback), util::partial_invoke(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)); // Forward the exception to the next available handler
CONTINUABLE_BLOCK_TRY_END*/ invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
exception_arg_t{},
std::move(result).get_exception());
CONTINUABLE_BLOCK_TRY_END
}, },
traits::identity<>{}); traits::identity<>{});
} }
/// - result<Args...> -> Args... /// - result<?...> -> next_callback(?...)
template <typename Hint, typename... Args> template <typename... Args>
auto invoker_of(Hint, traits::identity<result<Args...>>) { auto invoker_of(traits::identity<result<Args...>>) {
return make_invoker( return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) { [](auto&& callback, auto&& next_callback, auto&&... args) {
util::unused(callback, next_callback, args...); CONTINUABLE_BLOCK_TRY_BEGIN
// TODO result<Args...> result =
/*CONTINUABLE_BLOCK_TRY_BEGIN util::partial_invoke(std::forward<decltype(callback)>(callback),
util::partial_invoke(std::forward<decltype(callback)>(callback), std::forward<decltype(args)>(args)...);
std::forward<decltype(args)>(args)...); //
invoke_no_except( if (result.is_value()) {
std::forward<decltype(next_callback)>(next_callback)); // Workaround for MSVC not capturing the reference
CONTINUABLE_BLOCK_TRY_END*/ // correctly inside the lambda.
using Next = decltype(next_callback);
traits::unpack(
[&](auto&&... types) {
/// TODO Add inplace resolution here
invoke_no_except(std::forward<Next>(next_callback),
std::forward<decltype(types)>(types)...);
},
std::move(result));
} else if (result.is_exception()) {
}
// Otherwise the result is empty and we are cancelling our
// asynchronous chain.
CONTINUABLE_BLOCK_TRY_END
}, },
traits::identity<Args...>{}); traits::identity<Args...>{});
} }
@ -275,8 +295,8 @@ inline auto sequenced_unpack_invoker() {
util::partial_invoke(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 correctly inside // Workaround for MSVC not capturing the reference
// the lambda. // correctly inside the lambda.
using Next = decltype(next_callback); using Next = decltype(next_callback);
traits::unpack( traits::unpack(
@ -292,15 +312,15 @@ inline auto sequenced_unpack_invoker() {
} // namespace decoration } // namespace decoration
// - std::pair<?, ?> -> next_callback(?, ?) // - std::pair<?, ?> -> next_callback(?, ?)
template <typename Hint, typename First, typename Second> template <typename First, typename Second>
constexpr auto invoker_of(Hint, traits::identity<std::pair<First, Second>>) { constexpr auto invoker_of(traits::identity<std::pair<First, Second>>) {
return make_invoker(sequenced_unpack_invoker(), return make_invoker(sequenced_unpack_invoker(),
traits::identity<First, Second>{}); traits::identity<First, Second>{});
} }
// - std::tuple<?...> -> next_callback(?...) // - std::tuple<?...> -> next_callback(?...)
template <typename Hint, typename... Args> template <typename... Args>
constexpr auto invoker_of(Hint, traits::identity<std::tuple<Args...>>) { constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{}); return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{});
} }
@ -379,9 +399,7 @@ struct result_handler_base<handle_results::yes, Base,
std::move(static_cast<Base*>(this)->callback_), std::move(args)...))>{}; 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 = auto invoker = decoration::invoker_of(result);
decoration::invoker_of(hints::signature_hint_tag<Args...>{}, //
result);
// Invoke the callback // Invoke the callback
packed_dispatch(std::move(static_cast<Base*>(this)->executor_), packed_dispatch(std::move(static_cast<Base*>(this)->executor_),
@ -547,15 +565,13 @@ template <typename T, typename... Args>
constexpr auto constexpr auto
next_hint_of(std::integral_constant<handle_results, handle_results::yes>, next_hint_of(std::integral_constant<handle_results, handle_results::yes>,
traits::identity<T> /*callback*/, traits::identity<T> /*callback*/,
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>()...)); util::partial_invoke(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(current, // return decltype(decoration::invoker_of(traits::identify<Result>{}).hint()){};
traits::identify<Result>{})
.hint()){};
} }
/// Don't progress the hint when we don't continue /// Don't progress the hint when we don't continue
template <typename T, typename... Args> template <typename T, typename... Args>

View File

@ -39,7 +39,7 @@ cti::continuable<> resolve_async(S&& supplier) {
co_await supplier(); co_await supplier();
// 1 args // 1 args
co_await supplier(1); int a1 = co_await supplier(1);
EXPECT_EQ(a1, 1); EXPECT_EQ(a1, 1);
// 2-n args // 2-n args