1 | // |
2 | // any_completion_handler.hpp |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~~~~ |
4 | // |
5 | // Copyright (c) 2003-2024 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_ANY_COMPLETION_HANDLER_HPP |
12 | #define BOOST_ASIO_ANY_COMPLETION_HANDLER_HPP |
13 | |
14 | #include <boost/asio/detail/config.hpp> |
15 | #include <cstring> |
16 | #include <functional> |
17 | #include <memory> |
18 | #include <utility> |
19 | #include <boost/asio/any_completion_executor.hpp> |
20 | #include <boost/asio/any_io_executor.hpp> |
21 | #include <boost/asio/associated_allocator.hpp> |
22 | #include <boost/asio/associated_cancellation_slot.hpp> |
23 | #include <boost/asio/associated_executor.hpp> |
24 | #include <boost/asio/associated_immediate_executor.hpp> |
25 | #include <boost/asio/cancellation_state.hpp> |
26 | #include <boost/asio/recycling_allocator.hpp> |
27 | |
28 | #include <boost/asio/detail/push_options.hpp> |
29 | |
30 | namespace boost { |
31 | namespace asio { |
32 | namespace detail { |
33 | |
34 | class any_completion_handler_impl_base |
35 | { |
36 | public: |
37 | template <typename S> |
38 | explicit any_completion_handler_impl_base(S&& slot) |
39 | : cancel_state_(static_cast<S&&>(slot), enable_total_cancellation()) |
40 | { |
41 | } |
42 | |
43 | cancellation_slot get_cancellation_slot() const noexcept |
44 | { |
45 | return cancel_state_.slot(); |
46 | } |
47 | |
48 | private: |
49 | cancellation_state cancel_state_; |
50 | }; |
51 | |
52 | template <typename Handler> |
53 | class any_completion_handler_impl : |
54 | public any_completion_handler_impl_base |
55 | { |
56 | public: |
57 | template <typename S, typename H> |
58 | any_completion_handler_impl(S&& slot, H&& h) |
59 | : any_completion_handler_impl_base(static_cast<S&&>(slot)), |
60 | handler_(static_cast<H&&>(h)) |
61 | { |
62 | } |
63 | |
64 | struct uninit_deleter |
65 | { |
66 | typename std::allocator_traits< |
67 | associated_allocator_t<Handler, |
68 | boost::asio::recycling_allocator<void>>>::template |
69 | rebind_alloc<any_completion_handler_impl> alloc; |
70 | |
71 | void operator()(any_completion_handler_impl* ptr) |
72 | { |
73 | std::allocator_traits<decltype(alloc)>::deallocate(alloc, ptr, 1); |
74 | } |
75 | }; |
76 | |
77 | struct deleter |
78 | { |
79 | typename std::allocator_traits< |
80 | associated_allocator_t<Handler, |
81 | boost::asio::recycling_allocator<void>>>::template |
82 | rebind_alloc<any_completion_handler_impl> alloc; |
83 | |
84 | void operator()(any_completion_handler_impl* ptr) |
85 | { |
86 | std::allocator_traits<decltype(alloc)>::destroy(alloc, ptr); |
87 | std::allocator_traits<decltype(alloc)>::deallocate(alloc, ptr, 1); |
88 | } |
89 | }; |
90 | |
91 | template <typename S, typename H> |
92 | static any_completion_handler_impl* create(S&& slot, H&& h) |
93 | { |
94 | uninit_deleter d{ |
95 | (get_associated_allocator)(h, |
96 | boost::asio::recycling_allocator<void>())}; |
97 | |
98 | std::unique_ptr<any_completion_handler_impl, uninit_deleter> uninit_ptr( |
99 | std::allocator_traits<decltype(d.alloc)>::allocate(d.alloc, 1), d); |
100 | |
101 | any_completion_handler_impl* ptr = |
102 | new (uninit_ptr.get()) any_completion_handler_impl( |
103 | static_cast<S&&>(slot), static_cast<H&&>(h)); |
104 | |
105 | uninit_ptr.release(); |
106 | return ptr; |
107 | } |
108 | |
109 | void destroy() |
110 | { |
111 | deleter d{ |
112 | (get_associated_allocator)(handler_, |
113 | boost::asio::recycling_allocator<void>())}; |
114 | |
115 | d(this); |
116 | } |
117 | |
118 | any_completion_executor executor( |
119 | const any_completion_executor& candidate) const noexcept |
120 | { |
121 | return any_completion_executor(std::nothrow, |
122 | (get_associated_executor)(handler_, candidate)); |
123 | } |
124 | |
125 | any_completion_executor immediate_executor( |
126 | const any_io_executor& candidate) const noexcept |
127 | { |
128 | return any_completion_executor(std::nothrow, |
129 | (get_associated_immediate_executor)(handler_, candidate)); |
130 | } |
131 | |
132 | void* allocate(std::size_t size, std::size_t align) const |
133 | { |
134 | typename std::allocator_traits< |
135 | associated_allocator_t<Handler, |
136 | boost::asio::recycling_allocator<void>>>::template |
137 | rebind_alloc<unsigned char> alloc( |
138 | (get_associated_allocator)(handler_, |
139 | boost::asio::recycling_allocator<void>())); |
140 | |
141 | std::size_t space = size + align - 1; |
142 | unsigned char* base = |
143 | std::allocator_traits<decltype(alloc)>::allocate( |
144 | alloc, space + sizeof(std::ptrdiff_t)); |
145 | |
146 | void* p = base; |
147 | if (detail::align(alignment: align, size, ptr&: p, space)) |
148 | { |
149 | std::ptrdiff_t off = static_cast<unsigned char*>(p) - base; |
150 | std::memcpy(dest: static_cast<unsigned char*>(p) + size, src: &off, n: sizeof(off)); |
151 | return p; |
152 | } |
153 | |
154 | std::bad_alloc ex; |
155 | boost::asio::detail::throw_exception(e: ex); |
156 | return nullptr; |
157 | } |
158 | |
159 | void deallocate(void* p, std::size_t size, std::size_t align) const |
160 | { |
161 | if (p) |
162 | { |
163 | typename std::allocator_traits< |
164 | associated_allocator_t<Handler, |
165 | boost::asio::recycling_allocator<void>>>::template |
166 | rebind_alloc<unsigned char> alloc( |
167 | (get_associated_allocator)(handler_, |
168 | boost::asio::recycling_allocator<void>())); |
169 | |
170 | std::ptrdiff_t off; |
171 | std::memcpy(dest: &off, src: static_cast<unsigned char*>(p) + size, n: sizeof(off)); |
172 | unsigned char* base = static_cast<unsigned char*>(p) - off; |
173 | |
174 | std::allocator_traits<decltype(alloc)>::deallocate( |
175 | alloc, base, size + align -1 + sizeof(std::ptrdiff_t)); |
176 | } |
177 | } |
178 | |
179 | template <typename... Args> |
180 | void call(Args&&... args) |
181 | { |
182 | deleter d{ |
183 | (get_associated_allocator)(handler_, |
184 | boost::asio::recycling_allocator<void>())}; |
185 | |
186 | std::unique_ptr<any_completion_handler_impl, deleter> ptr(this, d); |
187 | Handler handler(static_cast<Handler&&>(handler_)); |
188 | ptr.reset(); |
189 | |
190 | static_cast<Handler&&>(handler)( |
191 | static_cast<Args&&>(args)...); |
192 | } |
193 | |
194 | private: |
195 | Handler handler_; |
196 | }; |
197 | |
198 | template <typename Signature> |
199 | class any_completion_handler_call_fn; |
200 | |
201 | template <typename R, typename... Args> |
202 | class any_completion_handler_call_fn<R(Args...)> |
203 | { |
204 | public: |
205 | using type = void(*)(any_completion_handler_impl_base*, Args...); |
206 | |
207 | constexpr any_completion_handler_call_fn(type fn) |
208 | : call_fn_(fn) |
209 | { |
210 | } |
211 | |
212 | void call(any_completion_handler_impl_base* impl, Args... args) const |
213 | { |
214 | call_fn_(impl, static_cast<Args&&>(args)...); |
215 | } |
216 | |
217 | template <typename Handler> |
218 | static void impl(any_completion_handler_impl_base* impl, Args... args) |
219 | { |
220 | static_cast<any_completion_handler_impl<Handler>*>(impl)->call( |
221 | static_cast<Args&&>(args)...); |
222 | } |
223 | |
224 | private: |
225 | type call_fn_; |
226 | }; |
227 | |
228 | template <typename... Signatures> |
229 | class any_completion_handler_call_fns; |
230 | |
231 | template <typename Signature> |
232 | class any_completion_handler_call_fns<Signature> : |
233 | public any_completion_handler_call_fn<Signature> |
234 | { |
235 | public: |
236 | using any_completion_handler_call_fn< |
237 | Signature>::any_completion_handler_call_fn; |
238 | using any_completion_handler_call_fn<Signature>::call; |
239 | }; |
240 | |
241 | template <typename Signature, typename... Signatures> |
242 | class any_completion_handler_call_fns<Signature, Signatures...> : |
243 | public any_completion_handler_call_fn<Signature>, |
244 | public any_completion_handler_call_fns<Signatures...> |
245 | { |
246 | public: |
247 | template <typename CallFn, typename... CallFns> |
248 | constexpr any_completion_handler_call_fns(CallFn fn, CallFns... fns) |
249 | : any_completion_handler_call_fn<Signature>(fn), |
250 | any_completion_handler_call_fns<Signatures...>(fns...) |
251 | { |
252 | } |
253 | |
254 | using any_completion_handler_call_fn<Signature>::call; |
255 | using any_completion_handler_call_fns<Signatures...>::call; |
256 | }; |
257 | |
258 | class any_completion_handler_destroy_fn |
259 | { |
260 | public: |
261 | using type = void(*)(any_completion_handler_impl_base*); |
262 | |
263 | constexpr any_completion_handler_destroy_fn(type fn) |
264 | : destroy_fn_(fn) |
265 | { |
266 | } |
267 | |
268 | void destroy(any_completion_handler_impl_base* impl) const |
269 | { |
270 | destroy_fn_(impl); |
271 | } |
272 | |
273 | template <typename Handler> |
274 | static void impl(any_completion_handler_impl_base* impl) |
275 | { |
276 | static_cast<any_completion_handler_impl<Handler>*>(impl)->destroy(); |
277 | } |
278 | |
279 | private: |
280 | type destroy_fn_; |
281 | }; |
282 | |
283 | class any_completion_handler_executor_fn |
284 | { |
285 | public: |
286 | using type = any_completion_executor(*)( |
287 | any_completion_handler_impl_base*, const any_completion_executor&); |
288 | |
289 | constexpr any_completion_handler_executor_fn(type fn) |
290 | : executor_fn_(fn) |
291 | { |
292 | } |
293 | |
294 | any_completion_executor executor(any_completion_handler_impl_base* impl, |
295 | const any_completion_executor& candidate) const |
296 | { |
297 | return executor_fn_(impl, candidate); |
298 | } |
299 | |
300 | template <typename Handler> |
301 | static any_completion_executor impl(any_completion_handler_impl_base* impl, |
302 | const any_completion_executor& candidate) |
303 | { |
304 | return static_cast<any_completion_handler_impl<Handler>*>(impl)->executor( |
305 | candidate); |
306 | } |
307 | |
308 | private: |
309 | type executor_fn_; |
310 | }; |
311 | |
312 | class any_completion_handler_immediate_executor_fn |
313 | { |
314 | public: |
315 | using type = any_completion_executor(*)( |
316 | any_completion_handler_impl_base*, const any_io_executor&); |
317 | |
318 | constexpr any_completion_handler_immediate_executor_fn(type fn) |
319 | : immediate_executor_fn_(fn) |
320 | { |
321 | } |
322 | |
323 | any_completion_executor immediate_executor( |
324 | any_completion_handler_impl_base* impl, |
325 | const any_io_executor& candidate) const |
326 | { |
327 | return immediate_executor_fn_(impl, candidate); |
328 | } |
329 | |
330 | template <typename Handler> |
331 | static any_completion_executor impl(any_completion_handler_impl_base* impl, |
332 | const any_io_executor& candidate) |
333 | { |
334 | return static_cast<any_completion_handler_impl<Handler>*>( |
335 | impl)->immediate_executor(candidate); |
336 | } |
337 | |
338 | private: |
339 | type immediate_executor_fn_; |
340 | }; |
341 | |
342 | class any_completion_handler_allocate_fn |
343 | { |
344 | public: |
345 | using type = void*(*)(any_completion_handler_impl_base*, |
346 | std::size_t, std::size_t); |
347 | |
348 | constexpr any_completion_handler_allocate_fn(type fn) |
349 | : allocate_fn_(fn) |
350 | { |
351 | } |
352 | |
353 | void* allocate(any_completion_handler_impl_base* impl, |
354 | std::size_t size, std::size_t align) const |
355 | { |
356 | return allocate_fn_(impl, size, align); |
357 | } |
358 | |
359 | template <typename Handler> |
360 | static void* impl(any_completion_handler_impl_base* impl, |
361 | std::size_t size, std::size_t align) |
362 | { |
363 | return static_cast<any_completion_handler_impl<Handler>*>(impl)->allocate( |
364 | size, align); |
365 | } |
366 | |
367 | private: |
368 | type allocate_fn_; |
369 | }; |
370 | |
371 | class any_completion_handler_deallocate_fn |
372 | { |
373 | public: |
374 | using type = void(*)(any_completion_handler_impl_base*, |
375 | void*, std::size_t, std::size_t); |
376 | |
377 | constexpr any_completion_handler_deallocate_fn(type fn) |
378 | : deallocate_fn_(fn) |
379 | { |
380 | } |
381 | |
382 | void deallocate(any_completion_handler_impl_base* impl, |
383 | void* p, std::size_t size, std::size_t align) const |
384 | { |
385 | deallocate_fn_(impl, p, size, align); |
386 | } |
387 | |
388 | template <typename Handler> |
389 | static void impl(any_completion_handler_impl_base* impl, |
390 | void* p, std::size_t size, std::size_t align) |
391 | { |
392 | static_cast<any_completion_handler_impl<Handler>*>(impl)->deallocate( |
393 | p, size, align); |
394 | } |
395 | |
396 | private: |
397 | type deallocate_fn_; |
398 | }; |
399 | |
400 | template <typename... Signatures> |
401 | class any_completion_handler_fn_table |
402 | : private any_completion_handler_destroy_fn, |
403 | private any_completion_handler_executor_fn, |
404 | private any_completion_handler_immediate_executor_fn, |
405 | private any_completion_handler_allocate_fn, |
406 | private any_completion_handler_deallocate_fn, |
407 | private any_completion_handler_call_fns<Signatures...> |
408 | { |
409 | public: |
410 | template <typename... CallFns> |
411 | constexpr any_completion_handler_fn_table( |
412 | any_completion_handler_destroy_fn::type destroy_fn, |
413 | any_completion_handler_executor_fn::type executor_fn, |
414 | any_completion_handler_immediate_executor_fn::type immediate_executor_fn, |
415 | any_completion_handler_allocate_fn::type allocate_fn, |
416 | any_completion_handler_deallocate_fn::type deallocate_fn, |
417 | CallFns... call_fns) |
418 | : any_completion_handler_destroy_fn(destroy_fn), |
419 | any_completion_handler_executor_fn(executor_fn), |
420 | any_completion_handler_immediate_executor_fn(immediate_executor_fn), |
421 | any_completion_handler_allocate_fn(allocate_fn), |
422 | any_completion_handler_deallocate_fn(deallocate_fn), |
423 | any_completion_handler_call_fns<Signatures...>(call_fns...) |
424 | { |
425 | } |
426 | |
427 | using any_completion_handler_destroy_fn::destroy; |
428 | using any_completion_handler_executor_fn::executor; |
429 | using any_completion_handler_immediate_executor_fn::immediate_executor; |
430 | using any_completion_handler_allocate_fn::allocate; |
431 | using any_completion_handler_deallocate_fn::deallocate; |
432 | using any_completion_handler_call_fns<Signatures...>::call; |
433 | }; |
434 | |
435 | template <typename Handler, typename... Signatures> |
436 | struct any_completion_handler_fn_table_instance |
437 | { |
438 | static constexpr any_completion_handler_fn_table<Signatures...> |
439 | value = any_completion_handler_fn_table<Signatures...>( |
440 | &any_completion_handler_destroy_fn::impl<Handler>, |
441 | &any_completion_handler_executor_fn::impl<Handler>, |
442 | &any_completion_handler_immediate_executor_fn::impl<Handler>, |
443 | &any_completion_handler_allocate_fn::impl<Handler>, |
444 | &any_completion_handler_deallocate_fn::impl<Handler>, |
445 | &any_completion_handler_call_fn<Signatures>::template impl<Handler>...); |
446 | }; |
447 | |
448 | template <typename Handler, typename... Signatures> |
449 | constexpr any_completion_handler_fn_table<Signatures...> |
450 | any_completion_handler_fn_table_instance<Handler, Signatures...>::value; |
451 | |
452 | } // namespace detail |
453 | |
454 | template <typename... Signatures> |
455 | class any_completion_handler; |
456 | |
457 | /// An allocator type that forwards memory allocation operations through an |
458 | /// instance of @c any_completion_handler. |
459 | template <typename T, typename... Signatures> |
460 | class any_completion_handler_allocator |
461 | { |
462 | private: |
463 | template <typename...> |
464 | friend class any_completion_handler; |
465 | |
466 | template <typename, typename...> |
467 | friend class any_completion_handler_allocator; |
468 | |
469 | const detail::any_completion_handler_fn_table<Signatures...>* fn_table_; |
470 | detail::any_completion_handler_impl_base* impl_; |
471 | |
472 | constexpr any_completion_handler_allocator(int, |
473 | const any_completion_handler<Signatures...>& h) noexcept |
474 | : fn_table_(h.fn_table_), |
475 | impl_(h.impl_) |
476 | { |
477 | } |
478 | |
479 | public: |
480 | /// The type of objects that may be allocated by the allocator. |
481 | typedef T value_type; |
482 | |
483 | /// Rebinds an allocator to another value type. |
484 | template <typename U> |
485 | struct rebind |
486 | { |
487 | /// Specifies the type of the rebound allocator. |
488 | typedef any_completion_handler_allocator<U, Signatures...> other; |
489 | }; |
490 | |
491 | /// Construct from another @c any_completion_handler_allocator. |
492 | template <typename U> |
493 | constexpr any_completion_handler_allocator( |
494 | const any_completion_handler_allocator<U, Signatures...>& a) |
495 | noexcept |
496 | : fn_table_(a.fn_table_), |
497 | impl_(a.impl_) |
498 | { |
499 | } |
500 | |
501 | /// Equality operator. |
502 | constexpr bool operator==( |
503 | const any_completion_handler_allocator& other) const noexcept |
504 | { |
505 | return fn_table_ == other.fn_table_ && impl_ == other.impl_; |
506 | } |
507 | |
508 | /// Inequality operator. |
509 | constexpr bool operator!=( |
510 | const any_completion_handler_allocator& other) const noexcept |
511 | { |
512 | return fn_table_ != other.fn_table_ || impl_ != other.impl_; |
513 | } |
514 | |
515 | /// Allocate space for @c n objects of the allocator's value type. |
516 | T* allocate(std::size_t n) const |
517 | { |
518 | if (fn_table_) |
519 | { |
520 | return static_cast<T*>( |
521 | fn_table_->allocate( |
522 | impl_, sizeof(T) * n, alignof(T))); |
523 | } |
524 | std::bad_alloc ex; |
525 | boost::asio::detail::throw_exception(e: ex); |
526 | return nullptr; |
527 | } |
528 | |
529 | /// Deallocate space for @c n objects of the allocator's value type. |
530 | void deallocate(T* p, std::size_t n) const |
531 | { |
532 | fn_table_->deallocate(impl_, p, sizeof(T) * n, alignof(T)); |
533 | } |
534 | }; |
535 | |
536 | /// A protoco-allocator type that may be rebound to obtain an allocator that |
537 | /// forwards memory allocation operations through an instance of |
538 | /// @c any_completion_handler. |
539 | template <typename... Signatures> |
540 | class any_completion_handler_allocator<void, Signatures...> |
541 | { |
542 | private: |
543 | template <typename...> |
544 | friend class any_completion_handler; |
545 | |
546 | template <typename, typename...> |
547 | friend class any_completion_handler_allocator; |
548 | |
549 | const detail::any_completion_handler_fn_table<Signatures...>* fn_table_; |
550 | detail::any_completion_handler_impl_base* impl_; |
551 | |
552 | constexpr any_completion_handler_allocator(int, |
553 | const any_completion_handler<Signatures...>& h) noexcept |
554 | : fn_table_(h.fn_table_), |
555 | impl_(h.impl_) |
556 | { |
557 | } |
558 | |
559 | public: |
560 | /// @c void as no objects can be allocated through a proto-allocator. |
561 | typedef void value_type; |
562 | |
563 | /// Rebinds an allocator to another value type. |
564 | template <typename U> |
565 | struct rebind |
566 | { |
567 | /// Specifies the type of the rebound allocator. |
568 | typedef any_completion_handler_allocator<U, Signatures...> other; |
569 | }; |
570 | |
571 | /// Construct from another @c any_completion_handler_allocator. |
572 | template <typename U> |
573 | constexpr any_completion_handler_allocator( |
574 | const any_completion_handler_allocator<U, Signatures...>& a) |
575 | noexcept |
576 | : fn_table_(a.fn_table_), |
577 | impl_(a.impl_) |
578 | { |
579 | } |
580 | |
581 | /// Equality operator. |
582 | constexpr bool operator==( |
583 | const any_completion_handler_allocator& other) const noexcept |
584 | { |
585 | return fn_table_ == other.fn_table_ && impl_ == other.impl_; |
586 | } |
587 | |
588 | /// Inequality operator. |
589 | constexpr bool operator!=( |
590 | const any_completion_handler_allocator& other) const noexcept |
591 | { |
592 | return fn_table_ != other.fn_table_ || impl_ != other.impl_; |
593 | } |
594 | }; |
595 | |
596 | /// Polymorphic wrapper for completion handlers. |
597 | /** |
598 | * The @c any_completion_handler class template is a polymorphic wrapper for |
599 | * completion handlers that propagates the associated executor, associated |
600 | * allocator, and associated cancellation slot through a type-erasing interface. |
601 | * |
602 | * When using @c any_completion_handler, specify one or more completion |
603 | * signatures as template parameters. These will dictate the arguments that may |
604 | * be passed to the handler through the polymorphic interface. |
605 | * |
606 | * Typical uses for @c any_completion_handler include: |
607 | * |
608 | * @li Separate compilation of asynchronous operation implementations. |
609 | * |
610 | * @li Enabling interoperability between asynchronous operations and virtual |
611 | * functions. |
612 | */ |
613 | template <typename... Signatures> |
614 | class any_completion_handler |
615 | { |
616 | #if !defined(GENERATING_DOCUMENTATION) |
617 | private: |
618 | template <typename, typename...> |
619 | friend class any_completion_handler_allocator; |
620 | |
621 | template <typename, typename> |
622 | friend struct associated_executor; |
623 | |
624 | template <typename, typename> |
625 | friend struct associated_immediate_executor; |
626 | |
627 | const detail::any_completion_handler_fn_table<Signatures...>* fn_table_; |
628 | detail::any_completion_handler_impl_base* impl_; |
629 | #endif // !defined(GENERATING_DOCUMENTATION) |
630 | |
631 | public: |
632 | /// The associated allocator type. |
633 | using allocator_type = any_completion_handler_allocator<void, Signatures...>; |
634 | |
635 | /// The associated cancellation slot type. |
636 | using cancellation_slot_type = cancellation_slot; |
637 | |
638 | /// Construct an @c any_completion_handler in an empty state, without a target |
639 | /// object. |
640 | constexpr any_completion_handler() |
641 | : fn_table_(nullptr), |
642 | impl_(nullptr) |
643 | { |
644 | } |
645 | |
646 | /// Construct an @c any_completion_handler in an empty state, without a target |
647 | /// object. |
648 | constexpr any_completion_handler(nullptr_t) |
649 | : fn_table_(nullptr), |
650 | impl_(nullptr) |
651 | { |
652 | } |
653 | |
654 | /// Construct an @c any_completion_handler to contain the specified target. |
655 | template <typename H, typename Handler = decay_t<H>> |
656 | any_completion_handler(H&& h, |
657 | constraint_t< |
658 | !is_same<decay_t<H>, any_completion_handler>::value |
659 | > = 0) |
660 | : fn_table_( |
661 | &detail::any_completion_handler_fn_table_instance< |
662 | Handler, Signatures...>::value), |
663 | impl_(detail::any_completion_handler_impl<Handler>::create( |
664 | (get_associated_cancellation_slot)(h), static_cast<H&&>(h))) |
665 | { |
666 | } |
667 | |
668 | /// Move-construct an @c any_completion_handler from another. |
669 | /** |
670 | * After the operation, the moved-from object @c other has no target. |
671 | */ |
672 | any_completion_handler(any_completion_handler&& other) noexcept |
673 | : fn_table_(other.fn_table_), |
674 | impl_(other.impl_) |
675 | { |
676 | other.fn_table_ = nullptr; |
677 | other.impl_ = nullptr; |
678 | } |
679 | |
680 | /// Move-assign an @c any_completion_handler from another. |
681 | /** |
682 | * After the operation, the moved-from object @c other has no target. |
683 | */ |
684 | any_completion_handler& operator=( |
685 | any_completion_handler&& other) noexcept |
686 | { |
687 | any_completion_handler( |
688 | static_cast<any_completion_handler&&>(other)).swap(*this); |
689 | return *this; |
690 | } |
691 | |
692 | /// Assignment operator that sets the polymorphic wrapper to the empty state. |
693 | any_completion_handler& operator=(nullptr_t) noexcept |
694 | { |
695 | any_completion_handler().swap(*this); |
696 | return *this; |
697 | } |
698 | |
699 | /// Destructor. |
700 | ~any_completion_handler() |
701 | { |
702 | if (impl_) |
703 | fn_table_->destroy(impl_); |
704 | } |
705 | |
706 | /// Test if the polymorphic wrapper is empty. |
707 | constexpr explicit operator bool() const noexcept |
708 | { |
709 | return impl_ != nullptr; |
710 | } |
711 | |
712 | /// Test if the polymorphic wrapper is non-empty. |
713 | constexpr bool operator!() const noexcept |
714 | { |
715 | return impl_ == nullptr; |
716 | } |
717 | |
718 | /// Swap the content of an @c any_completion_handler with another. |
719 | void swap(any_completion_handler& other) noexcept |
720 | { |
721 | std::swap(fn_table_, other.fn_table_); |
722 | std::swap(impl_, other.impl_); |
723 | } |
724 | |
725 | /// Get the associated allocator. |
726 | allocator_type get_allocator() const noexcept |
727 | { |
728 | return allocator_type(0, *this); |
729 | } |
730 | |
731 | /// Get the associated cancellation slot. |
732 | cancellation_slot_type get_cancellation_slot() const noexcept |
733 | { |
734 | return impl_ ? impl_->get_cancellation_slot() : cancellation_slot_type(); |
735 | } |
736 | |
737 | /// Function call operator. |
738 | /** |
739 | * Invokes target completion handler with the supplied arguments. |
740 | * |
741 | * This function may only be called once, as the target handler is moved from. |
742 | * The polymorphic wrapper is left in an empty state. |
743 | * |
744 | * Throws @c std::bad_function_call if the polymorphic wrapper is empty. |
745 | */ |
746 | template <typename... Args> |
747 | auto operator()(Args&&... args) |
748 | -> decltype(fn_table_->call(impl_, static_cast<Args&&>(args)...)) |
749 | { |
750 | if (detail::any_completion_handler_impl_base* impl = impl_) |
751 | { |
752 | impl_ = nullptr; |
753 | return fn_table_->call(impl, static_cast<Args&&>(args)...); |
754 | } |
755 | std::bad_function_call ex; |
756 | boost::asio::detail::throw_exception(e: ex); |
757 | } |
758 | |
759 | /// Equality operator. |
760 | friend constexpr bool operator==( |
761 | const any_completion_handler& a, nullptr_t) noexcept |
762 | { |
763 | return a.impl_ == nullptr; |
764 | } |
765 | |
766 | /// Equality operator. |
767 | friend constexpr bool operator==( |
768 | nullptr_t, const any_completion_handler& b) noexcept |
769 | { |
770 | return nullptr == b.impl_; |
771 | } |
772 | |
773 | /// Inequality operator. |
774 | friend constexpr bool operator!=( |
775 | const any_completion_handler& a, nullptr_t) noexcept |
776 | { |
777 | return a.impl_ != nullptr; |
778 | } |
779 | |
780 | /// Inequality operator. |
781 | friend constexpr bool operator!=( |
782 | nullptr_t, const any_completion_handler& b) noexcept |
783 | { |
784 | return nullptr != b.impl_; |
785 | } |
786 | }; |
787 | |
788 | template <typename... Signatures, typename Candidate> |
789 | struct associated_executor<any_completion_handler<Signatures...>, Candidate> |
790 | { |
791 | using type = any_completion_executor; |
792 | |
793 | static type get(const any_completion_handler<Signatures...>& handler, |
794 | const Candidate& candidate = Candidate()) noexcept |
795 | { |
796 | any_completion_executor any_candidate(std::nothrow, candidate); |
797 | return handler.fn_table_ |
798 | ? handler.fn_table_->executor(handler.impl_, any_candidate) |
799 | : any_candidate; |
800 | } |
801 | }; |
802 | |
803 | template <typename... Signatures, typename Candidate> |
804 | struct associated_immediate_executor< |
805 | any_completion_handler<Signatures...>, Candidate> |
806 | { |
807 | using type = any_completion_executor; |
808 | |
809 | static type get(const any_completion_handler<Signatures...>& handler, |
810 | const Candidate& candidate = Candidate()) noexcept |
811 | { |
812 | any_io_executor any_candidate(std::nothrow, candidate); |
813 | return handler.fn_table_ |
814 | ? handler.fn_table_->immediate_executor(handler.impl_, any_candidate) |
815 | : any_candidate; |
816 | } |
817 | }; |
818 | |
819 | } // namespace asio |
820 | } // namespace boost |
821 | |
822 | #include <boost/asio/detail/pop_options.hpp> |
823 | |
824 | #endif // BOOST_ASIO_ANY_COMPLETION_HANDLER_HPP |
825 | |