| // |
| // 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 |