blob: f09be1582a3705879d6f813e8a7920f34d89f651 [file] [log] [blame]
//
// experimental/deferred.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef ASIO_EXPERIMENTAL_DEFERRED_HPP
#define ASIO_EXPERIMENTAL_DEFERRED_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include "asio/detail/config.hpp"
#include <tuple>
#include "asio/associator.hpp"
#include "asio/async_result.hpp"
#include "asio/detail/type_traits.hpp"
#include "asio/detail/push_options.hpp"
namespace asio {
namespace experimental {
/// Trait for detecting objects that are usable as deferred operations.
template <typename T>
struct is_deferred : false_type
{
};
namespace detail {
// Helper trait for getting the completion signature from an async operation.
struct deferred_signature_probe {};
template <typename T>
struct deferred_signature_probe_result
{
typedef T type;
};
template <typename T>
struct deferred_signature
{
typedef typename decltype(
declval<T>()(declval<deferred_signature_probe>()))::type type;
};
// Helper trait for getting the completion signature of the tail in a sequence
// when invoked with the specified arguments.
template <typename HeadSignature, typename Tail>
struct deferred_sequence_signature;
template <typename R, typename... Args, typename Tail>
struct deferred_sequence_signature<R(Args...), Tail>
{
static_assert(
!is_same<decltype(declval<Tail>()(declval<Args>()...)), void>::value,
"deferred functions must produce a deferred return type");
typedef typename decltype(
declval<Tail>()(declval<Args>()...)(
declval<deferred_signature_probe>()))::type type;
};
// Completion handler for the head component of a deferred sequence.
template <typename Handler, typename Tail>
class deferred_sequence_handler
{
public:
template <typename H, typename T>
explicit deferred_sequence_handler(
ASIO_MOVE_ARG(H) handler, ASIO_MOVE_ARG(T) tail)
: handler_(ASIO_MOVE_CAST(H)(handler)),
tail_(ASIO_MOVE_CAST(T)(tail))
{
}
template <typename... Args>
void operator()(ASIO_MOVE_ARG(Args)... args)
{
ASIO_MOVE_OR_LVALUE(Tail)(tail_)(
ASIO_MOVE_CAST(Args)(args)...)(
ASIO_MOVE_OR_LVALUE(Handler)(handler_));
}
//private:
Handler handler_;
Tail tail_;
};
} // namespace detail
/// Used to represent an empty deferred action.
struct deferred_noop
{
/// No effect.
template <typename... Args>
void operator()(ASIO_MOVE_ARG(Args)...) ASIO_RVALUE_REF_QUAL
{
}
#if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
/// No effect.
template <typename... Args>
decltype(auto) operator()(ASIO_MOVE_ARG(Args)...) const &
{
}
#endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
};
#if !defined(GENERATING_DOCUMENTATION)
template <>
struct is_deferred<deferred_noop> : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Tag type to disambiguate deferred constructors.
struct deferred_init_tag {};
/// Wraps a function object so that it may be used as an element in a deferred
/// composition.
template <typename Function>
class deferred_function
{
public:
/// Constructor.
template <typename F>
ASIO_CONSTEXPR explicit deferred_function(
deferred_init_tag, ASIO_MOVE_ARG(F) function)
: function_(ASIO_MOVE_CAST(F)(function))
{
}
template <typename... Args>
decltype(auto) operator()(
ASIO_MOVE_ARG(Args)... args) ASIO_RVALUE_REF_QUAL
{
return ASIO_MOVE_CAST(Function)(function_)(
ASIO_MOVE_CAST(Args)(args)...);
}
#if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <typename... Args>
decltype(auto) operator()(
ASIO_MOVE_ARG(Args)... args) const &
{
return deferred_function(*this)(
ASIO_MOVE_CAST(Args)(args)...);
}
#endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
//private:
Function function_;
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename Function>
struct is_deferred<deferred_function<Function> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Encapsulates deferred values.
template <typename... Values>
class ASIO_NODISCARD deferred_values
{
private:
std::tuple<Values...> values_;
struct initiate
{
template <typename Handler, typename... V>
void operator()(Handler handler, ASIO_MOVE_ARG(V)... values)
{
ASIO_MOVE_OR_LVALUE(Handler)(handler)(
ASIO_MOVE_CAST(V)(values)...);
}
};
template <typename CompletionToken, std::size_t... I>
decltype(auto) invoke_helper(
ASIO_MOVE_ARG(CompletionToken) token,
std::index_sequence<I...>)
{
return asio::async_initiate<CompletionToken, void(Values...)>(
initiate(), token,
std::get<I>(ASIO_MOVE_CAST(std::tuple<Values...>)(values_))...);
}
public:
/// Construct a deferred asynchronous operation from the arguments to an
/// initiation function object.
template <typename... V>
ASIO_CONSTEXPR explicit deferred_values(
deferred_init_tag, ASIO_MOVE_ARG(V)... values)
: values_(ASIO_MOVE_CAST(V)(values)...)
{
}
/// Initiate the deferred operation using the supplied completion token.
template <ASIO_COMPLETION_TOKEN_FOR(void(Values...)) CompletionToken>
decltype(auto) operator()(
ASIO_MOVE_ARG(CompletionToken) token) ASIO_RVALUE_REF_QUAL
{
return this->invoke_helper(
ASIO_MOVE_CAST(CompletionToken)(token),
std::index_sequence_for<Values...>());
}
#if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <ASIO_COMPLETION_TOKEN_FOR(void(Values...)) CompletionToken>
decltype(auto) operator()(
ASIO_MOVE_ARG(CompletionToken) token) const &
{
return deferred_values(*this)(
ASIO_MOVE_CAST(CompletionToken)(token));
}
#endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename... Values>
struct is_deferred<deferred_values<Values...> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Encapsulates a deferred asynchronous operation.
template <typename Signature, typename Initiation, typename... InitArgs>
class ASIO_NODISCARD deferred_async_operation
{
private:
typename decay<Initiation>::type initiation_;
typedef std::tuple<typename decay<InitArgs>::type...> init_args_t;
init_args_t init_args_;
template <typename CompletionToken, std::size_t... I>
decltype(auto) invoke_helper(
ASIO_MOVE_ARG(CompletionToken) token,
std::index_sequence<I...>)
{
return asio::async_initiate<CompletionToken, Signature>(
ASIO_MOVE_CAST(typename decay<Initiation>::type)(initiation_),
token, std::get<I>(ASIO_MOVE_CAST(init_args_t)(init_args_))...);
}
public:
/// Construct a deferred asynchronous operation from the arguments to an
/// initiation function object.
template <typename I, typename... A>
ASIO_CONSTEXPR explicit deferred_async_operation(
deferred_init_tag, ASIO_MOVE_ARG(I) initiation,
ASIO_MOVE_ARG(A)... init_args)
: initiation_(ASIO_MOVE_CAST(I)(initiation)),
init_args_(ASIO_MOVE_CAST(A)(init_args)...)
{
}
/// Initiate the asynchronous operation using the supplied completion token.
template <ASIO_COMPLETION_TOKEN_FOR(Signature) CompletionToken>
decltype(auto) operator()(
ASIO_MOVE_ARG(CompletionToken) token) ASIO_RVALUE_REF_QUAL
{
return this->invoke_helper(
ASIO_MOVE_CAST(CompletionToken)(token),
std::index_sequence_for<InitArgs...>());
}
#if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <ASIO_COMPLETION_TOKEN_FOR(Signature) CompletionToken>
decltype(auto) operator()(
ASIO_MOVE_ARG(CompletionToken) token) const &
{
return deferred_async_operation(*this)(
ASIO_MOVE_CAST(CompletionToken)(token));
}
#endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename Signature, typename Initiation, typename... InitArgs>
struct is_deferred<
deferred_async_operation<Signature, Initiation, InitArgs...> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Defines a link between two consecutive operations in a sequence.
template <typename Head, typename Tail>
class ASIO_NODISCARD deferred_sequence
{
private:
typedef typename detail::deferred_sequence_signature<
typename detail::deferred_signature<Head>::type, Tail>::type
signature;
public:
template <typename H, typename T>
ASIO_CONSTEXPR explicit deferred_sequence(deferred_init_tag,
ASIO_MOVE_ARG(H) head, ASIO_MOVE_ARG(T) tail)
: head_(ASIO_MOVE_CAST(H)(head)),
tail_(ASIO_MOVE_CAST(T)(tail))
{
}
template <ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
decltype(auto) operator()(
ASIO_MOVE_ARG(CompletionToken) token) ASIO_RVALUE_REF_QUAL
{
return asio::async_initiate<CompletionToken, signature>(
initiate(), token, ASIO_MOVE_OR_LVALUE(Head)(head_),
ASIO_MOVE_OR_LVALUE(Tail)(tail_));
}
#if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
decltype(auto) operator()(
ASIO_MOVE_ARG(CompletionToken) token) const &
{
return deferred_sequence(*this)(
ASIO_MOVE_CAST(CompletionToken)(token));
}
#endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
private:
struct initiate
{
template <typename Handler>
void operator()(ASIO_MOVE_ARG(Handler) handler,
Head head, ASIO_MOVE_ARG(Tail) tail)
{
ASIO_MOVE_OR_LVALUE(Head)(head)(
detail::deferred_sequence_handler<
typename decay<Handler>::type,
typename decay<Tail>::type>(
ASIO_MOVE_CAST(Handler)(handler),
ASIO_MOVE_CAST(Tail)(tail)));
}
};
Head head_;
Tail tail_;
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename Head, typename Tail>
struct is_deferred<deferred_sequence<Head, Tail> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Used to represent a deferred conditional branch.
template <typename OnTrue = deferred_noop,
typename OnFalse = deferred_noop>
class ASIO_NODISCARD deferred_conditional
{
public:
/// Construct a deferred conditional with the value to determine which branch
/// will be executed.
ASIO_CONSTEXPR explicit deferred_conditional(bool b)
: on_true_(),
on_false_(),
bool_(b)
{
}
/// Invoke the conditional branch bsaed on the stored alue.
template <typename... Args>
auto operator()(ASIO_MOVE_ARG(Args)... args) ASIO_RVALUE_REF_QUAL
{
if (bool_)
{
return ASIO_MOVE_OR_LVALUE(OnTrue)(on_true_)(
ASIO_MOVE_CAST(Args)(args)...);
}
else
{
return ASIO_MOVE_OR_LVALUE(OnFalse)(on_false_)(
ASIO_MOVE_CAST(Args)(args)...);
}
}
#if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
template <typename... Args>
auto operator()(ASIO_MOVE_ARG(Args)... args) const &
{
return deferred_conditional(*this)(
ASIO_MOVE_CAST(Args)(args)...);
}
#endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
/// Set the true branch of the conditional.
template <typename T>
deferred_conditional<T, OnFalse> then(T on_true,
typename constraint<
is_deferred<T>::value
>::type* = 0,
typename constraint<
is_same<
typename conditional<true, OnTrue, T>::type,
deferred_noop
>::value
>::type* = 0) ASIO_RVALUE_REF_QUAL
{
return deferred_conditional<T, OnFalse>(
bool_, ASIO_MOVE_CAST(T)(on_true),
ASIO_MOVE_CAST(OnFalse)(on_false_));
}
/// Set the false branch of the conditional.
template <typename T>
deferred_conditional<OnTrue, T> otherwise(T on_false,
typename constraint<
is_deferred<T>::value
>::type* = 0,
typename constraint<
!is_same<
typename conditional<true, OnTrue, T>::type,
deferred_noop
>::value
>::type* = 0,
typename constraint<
is_same<
typename conditional<true, OnFalse, T>::type,
deferred_noop
>::value
>::type* = 0) ASIO_RVALUE_REF_QUAL
{
return deferred_conditional<OnTrue, T>(
bool_, ASIO_MOVE_CAST(OnTrue)(on_true_),
ASIO_MOVE_CAST(T)(on_false));
}
private:
template <typename T, typename F> friend class deferred_conditional;
// Helper constructor.
template <typename T, typename F>
explicit deferred_conditional(bool b, ASIO_MOVE_ARG(T) on_true,
ASIO_MOVE_ARG(F) on_false)
: on_true_(ASIO_MOVE_CAST(T)(on_true)),
on_false_(ASIO_MOVE_CAST(F)(on_false)),
bool_(b)
{
}
OnTrue on_true_;
OnFalse on_false_;
bool bool_;
};
#if !defined(GENERATING_DOCUMENTATION)
template <typename OnTrue, typename OnFalse>
struct is_deferred<deferred_conditional<OnTrue, OnFalse> > : true_type
{
};
#endif // !defined(GENERATING_DOCUMENTATION)
/// Class used to specify that an asynchronous operation should return a
/// function object to lazily launch the operation.
/**
* The deferred_t class is used to indicate that an asynchronous operation
* should return a function object which is itself an initiation function. A
* deferred_t object may be passed as a completion token to an asynchronous
* operation, typically using the special value @c asio::deferred. For
* example:
*
* @code auto my_sender
* = my_socket.async_read_some(my_buffer,
* asio::experimental::deferred); @endcode
*
* The initiating function (async_read_some in the above example) returns a
* function object that will lazily initiate the operation.
*/
class deferred_t
{
public:
/// Default constructor.
ASIO_CONSTEXPR deferred_t()
{
}
/// Adapts an executor to add the @c deferred_t completion token as the
/// default.
template <typename InnerExecutor>
struct executor_with_default : InnerExecutor
{
/// Specify @c deferred_t as the default completion token type.
typedef deferred_t default_completion_token_type;
/// Construct the adapted executor from the inner executor type.
template <typename InnerExecutor1>
executor_with_default(const InnerExecutor1& ex,
typename constraint<
conditional<
!is_same<InnerExecutor1, executor_with_default>::value,
is_convertible<InnerExecutor1, InnerExecutor>,
false_type
>::type::value
>::type = 0) ASIO_NOEXCEPT
: InnerExecutor(ex)
{
}
};
/// Type alias to adapt an I/O object to use @c deferred_t as its
/// default completion token type.
#if defined(ASIO_HAS_ALIAS_TEMPLATES) \
|| defined(GENERATING_DOCUMENTATION)
template <typename T>
using as_default_on_t = typename T::template rebind_executor<
executor_with_default<typename T::executor_type> >::other;
#endif // defined(ASIO_HAS_ALIAS_TEMPLATES)
// || defined(GENERATING_DOCUMENTATION)
/// Function helper to adapt an I/O object to use @c deferred_t as its
/// default completion token type.
template <typename T>
static typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other
as_default_on(ASIO_MOVE_ARG(T) object)
{
return typename decay<T>::type::template rebind_executor<
executor_with_default<typename decay<T>::type::executor_type>
>::other(ASIO_MOVE_CAST(T)(object));
}
/// Creates a new deferred from a function.
template <typename Function>
typename constraint<
!is_deferred<typename decay<Function>::type>::value,
deferred_function<typename decay<Function>::type>
>::type operator()(ASIO_MOVE_ARG(Function) function) const
{
return deferred_function<typename decay<Function>::type>(
deferred_init_tag{}, ASIO_MOVE_CAST(Function)(function));
}
/// Passes through anything that is already deferred.
template <typename T>
typename constraint<
is_deferred<typename decay<T>::type>::value,
typename decay<T>::type
>::type operator()(ASIO_MOVE_ARG(T) t) const
{
return ASIO_MOVE_CAST(T)(t);
}
/// Returns a deferred operation that returns the provided values.
template <typename... Args>
static ASIO_CONSTEXPR deferred_values<typename decay<Args>::type...>
values(ASIO_MOVE_ARG(Args)... args)
{
return deferred_values<typename decay<Args>::type...>(
deferred_init_tag{}, ASIO_MOVE_CAST(Args)(args)...);
}
/// Creates a conditional object for branching deferred operations.
static ASIO_CONSTEXPR deferred_conditional<> when(bool b)
{
return deferred_conditional<>(b);
}
};
/// Pipe operator used to chain deferred operations.
template <typename Head, typename Tail>
inline auto operator|(Head head, ASIO_MOVE_ARG(Tail) tail)
-> typename constraint<
is_deferred<Head>::value,
decltype(ASIO_MOVE_OR_LVALUE(Head)(head)(
ASIO_MOVE_CAST(Tail)(tail)))
>::type
{
return ASIO_MOVE_OR_LVALUE(Head)(head)(
ASIO_MOVE_CAST(Tail)(tail));
}
/// A special value, similar to std::nothrow.
/**
* See the documentation for asio::experimental::deferred_t for a usage
* example.
*/
#if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION)
constexpr deferred_t deferred;
#elif defined(ASIO_MSVC)
__declspec(selectany) deferred_t deferred;
#endif
} // namespace experimental
} // namespace asio
#include "asio/detail/pop_options.hpp"
#include "asio/experimental/impl/deferred.hpp"
#endif // ASIO_EXPERIMENTAL_DEFERRED_HPP