1 //
2 // bind_executor.hpp
3 // ~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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 
11 #ifndef BOOST_ASIO_BIND_EXECUTOR_HPP
12 #define BOOST_ASIO_BIND_EXECUTOR_HPP
13 
14 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
15 # pragma once
16 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
17 
18 #include <boost/asio/detail/config.hpp>
19 #include <boost/asio/detail/type_traits.hpp>
20 #include <boost/asio/detail/variadic_templates.hpp>
21 #include <boost/asio/associated_executor.hpp>
22 #include <boost/asio/associated_allocator.hpp>
23 #include <boost/asio/async_result.hpp>
24 #include <boost/asio/execution/executor.hpp>
25 #include <boost/asio/execution_context.hpp>
26 #include <boost/asio/is_executor.hpp>
27 #include <boost/asio/uses_executor.hpp>
28 
29 #include <boost/asio/detail/push_options.hpp>
30 
31 namespace boost {
32 namespace asio {
33 namespace detail {
34 
35 // Helper to automatically define nested typedef result_type.
36 
37 template <typename T, typename = void>
38 struct executor_binder_result_type
39 {
40 protected:
41   typedef void result_type_or_void;
42 };
43 
44 template <typename T>
45 struct executor_binder_result_type<T,
46   typename void_type<typename T::result_type>::type>
47 {
48   typedef typename T::result_type result_type;
49 protected:
50   typedef result_type result_type_or_void;
51 };
52 
53 template <typename R>
54 struct executor_binder_result_type<R(*)()>
55 {
56   typedef R result_type;
57 protected:
58   typedef result_type result_type_or_void;
59 };
60 
61 template <typename R>
62 struct executor_binder_result_type<R(&)()>
63 {
64   typedef R result_type;
65 protected:
66   typedef result_type result_type_or_void;
67 };
68 
69 template <typename R, typename A1>
70 struct executor_binder_result_type<R(*)(A1)>
71 {
72   typedef R result_type;
73 protected:
74   typedef result_type result_type_or_void;
75 };
76 
77 template <typename R, typename A1>
78 struct executor_binder_result_type<R(&)(A1)>
79 {
80   typedef R result_type;
81 protected:
82   typedef result_type result_type_or_void;
83 };
84 
85 template <typename R, typename A1, typename A2>
86 struct executor_binder_result_type<R(*)(A1, A2)>
87 {
88   typedef R result_type;
89 protected:
90   typedef result_type result_type_or_void;
91 };
92 
93 template <typename R, typename A1, typename A2>
94 struct executor_binder_result_type<R(&)(A1, A2)>
95 {
96   typedef R result_type;
97 protected:
98   typedef result_type result_type_or_void;
99 };
100 
101 // Helper to automatically define nested typedef argument_type.
102 
103 template <typename T, typename = void>
104 struct executor_binder_argument_type {};
105 
106 template <typename T>
107 struct executor_binder_argument_type<T,
108   typename void_type<typename T::argument_type>::type>
109 {
110   typedef typename T::argument_type argument_type;
111 };
112 
113 template <typename R, typename A1>
114 struct executor_binder_argument_type<R(*)(A1)>
115 {
116   typedef A1 argument_type;
117 };
118 
119 template <typename R, typename A1>
120 struct executor_binder_argument_type<R(&)(A1)>
121 {
122   typedef A1 argument_type;
123 };
124 
125 // Helper to automatically define nested typedefs first_argument_type and
126 // second_argument_type.
127 
128 template <typename T, typename = void>
129 struct executor_binder_argument_types {};
130 
131 template <typename T>
132 struct executor_binder_argument_types<T,
133   typename void_type<typename T::first_argument_type>::type>
134 {
135   typedef typename T::first_argument_type first_argument_type;
136   typedef typename T::second_argument_type second_argument_type;
137 };
138 
139 template <typename R, typename A1, typename A2>
140 struct executor_binder_argument_type<R(*)(A1, A2)>
141 {
142   typedef A1 first_argument_type;
143   typedef A2 second_argument_type;
144 };
145 
146 template <typename R, typename A1, typename A2>
147 struct executor_binder_argument_type<R(&)(A1, A2)>
148 {
149   typedef A1 first_argument_type;
150   typedef A2 second_argument_type;
151 };
152 
153 // Helper to perform uses_executor construction of the target type, if
154 // required.
155 
156 template <typename T, typename Executor, bool UsesExecutor>
157 class executor_binder_base;
158 
159 template <typename T, typename Executor>
160 class executor_binder_base<T, Executor, true>
161 {
162 protected:
163   template <typename E, typename U>
executor_binder_base(BOOST_ASIO_MOVE_ARG (E)e,BOOST_ASIO_MOVE_ARG (U)u)164   executor_binder_base(BOOST_ASIO_MOVE_ARG(E) e, BOOST_ASIO_MOVE_ARG(U) u)
165     : executor_(BOOST_ASIO_MOVE_CAST(E)(e)),
166       target_(executor_arg_t(), executor_, BOOST_ASIO_MOVE_CAST(U)(u))
167   {
168   }
169 
170   Executor executor_;
171   T target_;
172 };
173 
174 template <typename T, typename Executor>
175 class executor_binder_base<T, Executor, false>
176 {
177 protected:
178   template <typename E, typename U>
executor_binder_base(BOOST_ASIO_MOVE_ARG (E)e,BOOST_ASIO_MOVE_ARG (U)u)179   executor_binder_base(BOOST_ASIO_MOVE_ARG(E) e, BOOST_ASIO_MOVE_ARG(U) u)
180     : executor_(BOOST_ASIO_MOVE_CAST(E)(e)),
181       target_(BOOST_ASIO_MOVE_CAST(U)(u))
182   {
183   }
184 
185   Executor executor_;
186   T target_;
187 };
188 
189 // Helper to enable SFINAE on zero-argument operator() below.
190 
191 template <typename T, typename = void>
192 struct executor_binder_result_of0
193 {
194   typedef void type;
195 };
196 
197 template <typename T>
198 struct executor_binder_result_of0<T,
199   typename void_type<typename result_of<T()>::type>::type>
200 {
201   typedef typename result_of<T()>::type type;
202 };
203 
204 } // namespace detail
205 
206 /// A call wrapper type to bind an executor of type @c Executor to an object of
207 /// type @c T.
208 template <typename T, typename Executor>
209 class executor_binder
210 #if !defined(GENERATING_DOCUMENTATION)
211   : public detail::executor_binder_result_type<T>,
212     public detail::executor_binder_argument_type<T>,
213     public detail::executor_binder_argument_types<T>,
214     private detail::executor_binder_base<
215       T, Executor, uses_executor<T, Executor>::value>
216 #endif // !defined(GENERATING_DOCUMENTATION)
217 {
218 public:
219   /// The type of the target object.
220   typedef T target_type;
221 
222   /// The type of the associated executor.
223   typedef Executor executor_type;
224 
225 #if defined(GENERATING_DOCUMENTATION)
226   /// The return type if a function.
227   /**
228    * The type of @c result_type is based on the type @c T of the wrapper's
229    * target object:
230    *
231    * @li if @c T is a pointer to function type, @c result_type is a synonym for
232    * the return type of @c T;
233    *
234    * @li if @c T is a class type with a member type @c result_type, then @c
235    * result_type is a synonym for @c T::result_type;
236    *
237    * @li otherwise @c result_type is not defined.
238    */
239   typedef see_below result_type;
240 
241   /// The type of the function's argument.
242   /**
243    * The type of @c argument_type is based on the type @c T of the wrapper's
244    * target object:
245    *
246    * @li if @c T is a pointer to a function type accepting a single argument,
247    * @c argument_type is a synonym for the return type of @c T;
248    *
249    * @li if @c T is a class type with a member type @c argument_type, then @c
250    * argument_type is a synonym for @c T::argument_type;
251    *
252    * @li otherwise @c argument_type is not defined.
253    */
254   typedef see_below argument_type;
255 
256   /// The type of the function's first argument.
257   /**
258    * The type of @c first_argument_type is based on the type @c T of the
259    * wrapper's target object:
260    *
261    * @li if @c T is a pointer to a function type accepting two arguments, @c
262    * first_argument_type is a synonym for the return type of @c T;
263    *
264    * @li if @c T is a class type with a member type @c first_argument_type,
265    * then @c first_argument_type is a synonym for @c T::first_argument_type;
266    *
267    * @li otherwise @c first_argument_type is not defined.
268    */
269   typedef see_below first_argument_type;
270 
271   /// The type of the function's second argument.
272   /**
273    * The type of @c second_argument_type is based on the type @c T of the
274    * wrapper's target object:
275    *
276    * @li if @c T is a pointer to a function type accepting two arguments, @c
277    * second_argument_type is a synonym for the return type of @c T;
278    *
279    * @li if @c T is a class type with a member type @c first_argument_type,
280    * then @c second_argument_type is a synonym for @c T::second_argument_type;
281    *
282    * @li otherwise @c second_argument_type is not defined.
283    */
284   typedef see_below second_argument_type;
285 #endif // defined(GENERATING_DOCUMENTATION)
286 
287   /// Construct an executor wrapper for the specified object.
288   /**
289    * This constructor is only valid if the type @c T is constructible from type
290    * @c U.
291    */
292   template <typename U>
executor_binder(executor_arg_t,const executor_type & e,BOOST_ASIO_MOVE_ARG (U)u)293   executor_binder(executor_arg_t, const executor_type& e,
294       BOOST_ASIO_MOVE_ARG(U) u)
295     : base_type(e, BOOST_ASIO_MOVE_CAST(U)(u))
296   {
297   }
298 
299   /// Copy constructor.
executor_binder(const executor_binder & other)300   executor_binder(const executor_binder& other)
301     : base_type(other.get_executor(), other.get())
302   {
303   }
304 
305   /// Construct a copy, but specify a different executor.
executor_binder(executor_arg_t,const executor_type & e,const executor_binder & other)306   executor_binder(executor_arg_t, const executor_type& e,
307       const executor_binder& other)
308     : base_type(e, other.get())
309   {
310   }
311 
312   /// Construct a copy of a different executor wrapper type.
313   /**
314    * This constructor is only valid if the @c Executor type is constructible
315    * from type @c OtherExecutor, and the type @c T is constructible from type
316    * @c U.
317    */
318   template <typename U, typename OtherExecutor>
executor_binder(const executor_binder<U,OtherExecutor> & other)319   executor_binder(const executor_binder<U, OtherExecutor>& other)
320     : base_type(other.get_executor(), other.get())
321   {
322   }
323 
324   /// Construct a copy of a different executor wrapper type, but specify a
325   /// different executor.
326   /**
327    * This constructor is only valid if the type @c T is constructible from type
328    * @c U.
329    */
330   template <typename U, typename OtherExecutor>
executor_binder(executor_arg_t,const executor_type & e,const executor_binder<U,OtherExecutor> & other)331   executor_binder(executor_arg_t, const executor_type& e,
332       const executor_binder<U, OtherExecutor>& other)
333     : base_type(e, other.get())
334   {
335   }
336 
337 #if defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
338 
339   /// Move constructor.
executor_binder(executor_binder && other)340   executor_binder(executor_binder&& other)
341     : base_type(BOOST_ASIO_MOVE_CAST(executor_type)(other.get_executor()),
342         BOOST_ASIO_MOVE_CAST(T)(other.get()))
343   {
344   }
345 
346   /// Move construct the target object, but specify a different executor.
executor_binder(executor_arg_t,const executor_type & e,executor_binder && other)347   executor_binder(executor_arg_t, const executor_type& e,
348       executor_binder&& other)
349     : base_type(e, BOOST_ASIO_MOVE_CAST(T)(other.get()))
350   {
351   }
352 
353   /// Move construct from a different executor wrapper type.
354   template <typename U, typename OtherExecutor>
executor_binder(executor_binder<U,OtherExecutor> && other)355   executor_binder(executor_binder<U, OtherExecutor>&& other)
356     : base_type(BOOST_ASIO_MOVE_CAST(OtherExecutor)(other.get_executor()),
357         BOOST_ASIO_MOVE_CAST(U)(other.get()))
358   {
359   }
360 
361   /// Move construct from a different executor wrapper type, but specify a
362   /// different executor.
363   template <typename U, typename OtherExecutor>
executor_binder(executor_arg_t,const executor_type & e,executor_binder<U,OtherExecutor> && other)364   executor_binder(executor_arg_t, const executor_type& e,
365       executor_binder<U, OtherExecutor>&& other)
366     : base_type(e, BOOST_ASIO_MOVE_CAST(U)(other.get()))
367   {
368   }
369 
370 #endif // defined(BOOST_ASIO_HAS_MOVE) || defined(GENERATING_DOCUMENTATION)
371 
372   /// Destructor.
~executor_binder()373   ~executor_binder()
374   {
375   }
376 
377   /// Obtain a reference to the target object.
get()378   target_type& get() BOOST_ASIO_NOEXCEPT
379   {
380     return this->target_;
381   }
382 
383   /// Obtain a reference to the target object.
get() const384   const target_type& get() const BOOST_ASIO_NOEXCEPT
385   {
386     return this->target_;
387   }
388 
389   /// Obtain the associated executor.
get_executor() const390   executor_type get_executor() const BOOST_ASIO_NOEXCEPT
391   {
392     return this->executor_;
393   }
394 
395 #if defined(GENERATING_DOCUMENTATION)
396 
397   template <typename... Args> auto operator()(Args&& ...);
398   template <typename... Args> auto operator()(Args&& ...) const;
399 
400 #elif defined(BOOST_ASIO_HAS_VARIADIC_TEMPLATES)
401 
402   /// Forwarding function call operator.
403   template <typename... Args>
operator ()(BOOST_ASIO_MOVE_ARG (Args)...args)404   typename result_of<T(Args...)>::type operator()(
405       BOOST_ASIO_MOVE_ARG(Args)... args)
406   {
407     return this->target_(BOOST_ASIO_MOVE_CAST(Args)(args)...);
408   }
409 
410   /// Forwarding function call operator.
411   template <typename... Args>
operator ()(BOOST_ASIO_MOVE_ARG (Args)...args) const412   typename result_of<T(Args...)>::type operator()(
413       BOOST_ASIO_MOVE_ARG(Args)... args) const
414   {
415     return this->target_(BOOST_ASIO_MOVE_CAST(Args)(args)...);
416   }
417 
418 #elif defined(BOOST_ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
419 
operator ()()420   typename detail::executor_binder_result_of0<T>::type operator()()
421   {
422     return this->target_();
423   }
424 
operator ()() const425   typename detail::executor_binder_result_of0<T>::type operator()() const
426   {
427     return this->target_();
428   }
429 
430 #define BOOST_ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \
431   template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
432   typename result_of<T(BOOST_ASIO_VARIADIC_TARGS(n))>::type operator()( \
433       BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
434   { \
435     return this->target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
436   } \
437   \
438   template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
439   typename result_of<T(BOOST_ASIO_VARIADIC_TARGS(n))>::type operator()( \
440       BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) const \
441   { \
442     return this->target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
443   } \
444   /**/
445   BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF)
446 #undef BOOST_ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF
447 
448 #else // defined(BOOST_ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
449 
450   typedef typename detail::executor_binder_result_type<T>::result_type_or_void
451     result_type_or_void;
452 
453   result_type_or_void operator()()
454   {
455     return this->target_();
456   }
457 
458   result_type_or_void operator()() const
459   {
460     return this->target_();
461   }
462 
463 #define BOOST_ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF(n) \
464   template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
465   result_type_or_void operator()( \
466       BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) \
467   { \
468     return this->target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
469   } \
470   \
471   template <BOOST_ASIO_VARIADIC_TPARAMS(n)> \
472   result_type_or_void operator()( \
473       BOOST_ASIO_VARIADIC_MOVE_PARAMS(n)) const \
474   { \
475     return this->target_(BOOST_ASIO_VARIADIC_MOVE_ARGS(n)); \
476   } \
477   /**/
478   BOOST_ASIO_VARIADIC_GENERATE(BOOST_ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF)
479 #undef BOOST_ASIO_PRIVATE_BIND_EXECUTOR_CALL_DEF
480 
481 #endif // defined(BOOST_ASIO_HAS_STD_TYPE_TRAITS) && !defined(_MSC_VER)
482 
483 private:
484   typedef detail::executor_binder_base<T, Executor,
485     uses_executor<T, Executor>::value> base_type;
486 };
487 
488 /// Associate an object of type @c T with an executor of type @c Executor.
489 template <typename Executor, typename T>
490 inline executor_binder<typename decay<T>::type, Executor>
bind_executor(const Executor & ex,BOOST_ASIO_MOVE_ARG (T)t,typename constraint<is_executor<Executor>::value||execution::is_executor<Executor>::value>::type=0)491 bind_executor(const Executor& ex, BOOST_ASIO_MOVE_ARG(T) t,
492     typename constraint<
493       is_executor<Executor>::value || execution::is_executor<Executor>::value
494     >::type = 0)
495 {
496   return executor_binder<typename decay<T>::type, Executor>(
497       executor_arg_t(), ex, BOOST_ASIO_MOVE_CAST(T)(t));
498 }
499 
500 /// Associate an object of type @c T with an execution context's executor.
501 template <typename ExecutionContext, typename T>
502 inline executor_binder<typename decay<T>::type,
503   typename ExecutionContext::executor_type>
bind_executor(ExecutionContext & ctx,BOOST_ASIO_MOVE_ARG (T)t,typename constraint<is_convertible<ExecutionContext &,execution_context &>::value>::type=0)504 bind_executor(ExecutionContext& ctx, BOOST_ASIO_MOVE_ARG(T) t,
505     typename constraint<is_convertible<
506       ExecutionContext&, execution_context&>::value>::type = 0)
507 {
508   return executor_binder<typename decay<T>::type,
509     typename ExecutionContext::executor_type>(
510       executor_arg_t(), ctx.get_executor(), BOOST_ASIO_MOVE_CAST(T)(t));
511 }
512 
513 #if !defined(GENERATING_DOCUMENTATION)
514 
515 template <typename T, typename Executor>
516 struct uses_executor<executor_binder<T, Executor>, Executor>
517   : true_type {};
518 
519 template <typename T, typename Executor, typename Signature>
520 class async_result<executor_binder<T, Executor>, Signature>
521 {
522 public:
523   typedef executor_binder<
524     typename async_result<T, Signature>::completion_handler_type, Executor>
525       completion_handler_type;
526 
527   typedef typename async_result<T, Signature>::return_type return_type;
528 
async_result(executor_binder<T,Executor> & b)529   explicit async_result(executor_binder<T, Executor>& b)
530     : target_(b.get())
531   {
532   }
533 
get()534   return_type get()
535   {
536     return target_.get();
537   }
538 
539 private:
540   async_result(const async_result&) BOOST_ASIO_DELETED;
541   async_result& operator=(const async_result&) BOOST_ASIO_DELETED;
542 
543   async_result<T, Signature> target_;
544 };
545 
546 template <typename T, typename Executor, typename Allocator>
547 struct associated_allocator<executor_binder<T, Executor>, Allocator>
548 {
549   typedef typename associated_allocator<T, Allocator>::type type;
550 
getboost::asio::associated_allocator551   static type get(const executor_binder<T, Executor>& b,
552       const Allocator& a = Allocator()) BOOST_ASIO_NOEXCEPT
553   {
554     return associated_allocator<T, Allocator>::get(b.get(), a);
555   }
556 };
557 
558 template <typename T, typename Executor, typename Executor1>
559 struct associated_executor<executor_binder<T, Executor>, Executor1>
560 {
561   typedef Executor type;
562 
getboost::asio::associated_executor563   static type get(const executor_binder<T, Executor>& b,
564       const Executor1& = Executor1()) BOOST_ASIO_NOEXCEPT
565   {
566     return b.get_executor();
567   }
568 };
569 
570 #endif // !defined(GENERATING_DOCUMENTATION)
571 
572 } // namespace asio
573 } // namespace boost
574 
575 #include <boost/asio/detail/pop_options.hpp>
576 
577 #endif // BOOST_ASIO_BIND_EXECUTOR_HPP
578