mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
* Adapts the new naming scheme from the "Unified Futures" proposal * Provides new tags for the future inplace resolution
606 lines
22 KiB
C++
606 lines
22 KiB
C++
|
|
/*
|
|
|
|
/~` _ _ _|_. _ _ |_ | _
|
|
\_,(_)| | | || ||_|(_||_)|(/_
|
|
|
|
https://github.com/Naios/continuable
|
|
v3.0.0
|
|
|
|
Copyright(c) 2015 - 2018 Denis Blank <denis.blank at outlook dot com>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files(the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions :
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
**/
|
|
|
|
#ifndef CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
|
|
#define CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
|
|
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <continuable/continuable-primitives.hpp>
|
|
#include <continuable/detail/core/hints.hpp>
|
|
#include <continuable/detail/core/types.hpp>
|
|
#include <continuable/detail/features.hpp>
|
|
#include <continuable/detail/utility/traits.hpp>
|
|
#include <continuable/detail/utility/util.hpp>
|
|
|
|
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
|
#include <exception>
|
|
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
|
|
|
namespace cti {
|
|
namespace detail {
|
|
/// The namespace `base` provides the low level API for working
|
|
/// with continuable types.
|
|
///
|
|
/// Important methods are:
|
|
/// - Creating a continuation from a callback taking functional
|
|
/// base::attorney::create(auto&& callback)
|
|
/// -> base::continuation<auto>
|
|
/// - Chaining a continuation together with a callback
|
|
/// base::chain_continuation(base::continuation<auto> continuation,
|
|
/// auto&& callback)
|
|
/// -> base::continuation<auto>
|
|
/// - Finally invoking the continuation chain
|
|
/// base::finalize_continuation(base::continuation<auto> continuation)
|
|
/// -> void
|
|
namespace base {
|
|
template <typename T>
|
|
struct is_continuable : std::false_type {};
|
|
template <typename Data, typename Annotation>
|
|
struct is_continuable<continuable_base<Data, Annotation>> : std::true_type {};
|
|
|
|
/// Helper class to access private methods and members of
|
|
/// the continuable_base class.
|
|
struct attorney {
|
|
/// Makes a continuation wrapper from the given argument
|
|
template <typename T, typename A>
|
|
static auto create(T&& continuation, A /*hint*/, util::ownership ownership_) {
|
|
return continuable_base<std::decay_t<T>, std::decay_t<A>>(
|
|
std::forward<T>(continuation), ownership_);
|
|
}
|
|
|
|
/// Invokes a continuation object in a reference correct way
|
|
template <typename Data, typename Annotation, typename Callback>
|
|
static auto
|
|
invoke_continuation(continuable_base<Data, Annotation>&& continuation,
|
|
Callback&& callback) noexcept {
|
|
auto materialized = std::move(continuation).materialize();
|
|
materialized.release();
|
|
return materialized.data_(std::forward<Callback>(callback));
|
|
}
|
|
|
|
template <typename Data, typename Annotation>
|
|
static auto materialize(continuable_base<Data, Annotation>&& continuation) {
|
|
return std::move(continuation).materialize();
|
|
}
|
|
|
|
template <typename Data, typename Annotation>
|
|
static Data&&
|
|
consume_data(continuable_base<Data, Annotation>&& continuation) {
|
|
return std::move(continuation).consume_data();
|
|
}
|
|
|
|
template <typename Continuable>
|
|
static util::ownership ownership_of(Continuable&& continuation) noexcept {
|
|
return continuation.ownership_;
|
|
}
|
|
};
|
|
|
|
// Returns the invoker of a callback, the next callback
|
|
// and the arguments of the previous continuation.
|
|
//
|
|
// The return type of the invokerOf function matches a callable of:
|
|
// void(auto&& callback, auto&& next_callback, auto&&... args)
|
|
//
|
|
// The invoker decorates the result type in the following way
|
|
// - void -> next_callback()
|
|
// - ? -> next_callback(?)
|
|
// - std::pair<?, ?> -> next_callback(?, ?)
|
|
// - std::tuple<?...> -> next_callback(?...)
|
|
//
|
|
// When the result is a continuation itself pass the callback to it
|
|
// - continuation<?...> -> result(next_callback);
|
|
namespace decoration {
|
|
/// Helper class wrapping the underlaying unwrapping lambda
|
|
/// in order to extend it with a hint method.
|
|
template <typename T, typename Hint>
|
|
class invoker : public T {
|
|
public:
|
|
constexpr explicit invoker(T invoke) : T(std::move(invoke)) {
|
|
}
|
|
|
|
using T::operator();
|
|
|
|
/// Returns the underlaying signature hint
|
|
static constexpr Hint hint() noexcept {
|
|
return {};
|
|
}
|
|
};
|
|
|
|
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
|
#define CONTINUABLE_BLOCK_TRY_BEGIN try {
|
|
#define CONTINUABLE_BLOCK_TRY_END \
|
|
} \
|
|
catch (...) { \
|
|
std::forward<decltype(next_callback)>(next_callback)( \
|
|
exception_arg_t{}, std::current_exception()); \
|
|
}
|
|
|
|
#else // CONTINUABLE_HAS_EXCEPTIONS
|
|
#define CONTINUABLE_BLOCK_TRY_BEGIN {
|
|
#define CONTINUABLE_BLOCK_TRY_END }
|
|
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
|
|
|
/// Invokes the given callable object with the given arguments while
|
|
/// marking the operation as non exceptional.
|
|
template <typename T, typename... Args>
|
|
constexpr auto invoke_no_except(T&& callable, Args&&... args) noexcept {
|
|
return std::forward<T>(callable)(std::forward<Args>(args)...);
|
|
}
|
|
|
|
template <typename T, typename... Args>
|
|
constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag<Args...>) {
|
|
return invoker<std::decay_t<T>, hints::signature_hint_tag<Args...>>(
|
|
std::forward<T>(invoke));
|
|
}
|
|
|
|
/// - continuable<?...> -> result(next_callback);
|
|
template <typename Data, typename Annotation>
|
|
constexpr auto
|
|
invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
|
|
/// Get the hint of the unwrapped returned continuable
|
|
using Type = decltype(attorney::materialize(
|
|
std::declval<continuable_base<Data, Annotation>>()));
|
|
|
|
auto constexpr const hint = hints::hint_of(traits::identify<Type>{});
|
|
|
|
return make_invoker(
|
|
[](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)...);
|
|
|
|
attorney::invoke_continuation(
|
|
std::move(continuation_),
|
|
std::forward<decltype(next_callback)>(next_callback));
|
|
CONTINUABLE_BLOCK_TRY_END
|
|
},
|
|
hint);
|
|
}
|
|
|
|
/// - ? -> next_callback(?)
|
|
template <typename T>
|
|
constexpr auto invoker_of(traits::identity<T>) {
|
|
return make_invoker(
|
|
[](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_no_except(std::forward<decltype(next_callback)>(next_callback),
|
|
std::move(result));
|
|
CONTINUABLE_BLOCK_TRY_END
|
|
},
|
|
traits::identify<T>{});
|
|
}
|
|
|
|
/// - void -> next_callback()
|
|
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_no_except(
|
|
std::forward<decltype(next_callback)>(next_callback));
|
|
CONTINUABLE_BLOCK_TRY_END
|
|
},
|
|
traits::identity<>{});
|
|
}
|
|
|
|
/// Returns a sequenced invoker which is able to invoke
|
|
/// objects where std::get is applicable.
|
|
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)...);
|
|
|
|
// Workaround for MSVC not capturing the reference 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));
|
|
CONTINUABLE_BLOCK_TRY_END
|
|
};
|
|
} // namespace decoration
|
|
|
|
// - std::pair<?, ?> -> next_callback(?, ?)
|
|
template <typename First, typename Second>
|
|
constexpr auto invoker_of(traits::identity<std::pair<First, Second>>) {
|
|
return make_invoker(sequenced_unpack_invoker(),
|
|
traits::identity<First, Second>{});
|
|
}
|
|
|
|
// - std::tuple<?...> -> next_callback(?...)
|
|
template <typename... Args>
|
|
constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
|
|
return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{});
|
|
}
|
|
|
|
#undef CONTINUABLE_BLOCK_TRY_BEGIN
|
|
#undef CONTINUABLE_BLOCK_TRY_END
|
|
} // namespace decoration
|
|
|
|
/// Invoke the callback immediately
|
|
template <typename Invoker, typename... Args>
|
|
void packed_dispatch(types::this_thread_executor_tag, Invoker&& invoker,
|
|
Args&&... args) {
|
|
|
|
// Invoke the callback with the decorated invoker immediately
|
|
std::forward<Invoker>(invoker)(std::forward<Args>(args)...);
|
|
}
|
|
|
|
/// Invoke the callback through the given executor
|
|
template <typename Executor, typename Invoker, typename... Args>
|
|
void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) {
|
|
|
|
// Create a worker object which when invoked calls the callback with the
|
|
// the returned arguments.
|
|
auto work = [invoker = std::forward<Invoker>(invoker),
|
|
args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
|
|
traits::unpack(
|
|
[&](auto&&... captured_args) {
|
|
// Just use the packed dispatch method which dispatches the work on
|
|
// the current thread.
|
|
packed_dispatch(
|
|
types::this_thread_executor_tag{}, std::move(invoker),
|
|
std::forward<decltype(captured_args)>(captured_args)...);
|
|
},
|
|
std::move(args));
|
|
};
|
|
|
|
// Pass the work callable object to the executor
|
|
std::forward<Executor>(executor)(std::move(work));
|
|
}
|
|
|
|
/// Tells whether we potentially move the chain upwards and handle the result
|
|
enum class handle_results {
|
|
no, //< The result is forwarded to the next callable
|
|
yes //< The result is handled by the current callable
|
|
};
|
|
|
|
// Silences a doxygen bug, it tries to map forward to std::forward
|
|
/// \cond false
|
|
/// Tells whether we handle the error through the callback
|
|
enum class handle_errors {
|
|
no, //< The error is forwarded to the next callable
|
|
plain, //< The error is the only argument accepted by the callable
|
|
forward //< The error is forwarded to the callable while keeping its tag
|
|
};
|
|
/// \endcond
|
|
|
|
namespace callbacks {
|
|
namespace proto {
|
|
template <handle_results HandleResults, typename Base, typename Hint>
|
|
struct result_handler_base;
|
|
template <typename Base, typename... Args>
|
|
struct result_handler_base<handle_results::no, Base,
|
|
hints::signature_hint_tag<Args...>> {
|
|
void operator()(Args... args) && {
|
|
// Forward the arguments to the next callback
|
|
std::move(static_cast<Base*>(this)->next_callback_)(std::move(args)...);
|
|
}
|
|
};
|
|
template <typename Base, typename... Args>
|
|
struct result_handler_base<handle_results::yes, Base,
|
|
hints::signature_hint_tag<Args...>> {
|
|
/// The operator which is called when the result was provided
|
|
void operator()(Args... args) && {
|
|
// In order to retrieve the correct decorator we must know what the
|
|
// result type is.
|
|
auto result = traits::identify<decltype(util::partial_invoke(
|
|
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);
|
|
|
|
// Invoke the callback
|
|
packed_dispatch(std::move(static_cast<Base*>(this)->executor_),
|
|
std::move(invoker),
|
|
std::move(static_cast<Base*>(this)->callback_),
|
|
std::move(static_cast<Base*>(this)->next_callback_),
|
|
std::move(args)...);
|
|
}
|
|
};
|
|
|
|
inline auto make_error_invoker(
|
|
std::integral_constant<handle_errors, handle_errors::plain>) noexcept {
|
|
return [](auto&& callback, exception_t&& error) {
|
|
// Errors are not partial invoked
|
|
// NOLINTNEXTLINE(hicpp-move-const-arg)
|
|
std::forward<decltype(callback)>(callback)(std::move(error));
|
|
};
|
|
}
|
|
inline auto make_error_invoker(
|
|
std::integral_constant<handle_errors, handle_errors::forward>) noexcept {
|
|
return [](auto&& callback, exception_t&& error) {
|
|
// Errors are not partial invoked
|
|
std::forward<decltype(callback)>(callback)(
|
|
exception_arg_t{},
|
|
std::move(error)); // NOLINT(hicpp-move-const-arg)
|
|
};
|
|
}
|
|
|
|
template <handle_errors HandleErrors /* = plain or forward*/, typename Base>
|
|
struct error_handler_base {
|
|
void operator()(exception_arg_t, exception_t error) && {
|
|
// Just invoke the error handler, cancel the calling hierarchy after
|
|
auto invoker = make_error_invoker(
|
|
std::integral_constant<handle_errors, HandleErrors>{});
|
|
|
|
// Invoke the error handler
|
|
packed_dispatch(
|
|
std::move(static_cast<Base*>(this)->executor_), std::move(invoker),
|
|
std::move(static_cast<Base*>(this)->callback_), std::move(error));
|
|
}
|
|
};
|
|
template <typename Base>
|
|
struct error_handler_base<handle_errors::no, Base> {
|
|
/// The operator which is called when an error occurred
|
|
void operator()(exception_arg_t tag, exception_t error) && {
|
|
// Forward the error to the next callback
|
|
std::move(static_cast<Base*>(this)->next_callback_)(tag, std::move(error));
|
|
}
|
|
};
|
|
} // namespace proto
|
|
|
|
template <typename Hint, handle_results HandleResults,
|
|
handle_errors HandleErrors, typename Callback, typename Executor,
|
|
typename NextCallback>
|
|
struct callback_base;
|
|
|
|
template <typename... Args, handle_results HandleResults,
|
|
handle_errors HandleErrors, typename Callback, typename Executor,
|
|
typename NextCallback>
|
|
struct callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
|
HandleErrors, Callback, Executor, NextCallback>
|
|
: proto::result_handler_base<
|
|
HandleResults,
|
|
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
|
HandleErrors, Callback, Executor, NextCallback>,
|
|
hints::signature_hint_tag<Args...>>,
|
|
proto::error_handler_base<
|
|
HandleErrors,
|
|
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
|
HandleErrors, Callback, Executor, NextCallback>>,
|
|
util::non_copyable {
|
|
|
|
Callback callback_;
|
|
Executor executor_;
|
|
NextCallback next_callback_;
|
|
|
|
explicit callback_base(Callback callback, Executor executor,
|
|
NextCallback next_callback)
|
|
: callback_(std::move(callback)), executor_(std::move(executor)),
|
|
next_callback_(std::move(next_callback)) {
|
|
}
|
|
|
|
/// Pull the result handling operator() in
|
|
using proto::result_handler_base<
|
|
HandleResults,
|
|
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
|
HandleErrors, Callback, Executor, NextCallback>,
|
|
hints::signature_hint_tag<Args...>>::operator();
|
|
|
|
/// Pull the error handling operator() in
|
|
using proto::error_handler_base<
|
|
HandleErrors,
|
|
callback_base<hints::signature_hint_tag<Args...>, HandleResults,
|
|
HandleErrors, Callback, Executor, NextCallback>>::
|
|
operator();
|
|
|
|
/// Resolves the continuation with the given values
|
|
void set_value(Args... args) {
|
|
std::move (*this)(std::move(args)...);
|
|
}
|
|
|
|
/// Resolves the continuation with the given error variable.
|
|
void set_exception(exception_t error) {
|
|
std::move (*this)(exception_arg_t{}, std::move(error));
|
|
}
|
|
};
|
|
|
|
template <typename Hint, handle_results HandleResults,
|
|
handle_errors HandleErrors, typename Callback, typename Executor,
|
|
typename NextCallback>
|
|
auto make_callback(Callback&& callback, Executor&& executor,
|
|
NextCallback&& next_callback) {
|
|
return callback_base<Hint, HandleResults, HandleErrors,
|
|
std::decay_t<Callback>, std::decay_t<Executor>,
|
|
std::decay_t<NextCallback>>{
|
|
std::forward<Callback>(callback), std::forward<Executor>(executor),
|
|
std::forward<NextCallback>(next_callback)};
|
|
}
|
|
|
|
/// Once this was a workaround for GCC bug:
|
|
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
|
|
struct final_callback : util::non_copyable {
|
|
template <typename... Args>
|
|
void operator()(Args... /*args*/) && {
|
|
}
|
|
|
|
void operator()(exception_arg_t, exception_t error) && {
|
|
(void)error;
|
|
#ifndef CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS
|
|
// There were unhandled errors inside the asynchronous call chain!
|
|
// Define `CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS` in order
|
|
// to ignore unhandled errors!"
|
|
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
|
try {
|
|
std::rethrow_exception(error);
|
|
} catch (std::exception const& exception) {
|
|
(void)exception;
|
|
util::trap();
|
|
} catch (...) {
|
|
util::trap();
|
|
}
|
|
#else
|
|
util::trap();
|
|
#endif
|
|
#endif // CONTINUABLE_WITH_UNHANDLED_EXCEPTIONS
|
|
}
|
|
|
|
template <typename... Args>
|
|
void set_value(Args... args) {
|
|
std::move (*this)(std::forward<Args>(args)...);
|
|
}
|
|
|
|
void set_exception(exception_t error) {
|
|
// NOLINTNEXTLINE(hicpp-move-const-arg)
|
|
std::move (*this)(exception_arg_t{}, std::move(error));
|
|
}
|
|
};
|
|
} // namespace callbacks
|
|
|
|
/// Returns the next hint when the callback is invoked with the given hint
|
|
template <typename T, typename... Args>
|
|
constexpr auto
|
|
next_hint_of(std::integral_constant<handle_results, handle_results::yes>,
|
|
traits::identity<T> /*callback*/,
|
|
hints::signature_hint_tag<Args...> /*current*/) {
|
|
// Partial Invoke the given callback
|
|
using Result = decltype(
|
|
util::partial_invoke(std::declval<T>(), std::declval<Args>()...));
|
|
|
|
// Return the hint of thr given invoker
|
|
return decltype(decoration::invoker_of(traits::identify<Result>{}).hint()){};
|
|
}
|
|
/// Don't progress the hint when we don't continue
|
|
template <typename T, typename... Args>
|
|
constexpr auto
|
|
next_hint_of(std::integral_constant<handle_results, handle_results::no>,
|
|
traits::identity<T> /*callback*/,
|
|
hints::signature_hint_tag<Args...> current) {
|
|
return current;
|
|
}
|
|
|
|
/// Chains a callback together with a continuation and returns a continuation:
|
|
///
|
|
/// For example given:
|
|
/// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
|
|
/// - Callback: [](std::string) { }
|
|
///
|
|
/// This function returns a function accepting the next callback in the chain:
|
|
/// - Result: continuation<[](auto&& callback) { /*...*/ }>
|
|
///
|
|
template <handle_results HandleResults, handle_errors HandleErrors,
|
|
typename Continuation, typename Callback, typename Executor>
|
|
auto chain_continuation(Continuation&& continuation, Callback&& callback,
|
|
Executor&& executor) {
|
|
static_assert(is_continuable<std::decay_t<Continuation>>{},
|
|
"Expected a continuation!");
|
|
|
|
using Hint = decltype(hints::hint_of(traits::identify<Continuation>()));
|
|
constexpr auto next_hint =
|
|
next_hint_of(std::integral_constant<handle_results, HandleResults>{},
|
|
traits::identify<decltype(callback)>{}, Hint{});
|
|
|
|
// TODO consume only the data here so the freeze isn't needed
|
|
auto ownership_ = attorney::ownership_of(continuation);
|
|
continuation.freeze();
|
|
|
|
return attorney::create(
|
|
[continuation = std::forward<Continuation>(continuation),
|
|
callback = std::forward<Callback>(callback),
|
|
executor =
|
|
std::forward<Executor>(executor)](auto&& next_callback) mutable {
|
|
// Invokes a continuation with a given callback.
|
|
// Passes the next callback to the resulting continuable or
|
|
// invokes the next callback directly if possible.
|
|
//
|
|
// For example given:
|
|
// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
|
|
// - Callback: [](std::string) { }
|
|
// - NextCallback: []() { }
|
|
auto proxy =
|
|
callbacks::make_callback<Hint, HandleResults, HandleErrors>(
|
|
std::move(callback), std::move(executor),
|
|
std::forward<decltype(next_callback)>(next_callback));
|
|
|
|
// Invoke the continuation with a proxy callback.
|
|
// The proxy callback is responsible for passing
|
|
// the result to the callback as well as decorating it.
|
|
attorney::invoke_continuation(std::move(continuation),
|
|
std::move(proxy));
|
|
},
|
|
next_hint, ownership_);
|
|
}
|
|
|
|
/// Final invokes the given continuation chain:
|
|
///
|
|
/// For example given:
|
|
/// - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
|
|
template <typename Continuation>
|
|
void finalize_continuation(Continuation&& continuation) {
|
|
attorney::invoke_continuation(std::forward<Continuation>(continuation),
|
|
callbacks::final_callback{});
|
|
}
|
|
|
|
/// Workaround for GCC bug:
|
|
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
|
|
template <typename T>
|
|
class supplier_callback {
|
|
T data_;
|
|
|
|
public:
|
|
explicit supplier_callback(T data) : data_(std::move(data)) {
|
|
}
|
|
|
|
template <typename... Args>
|
|
auto operator()(Args...) {
|
|
return std::move(data_);
|
|
}
|
|
};
|
|
|
|
/// Returns a continuable into a callable object returning the continuable
|
|
template <typename Continuation>
|
|
auto wrap_continuation(Continuation&& continuation) {
|
|
continuation.freeze();
|
|
return supplier_callback<std::decay_t<Continuation>>(
|
|
std::forward<Continuation>(continuation));
|
|
}
|
|
} // namespace base
|
|
} // namespace detail
|
|
} // namespace cti
|
|
|
|
#endif // CONTINUABLE_DETAIL_BASE_HPP_INCLUDED
|