// // deferred.hpp // ~~~~~~~~~~~~ // // Copyright (c) 2003-2024 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_DEFERRED_HPP #define ASIO_DEFERRED_HPP #if defined(_MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include "asio/detail/config.hpp" #include #include "asio/associator.hpp" #include "asio/async_result.hpp" #include "asio/detail/type_traits.hpp" #include "asio/detail/utility.hpp" #include "asio/detail/push_options.hpp" namespace asio { /// Trait for detecting objects that are usable as deferred operations. template struct is_deferred : false_type { }; /// Helper type to wrap multiple completion signatures. template struct deferred_signatures { }; namespace detail { // Helper trait for getting the completion signatures of the tail in a sequence // when invoked with the specified arguments. template struct deferred_sequence_signatures; template struct deferred_sequence_signatures : completion_signature_of()(declval()...))> { static_assert( !is_same()(declval()...)), void>::value, "deferred functions must produce a deferred return type"); }; // Completion handler for the head component of a deferred sequence. template class deferred_sequence_handler { public: template explicit deferred_sequence_handler(H&& handler, T&& tail) : handler_(static_cast(handler)), tail_(static_cast(tail)) { } template void operator()(Args&&... args) { static_cast(tail_)( static_cast(args)...)( static_cast(handler_)); } //private: Handler handler_; Tail tail_; }; template class deferred_sequence_base { private: struct initiate { template void operator()(Handler&& handler, Head head, Tail&& tail) { static_cast(head)( deferred_sequence_handler, decay_t>( static_cast(handler), static_cast(tail))); } }; Head head_; Tail tail_; public: template constexpr explicit deferred_sequence_base(H&& head, T&& tail) : head_(static_cast(head)), tail_(static_cast(tail)) { } template auto operator()(CompletionToken&& token) && -> decltype( async_initiate( initiate(), token, static_cast(this->head_), static_cast(this->tail_))) { return async_initiate(initiate(), token, static_cast(head_), static_cast(tail_)); } template auto operator()(CompletionToken&& token) const & -> decltype( async_initiate( initiate(), token, this->head_, this->tail_)) { return async_initiate( initiate(), token, head_, tail_); } }; // Two-step application of variadic Signatures to determine correct base type. template struct deferred_sequence_types { template struct op1 { typedef deferred_sequence_base type; }; template struct op2 { typedef typename deferred_sequence_signatures::template apply::type::type type; }; typedef typename completion_signature_of::template apply::type::type base; }; } // namespace detail /// Used to represent an empty deferred action. struct deferred_noop { /// No effect. template void operator()(Args&&...) && { } /// No effect. template void operator()(Args&&...) const & { } }; #if !defined(GENERATING_DOCUMENTATION) template <> struct is_deferred : 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 class deferred_function { public: /// Constructor. template constexpr explicit deferred_function(deferred_init_tag, F&& function) : function_(static_cast(function)) { } //private: Function function_; public: template auto operator()(Args&&... args) && -> decltype( static_cast(this->function_)(static_cast(args)...)) { return static_cast(function_)(static_cast(args)...); } template auto operator()(Args&&... args) const & -> decltype(Function(function_)(static_cast(args)...)) { return Function(function_)(static_cast(args)...); } }; #if !defined(GENERATING_DOCUMENTATION) template struct is_deferred> : true_type { }; #endif // !defined(GENERATING_DOCUMENTATION) /// Encapsulates deferred values. template class ASIO_NODISCARD deferred_values { private: std::tuple values_; struct initiate { template void operator()(Handler handler, V&&... values) { static_cast(handler)(static_cast(values)...); } }; template auto invoke_helper(CompletionToken&& token, detail::index_sequence) -> decltype( async_initiate(initiate(), token, std::get(static_cast&&>(this->values_))...)) { return async_initiate(initiate(), token, std::get(static_cast&&>(values_))...); } template auto const_invoke_helper(CompletionToken&& token, detail::index_sequence) -> decltype( async_initiate( initiate(), token, std::get(values_)...)) { return async_initiate( initiate(), token, std::get(values_)...); } public: /// Construct a deferred asynchronous operation from the arguments to an /// initiation function object. template constexpr explicit deferred_values( deferred_init_tag, V&&... values) : values_(static_cast(values)...) { } /// Initiate the deferred operation using the supplied completion token. template auto operator()(CompletionToken&& token) && -> decltype( this->invoke_helper( static_cast(token), detail::index_sequence_for())) { return this->invoke_helper( static_cast(token), detail::index_sequence_for()); } template auto operator()(CompletionToken&& token) const & -> decltype( this->const_invoke_helper( static_cast(token), detail::index_sequence_for())) { return this->const_invoke_helper( static_cast(token), detail::index_sequence_for()); } }; #if !defined(GENERATING_DOCUMENTATION) template struct is_deferred> : true_type { }; #endif // !defined(GENERATING_DOCUMENTATION) /// Encapsulates a deferred asynchronous operation. template class ASIO_NODISCARD deferred_async_operation { private: typedef decay_t initiation_t; initiation_t initiation_; typedef std::tuple...> init_args_t; init_args_t init_args_; template auto invoke_helper(CompletionToken&& token, detail::index_sequence) -> decltype( async_initiate( static_cast(initiation_), token, std::get(static_cast(init_args_))...)) { return async_initiate( static_cast(initiation_), token, std::get(static_cast(init_args_))...); } template auto const_invoke_helper(CompletionToken&& token, detail::index_sequence) const & -> decltype( async_initiate( initiation_t(initiation_), token, std::get(init_args_)...)) { return async_initiate( initiation_t(initiation_), token, std::get(init_args_)...); } public: /// Construct a deferred asynchronous operation from the arguments to an /// initiation function object. template constexpr explicit deferred_async_operation( deferred_init_tag, I&& initiation, A&&... init_args) : initiation_(static_cast(initiation)), init_args_(static_cast(init_args)...) { } /// Initiate the asynchronous operation using the supplied completion token. template auto operator()(CompletionToken&& token) && -> decltype( this->invoke_helper( static_cast(token), detail::index_sequence_for())) { return this->invoke_helper( static_cast(token), detail::index_sequence_for()); } template auto operator()(CompletionToken&& token) const & -> decltype( this->const_invoke_helper( static_cast(token), detail::index_sequence_for())) { return this->const_invoke_helper( static_cast(token), detail::index_sequence_for()); } }; /// Encapsulates a deferred asynchronous operation thas has multiple completion /// signatures. template class ASIO_NODISCARD deferred_async_operation< deferred_signatures, Initiation, InitArgs...> { private: typedef decay_t initiation_t; initiation_t initiation_; typedef std::tuple...> init_args_t; init_args_t init_args_; template auto invoke_helper(CompletionToken&& token, detail::index_sequence) -> decltype( async_initiate( static_cast(initiation_), token, std::get(static_cast(init_args_))...)) { return async_initiate( static_cast(initiation_), token, std::get(static_cast(init_args_))...); } template auto const_invoke_helper(CompletionToken&& token, detail::index_sequence) const & -> decltype( async_initiate( initiation_t(initiation_), token, std::get(init_args_)...)) { return async_initiate( initiation_t(initiation_), token, std::get(init_args_)...); } public: /// Construct a deferred asynchronous operation from the arguments to an /// initiation function object. template constexpr explicit deferred_async_operation( deferred_init_tag, I&& initiation, A&&... init_args) : initiation_(static_cast(initiation)), init_args_(static_cast(init_args)...) { } /// Initiate the asynchronous operation using the supplied completion token. template auto operator()(CompletionToken&& token) && -> decltype( this->invoke_helper( static_cast(token), detail::index_sequence_for())) { return this->invoke_helper( static_cast(token), detail::index_sequence_for()); } template auto operator()(CompletionToken&& token) const & -> decltype( this->const_invoke_helper( static_cast(token), detail::index_sequence_for())) { return this->const_invoke_helper( static_cast(token), detail::index_sequence_for()); } }; #if !defined(GENERATING_DOCUMENTATION) template struct is_deferred< deferred_async_operation> : true_type { }; #endif // !defined(GENERATING_DOCUMENTATION) /// Defines a link between two consecutive operations in a sequence. template class ASIO_NODISCARD deferred_sequence : public detail::deferred_sequence_types::base { public: template constexpr explicit deferred_sequence(deferred_init_tag, H&& head, T&& tail) : detail::deferred_sequence_types::base( static_cast(head), static_cast(tail)) { } #if defined(GENERATING_DOCUMENTATION) template auto operator()(CompletionToken&& token) &&; template auto operator()(CompletionToken&& token) const &; #endif // defined(GENERATING_DOCUMENTATION) }; #if !defined(GENERATING_DOCUMENTATION) template struct is_deferred> : true_type { }; #endif // !defined(GENERATING_DOCUMENTATION) /// Used to represent a deferred conditional branch. template class ASIO_NODISCARD deferred_conditional { private: template friend class deferred_conditional; // Helper constructor. template explicit deferred_conditional(bool b, T&& on_true, F&& on_false) : on_true_(static_cast(on_true)), on_false_(static_cast(on_false)), bool_(b) { } OnTrue on_true_; OnFalse on_false_; bool bool_; public: /// Construct a deferred conditional with the value to determine which branch /// will be executed. constexpr explicit deferred_conditional(bool b) : on_true_(), on_false_(), bool_(b) { } /// Invoke the conditional branch bsaed on the stored value. template auto operator()(Args&&... args) && -> decltype(static_cast(on_true_)(static_cast(args)...)) { if (bool_) { return static_cast(on_true_)(static_cast(args)...); } else { return static_cast(on_false_)(static_cast(args)...); } } template auto operator()(Args&&... args) const & -> decltype(on_true_(static_cast(args)...)) { if (bool_) { return on_true_(static_cast(args)...); } else { return on_false_(static_cast(args)...); } } /// Set the true branch of the conditional. template deferred_conditional then(T on_true, constraint_t< is_deferred::value >* = 0, constraint_t< is_same< conditional_t, deferred_noop >::value >* = 0) && { return deferred_conditional( bool_, static_cast(on_true), static_cast(on_false_)); } /// Set the false branch of the conditional. template deferred_conditional otherwise(T on_false, constraint_t< is_deferred::value >* = 0, constraint_t< !is_same< conditional_t, deferred_noop >::value >* = 0, constraint_t< is_same< conditional_t, deferred_noop >::value >* = 0) && { return deferred_conditional( bool_, static_cast(on_true_), static_cast(on_false)); } }; #if !defined(GENERATING_DOCUMENTATION) template struct is_deferred> : 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_deferred_op * = my_socket.async_read_some(my_buffer, * asio::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. constexpr deferred_t() { } /// Adapts an executor to add the @c deferred_t completion token as the /// default. template 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 executor_with_default(const InnerExecutor1& ex, constraint_t< conditional_t< !is_same::value, is_convertible, false_type >::value > = 0) noexcept : InnerExecutor(ex) { } }; /// Type alias to adapt an I/O object to use @c deferred_t as its /// default completion token type. template using as_default_on_t = typename T::template rebind_executor< executor_with_default>::other; /// Function helper to adapt an I/O object to use @c deferred_t as its /// default completion token type. template static typename decay_t::template rebind_executor< executor_with_default::executor_type> >::other as_default_on(T&& object) { return typename decay_t::template rebind_executor< executor_with_default::executor_type> >::other(static_cast(object)); } /// Creates a new deferred from a function. template constraint_t< !is_deferred>::value, deferred_function> > operator()(Function&& function) const { return deferred_function>( deferred_init_tag{}, static_cast(function)); } /// Passes through anything that is already deferred. template constraint_t< is_deferred>::value, decay_t > operator()(T&& t) const { return static_cast(t); } /// Returns a deferred operation that returns the provided values. template static constexpr deferred_values...> values(Args&&... args) { return deferred_values...>( deferred_init_tag{}, static_cast(args)...); } /// Creates a conditional object for branching deferred operations. static constexpr deferred_conditional<> when(bool b) { return deferred_conditional<>(b); } }; /// Pipe operator used to chain deferred operations. template inline auto operator|(Head head, Tail&& tail) -> constraint_t< is_deferred::value, decltype(static_cast(head)(static_cast(tail))) > { return static_cast(head)(static_cast(tail)); } /// A @ref completion_token object used to specify that an asynchronous /// operation should return a function object to lazily launch the operation. /** * See the documentation for asio::deferred_t for a usage example. */ constexpr deferred_t deferred; } // namespace asio #include "asio/detail/pop_options.hpp" #include "asio/impl/deferred.hpp" #endif // ASIO_DEFERRED_HPP