deferred.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. //
  2. // experimental/deferred.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2022 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef ASIO_EXPERIMENTAL_DEFERRED_HPP
  11. #define ASIO_EXPERIMENTAL_DEFERRED_HPP
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. # pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include "asio/detail/config.hpp"
  16. #include <tuple>
  17. #include "asio/associator.hpp"
  18. #include "asio/async_result.hpp"
  19. #include "asio/detail/type_traits.hpp"
  20. #include "asio/detail/push_options.hpp"
  21. namespace asio {
  22. namespace experimental {
  23. /// Trait for detecting objects that are usable as deferred operations.
  24. template <typename T>
  25. struct is_deferred : false_type
  26. {
  27. };
  28. namespace detail {
  29. // Helper trait for getting the completion signature from an async operation.
  30. struct deferred_signature_probe {};
  31. template <typename T>
  32. struct deferred_signature_probe_result
  33. {
  34. typedef T type;
  35. };
  36. template <typename T>
  37. struct deferred_signature
  38. {
  39. typedef typename decltype(
  40. declval<T>()(declval<deferred_signature_probe>()))::type type;
  41. };
  42. // Helper trait for getting the completion signature of the tail in a sequence
  43. // when invoked with the specified arguments.
  44. template <typename HeadSignature, typename Tail>
  45. struct deferred_sequence_signature;
  46. template <typename R, typename... Args, typename Tail>
  47. struct deferred_sequence_signature<R(Args...), Tail>
  48. {
  49. static_assert(
  50. !is_same<decltype(declval<Tail>()(declval<Args>()...)), void>::value,
  51. "deferred functions must produce a deferred return type");
  52. typedef typename decltype(
  53. declval<Tail>()(declval<Args>()...)(
  54. declval<deferred_signature_probe>()))::type type;
  55. };
  56. // Completion handler for the head component of a deferred sequence.
  57. template <typename Handler, typename Tail>
  58. class deferred_sequence_handler
  59. {
  60. public:
  61. template <typename H, typename T>
  62. explicit deferred_sequence_handler(
  63. ASIO_MOVE_ARG(H) handler, ASIO_MOVE_ARG(T) tail)
  64. : handler_(ASIO_MOVE_CAST(H)(handler)),
  65. tail_(ASIO_MOVE_CAST(T)(tail))
  66. {
  67. }
  68. template <typename... Args>
  69. void operator()(ASIO_MOVE_ARG(Args)... args)
  70. {
  71. ASIO_MOVE_OR_LVALUE(Tail)(tail_)(
  72. ASIO_MOVE_CAST(Args)(args)...)(
  73. ASIO_MOVE_OR_LVALUE(Handler)(handler_));
  74. }
  75. //private:
  76. Handler handler_;
  77. Tail tail_;
  78. };
  79. } // namespace detail
  80. /// Used to represent an empty deferred action.
  81. struct deferred_noop
  82. {
  83. /// No effect.
  84. template <typename... Args>
  85. void operator()(ASIO_MOVE_ARG(Args)...) ASIO_RVALUE_REF_QUAL
  86. {
  87. }
  88. #if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  89. /// No effect.
  90. template <typename... Args>
  91. decltype(auto) operator()(ASIO_MOVE_ARG(Args)...) const &
  92. {
  93. }
  94. #endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  95. };
  96. #if !defined(GENERATING_DOCUMENTATION)
  97. template <>
  98. struct is_deferred<deferred_noop> : true_type
  99. {
  100. };
  101. #endif // !defined(GENERATING_DOCUMENTATION)
  102. /// Tag type to disambiguate deferred constructors.
  103. struct deferred_init_tag {};
  104. /// Wraps a function object so that it may be used as an element in a deferred
  105. /// composition.
  106. template <typename Function>
  107. class deferred_function
  108. {
  109. public:
  110. /// Constructor.
  111. template <typename F>
  112. ASIO_CONSTEXPR explicit deferred_function(
  113. deferred_init_tag, ASIO_MOVE_ARG(F) function)
  114. : function_(ASIO_MOVE_CAST(F)(function))
  115. {
  116. }
  117. template <typename... Args>
  118. decltype(auto) operator()(
  119. ASIO_MOVE_ARG(Args)... args) ASIO_RVALUE_REF_QUAL
  120. {
  121. return ASIO_MOVE_CAST(Function)(function_)(
  122. ASIO_MOVE_CAST(Args)(args)...);
  123. }
  124. #if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  125. template <typename... Args>
  126. decltype(auto) operator()(
  127. ASIO_MOVE_ARG(Args)... args) const &
  128. {
  129. return deferred_function(*this)(
  130. ASIO_MOVE_CAST(Args)(args)...);
  131. }
  132. #endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  133. //private:
  134. Function function_;
  135. };
  136. #if !defined(GENERATING_DOCUMENTATION)
  137. template <typename Function>
  138. struct is_deferred<deferred_function<Function> > : true_type
  139. {
  140. };
  141. #endif // !defined(GENERATING_DOCUMENTATION)
  142. /// Encapsulates deferred values.
  143. template <typename... Values>
  144. class ASIO_NODISCARD deferred_values
  145. {
  146. private:
  147. std::tuple<Values...> values_;
  148. struct initiate
  149. {
  150. template <typename Handler, typename... V>
  151. void operator()(Handler handler, ASIO_MOVE_ARG(V)... values)
  152. {
  153. ASIO_MOVE_OR_LVALUE(Handler)(handler)(
  154. ASIO_MOVE_CAST(V)(values)...);
  155. }
  156. };
  157. template <typename CompletionToken, std::size_t... I>
  158. decltype(auto) invoke_helper(
  159. ASIO_MOVE_ARG(CompletionToken) token,
  160. std::index_sequence<I...>)
  161. {
  162. return asio::async_initiate<CompletionToken, void(Values...)>(
  163. initiate(), token,
  164. std::get<I>(ASIO_MOVE_CAST(std::tuple<Values...>)(values_))...);
  165. }
  166. public:
  167. /// Construct a deferred asynchronous operation from the arguments to an
  168. /// initiation function object.
  169. template <typename... V>
  170. ASIO_CONSTEXPR explicit deferred_values(
  171. deferred_init_tag, ASIO_MOVE_ARG(V)... values)
  172. : values_(ASIO_MOVE_CAST(V)(values)...)
  173. {
  174. }
  175. /// Initiate the deferred operation using the supplied completion token.
  176. template <ASIO_COMPLETION_TOKEN_FOR(void(Values...)) CompletionToken>
  177. decltype(auto) operator()(
  178. ASIO_MOVE_ARG(CompletionToken) token) ASIO_RVALUE_REF_QUAL
  179. {
  180. return this->invoke_helper(
  181. ASIO_MOVE_CAST(CompletionToken)(token),
  182. std::index_sequence_for<Values...>());
  183. }
  184. #if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  185. template <ASIO_COMPLETION_TOKEN_FOR(void(Values...)) CompletionToken>
  186. decltype(auto) operator()(
  187. ASIO_MOVE_ARG(CompletionToken) token) const &
  188. {
  189. return deferred_values(*this)(
  190. ASIO_MOVE_CAST(CompletionToken)(token));
  191. }
  192. #endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  193. };
  194. #if !defined(GENERATING_DOCUMENTATION)
  195. template <typename... Values>
  196. struct is_deferred<deferred_values<Values...> > : true_type
  197. {
  198. };
  199. #endif // !defined(GENERATING_DOCUMENTATION)
  200. /// Encapsulates a deferred asynchronous operation.
  201. template <typename Signature, typename Initiation, typename... InitArgs>
  202. class ASIO_NODISCARD deferred_async_operation
  203. {
  204. private:
  205. typename decay<Initiation>::type initiation_;
  206. typedef std::tuple<typename decay<InitArgs>::type...> init_args_t;
  207. init_args_t init_args_;
  208. template <typename CompletionToken, std::size_t... I>
  209. decltype(auto) invoke_helper(
  210. ASIO_MOVE_ARG(CompletionToken) token,
  211. std::index_sequence<I...>)
  212. {
  213. return asio::async_initiate<CompletionToken, Signature>(
  214. ASIO_MOVE_CAST(typename decay<Initiation>::type)(initiation_),
  215. token, std::get<I>(ASIO_MOVE_CAST(init_args_t)(init_args_))...);
  216. }
  217. public:
  218. /// Construct a deferred asynchronous operation from the arguments to an
  219. /// initiation function object.
  220. template <typename I, typename... A>
  221. ASIO_CONSTEXPR explicit deferred_async_operation(
  222. deferred_init_tag, ASIO_MOVE_ARG(I) initiation,
  223. ASIO_MOVE_ARG(A)... init_args)
  224. : initiation_(ASIO_MOVE_CAST(I)(initiation)),
  225. init_args_(ASIO_MOVE_CAST(A)(init_args)...)
  226. {
  227. }
  228. /// Initiate the asynchronous operation using the supplied completion token.
  229. template <ASIO_COMPLETION_TOKEN_FOR(Signature) CompletionToken>
  230. decltype(auto) operator()(
  231. ASIO_MOVE_ARG(CompletionToken) token) ASIO_RVALUE_REF_QUAL
  232. {
  233. return this->invoke_helper(
  234. ASIO_MOVE_CAST(CompletionToken)(token),
  235. std::index_sequence_for<InitArgs...>());
  236. }
  237. #if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  238. template <ASIO_COMPLETION_TOKEN_FOR(Signature) CompletionToken>
  239. decltype(auto) operator()(
  240. ASIO_MOVE_ARG(CompletionToken) token) const &
  241. {
  242. return deferred_async_operation(*this)(
  243. ASIO_MOVE_CAST(CompletionToken)(token));
  244. }
  245. #endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  246. };
  247. #if !defined(GENERATING_DOCUMENTATION)
  248. template <typename Signature, typename Initiation, typename... InitArgs>
  249. struct is_deferred<
  250. deferred_async_operation<Signature, Initiation, InitArgs...> > : true_type
  251. {
  252. };
  253. #endif // !defined(GENERATING_DOCUMENTATION)
  254. /// Defines a link between two consecutive operations in a sequence.
  255. template <typename Head, typename Tail>
  256. class ASIO_NODISCARD deferred_sequence
  257. {
  258. private:
  259. typedef typename detail::deferred_sequence_signature<
  260. typename detail::deferred_signature<Head>::type, Tail>::type
  261. signature;
  262. public:
  263. template <typename H, typename T>
  264. ASIO_CONSTEXPR explicit deferred_sequence(deferred_init_tag,
  265. ASIO_MOVE_ARG(H) head, ASIO_MOVE_ARG(T) tail)
  266. : head_(ASIO_MOVE_CAST(H)(head)),
  267. tail_(ASIO_MOVE_CAST(T)(tail))
  268. {
  269. }
  270. template <ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
  271. decltype(auto) operator()(
  272. ASIO_MOVE_ARG(CompletionToken) token) ASIO_RVALUE_REF_QUAL
  273. {
  274. return asio::async_initiate<CompletionToken, signature>(
  275. initiate(), token, ASIO_MOVE_OR_LVALUE(Head)(head_),
  276. ASIO_MOVE_OR_LVALUE(Tail)(tail_));
  277. }
  278. #if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  279. template <ASIO_COMPLETION_TOKEN_FOR(signature) CompletionToken>
  280. decltype(auto) operator()(
  281. ASIO_MOVE_ARG(CompletionToken) token) const &
  282. {
  283. return deferred_sequence(*this)(
  284. ASIO_MOVE_CAST(CompletionToken)(token));
  285. }
  286. #endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  287. private:
  288. struct initiate
  289. {
  290. template <typename Handler>
  291. void operator()(ASIO_MOVE_ARG(Handler) handler,
  292. Head head, ASIO_MOVE_ARG(Tail) tail)
  293. {
  294. ASIO_MOVE_OR_LVALUE(Head)(head)(
  295. detail::deferred_sequence_handler<
  296. typename decay<Handler>::type,
  297. typename decay<Tail>::type>(
  298. ASIO_MOVE_CAST(Handler)(handler),
  299. ASIO_MOVE_CAST(Tail)(tail)));
  300. }
  301. };
  302. Head head_;
  303. Tail tail_;
  304. };
  305. #if !defined(GENERATING_DOCUMENTATION)
  306. template <typename Head, typename Tail>
  307. struct is_deferred<deferred_sequence<Head, Tail> > : true_type
  308. {
  309. };
  310. #endif // !defined(GENERATING_DOCUMENTATION)
  311. /// Used to represent a deferred conditional branch.
  312. template <typename OnTrue = deferred_noop,
  313. typename OnFalse = deferred_noop>
  314. class ASIO_NODISCARD deferred_conditional
  315. {
  316. public:
  317. /// Construct a deferred conditional with the value to determine which branch
  318. /// will be executed.
  319. ASIO_CONSTEXPR explicit deferred_conditional(bool b)
  320. : on_true_(),
  321. on_false_(),
  322. bool_(b)
  323. {
  324. }
  325. /// Invoke the conditional branch bsaed on the stored alue.
  326. template <typename... Args>
  327. auto operator()(ASIO_MOVE_ARG(Args)... args) ASIO_RVALUE_REF_QUAL
  328. {
  329. if (bool_)
  330. {
  331. return ASIO_MOVE_OR_LVALUE(OnTrue)(on_true_)(
  332. ASIO_MOVE_CAST(Args)(args)...);
  333. }
  334. else
  335. {
  336. return ASIO_MOVE_OR_LVALUE(OnFalse)(on_false_)(
  337. ASIO_MOVE_CAST(Args)(args)...);
  338. }
  339. }
  340. #if defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  341. template <typename... Args>
  342. auto operator()(ASIO_MOVE_ARG(Args)... args) const &
  343. {
  344. return deferred_conditional(*this)(
  345. ASIO_MOVE_CAST(Args)(args)...);
  346. }
  347. #endif // defined(ASIO_HAS_REF_QUALIFIED_FUNCTIONS)
  348. /// Set the true branch of the conditional.
  349. template <typename T>
  350. deferred_conditional<T, OnFalse> then(T on_true,
  351. typename constraint<
  352. is_deferred<T>::value
  353. >::type* = 0,
  354. typename constraint<
  355. is_same<
  356. typename conditional<true, OnTrue, T>::type,
  357. deferred_noop
  358. >::value
  359. >::type* = 0) ASIO_RVALUE_REF_QUAL
  360. {
  361. return deferred_conditional<T, OnFalse>(
  362. bool_, ASIO_MOVE_CAST(T)(on_true),
  363. ASIO_MOVE_CAST(OnFalse)(on_false_));
  364. }
  365. /// Set the false branch of the conditional.
  366. template <typename T>
  367. deferred_conditional<OnTrue, T> otherwise(T on_false,
  368. typename constraint<
  369. is_deferred<T>::value
  370. >::type* = 0,
  371. typename constraint<
  372. !is_same<
  373. typename conditional<true, OnTrue, T>::type,
  374. deferred_noop
  375. >::value
  376. >::type* = 0,
  377. typename constraint<
  378. is_same<
  379. typename conditional<true, OnFalse, T>::type,
  380. deferred_noop
  381. >::value
  382. >::type* = 0) ASIO_RVALUE_REF_QUAL
  383. {
  384. return deferred_conditional<OnTrue, T>(
  385. bool_, ASIO_MOVE_CAST(OnTrue)(on_true_),
  386. ASIO_MOVE_CAST(T)(on_false));
  387. }
  388. private:
  389. template <typename T, typename F> friend class deferred_conditional;
  390. // Helper constructor.
  391. template <typename T, typename F>
  392. explicit deferred_conditional(bool b, ASIO_MOVE_ARG(T) on_true,
  393. ASIO_MOVE_ARG(F) on_false)
  394. : on_true_(ASIO_MOVE_CAST(T)(on_true)),
  395. on_false_(ASIO_MOVE_CAST(F)(on_false)),
  396. bool_(b)
  397. {
  398. }
  399. OnTrue on_true_;
  400. OnFalse on_false_;
  401. bool bool_;
  402. };
  403. #if !defined(GENERATING_DOCUMENTATION)
  404. template <typename OnTrue, typename OnFalse>
  405. struct is_deferred<deferred_conditional<OnTrue, OnFalse> > : true_type
  406. {
  407. };
  408. #endif // !defined(GENERATING_DOCUMENTATION)
  409. /// Class used to specify that an asynchronous operation should return a
  410. /// function object to lazily launch the operation.
  411. /**
  412. * The deferred_t class is used to indicate that an asynchronous operation
  413. * should return a function object which is itself an initiation function. A
  414. * deferred_t object may be passed as a completion token to an asynchronous
  415. * operation, typically using the special value @c asio::deferred. For
  416. * example:
  417. *
  418. * @code auto my_sender
  419. * = my_socket.async_read_some(my_buffer,
  420. * asio::experimental::deferred); @endcode
  421. *
  422. * The initiating function (async_read_some in the above example) returns a
  423. * function object that will lazily initiate the operation.
  424. */
  425. class deferred_t
  426. {
  427. public:
  428. /// Default constructor.
  429. ASIO_CONSTEXPR deferred_t()
  430. {
  431. }
  432. /// Adapts an executor to add the @c deferred_t completion token as the
  433. /// default.
  434. template <typename InnerExecutor>
  435. struct executor_with_default : InnerExecutor
  436. {
  437. /// Specify @c deferred_t as the default completion token type.
  438. typedef deferred_t default_completion_token_type;
  439. /// Construct the adapted executor from the inner executor type.
  440. template <typename InnerExecutor1>
  441. executor_with_default(const InnerExecutor1& ex,
  442. typename constraint<
  443. conditional<
  444. !is_same<InnerExecutor1, executor_with_default>::value,
  445. is_convertible<InnerExecutor1, InnerExecutor>,
  446. false_type
  447. >::type::value
  448. >::type = 0) ASIO_NOEXCEPT
  449. : InnerExecutor(ex)
  450. {
  451. }
  452. };
  453. /// Type alias to adapt an I/O object to use @c deferred_t as its
  454. /// default completion token type.
  455. #if defined(ASIO_HAS_ALIAS_TEMPLATES) \
  456. || defined(GENERATING_DOCUMENTATION)
  457. template <typename T>
  458. using as_default_on_t = typename T::template rebind_executor<
  459. executor_with_default<typename T::executor_type> >::other;
  460. #endif // defined(ASIO_HAS_ALIAS_TEMPLATES)
  461. // || defined(GENERATING_DOCUMENTATION)
  462. /// Function helper to adapt an I/O object to use @c deferred_t as its
  463. /// default completion token type.
  464. template <typename T>
  465. static typename decay<T>::type::template rebind_executor<
  466. executor_with_default<typename decay<T>::type::executor_type>
  467. >::other
  468. as_default_on(ASIO_MOVE_ARG(T) object)
  469. {
  470. return typename decay<T>::type::template rebind_executor<
  471. executor_with_default<typename decay<T>::type::executor_type>
  472. >::other(ASIO_MOVE_CAST(T)(object));
  473. }
  474. /// Creates a new deferred from a function.
  475. template <typename Function>
  476. typename constraint<
  477. !is_deferred<typename decay<Function>::type>::value,
  478. deferred_function<typename decay<Function>::type>
  479. >::type operator()(ASIO_MOVE_ARG(Function) function) const
  480. {
  481. return deferred_function<typename decay<Function>::type>(
  482. deferred_init_tag{}, ASIO_MOVE_CAST(Function)(function));
  483. }
  484. /// Passes through anything that is already deferred.
  485. template <typename T>
  486. typename constraint<
  487. is_deferred<typename decay<T>::type>::value,
  488. typename decay<T>::type
  489. >::type operator()(ASIO_MOVE_ARG(T) t) const
  490. {
  491. return ASIO_MOVE_CAST(T)(t);
  492. }
  493. /// Returns a deferred operation that returns the provided values.
  494. template <typename... Args>
  495. static ASIO_CONSTEXPR deferred_values<typename decay<Args>::type...>
  496. values(ASIO_MOVE_ARG(Args)... args)
  497. {
  498. return deferred_values<typename decay<Args>::type...>(
  499. deferred_init_tag{}, ASIO_MOVE_CAST(Args)(args)...);
  500. }
  501. /// Creates a conditional object for branching deferred operations.
  502. static ASIO_CONSTEXPR deferred_conditional<> when(bool b)
  503. {
  504. return deferred_conditional<>(b);
  505. }
  506. };
  507. /// Pipe operator used to chain deferred operations.
  508. template <typename Head, typename Tail>
  509. inline auto operator|(Head head, ASIO_MOVE_ARG(Tail) tail)
  510. -> typename constraint<
  511. is_deferred<Head>::value,
  512. decltype(ASIO_MOVE_OR_LVALUE(Head)(head)(
  513. ASIO_MOVE_CAST(Tail)(tail)))
  514. >::type
  515. {
  516. return ASIO_MOVE_OR_LVALUE(Head)(head)(
  517. ASIO_MOVE_CAST(Tail)(tail));
  518. }
  519. /// A special value, similar to std::nothrow.
  520. /**
  521. * See the documentation for asio::experimental::deferred_t for a usage
  522. * example.
  523. */
  524. #if defined(ASIO_HAS_CONSTEXPR) || defined(GENERATING_DOCUMENTATION)
  525. constexpr deferred_t deferred;
  526. #elif defined(ASIO_MSVC)
  527. __declspec(selectany) deferred_t deferred;
  528. #endif
  529. } // namespace experimental
  530. } // namespace asio
  531. #include "asio/detail/pop_options.hpp"
  532. #include "asio/experimental/impl/deferred.hpp"
  533. #endif // ASIO_EXPERIMENTAL_DEFERRED_HPP