GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/ex/any_dispatcher.hpp
Date: 2026-01-15 23:24:40
Exec Total Coverage
Lines: 8 8 100.0%
Functions: 5 5 100.0%
Branches: 0 0 -%

Line Branch Exec Source
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/capy
8 //
9
10 #ifndef BOOST_CAPY_ANY_DISPATCHER_HPP
11 #define BOOST_CAPY_ANY_DISPATCHER_HPP
12
13 #include <boost/capy/detail/config.hpp>
14 #include <boost/capy/ex/any_coro.hpp>
15 #include <boost/capy/concept/dispatcher.hpp>
16
17 #include <concepts>
18 #include <type_traits>
19
20 namespace boost {
21 namespace capy {
22
23 /** A type-erased wrapper for dispatcher objects.
24
25 This class provides type erasure for any type satisfying the `dispatcher`
26 concept, enabling runtime polymorphism without virtual functions. It stores
27 a pointer to the original dispatcher and a function pointer to invoke it,
28 allowing dispatchers of different types to be stored uniformly.
29
30 @par Thread Safety
31 The `any_dispatcher` itself is not thread-safe for concurrent modification,
32 but `operator()` is const and safe to call concurrently if the underlying
33 dispatcher supports concurrent dispatch.
34
35 @par Lifetime
36 The `any_dispatcher` stores a pointer to the original dispatcher object.
37 The caller must ensure the referenced dispatcher outlives the `any_dispatcher`
38 instance. This is typically satisfied when the dispatcher is an executor
39 stored in a coroutine promise or service provider.
40
41 @see dispatcher
42 */
43 class any_dispatcher
44 {
45 void const* d_ = nullptr;
46 any_coro(*f_)(void const*, any_coro) = nullptr;
47
48 public:
49 /** Default constructor.
50
51 Constructs an empty `any_dispatcher`. Calling `operator()` on a
52 default-constructed instance results in undefined behavior.
53 */
54 495 any_dispatcher() = default;
55
56 /** Copy constructor.
57
58 Copies the internal pointer and function, preserving identity.
59 This enables the same-dispatcher optimization when passing
60 any_dispatcher through coroutine chains.
61 */
62 any_dispatcher(any_dispatcher const&) = default;
63
64 /** Copy assignment operator. */
65 any_dispatcher& operator=(any_dispatcher const&) = default;
66
67 /** Constructs from any dispatcher type.
68
69 Captures a reference to the given dispatcher and stores a type-erased
70 invocation function. The dispatcher must remain valid for the lifetime
71 of this `any_dispatcher` instance.
72
73 @param d The dispatcher to wrap. Must satisfy the `dispatcher` concept.
74 A pointer to this object is stored internally; the dispatcher
75 must outlive this wrapper.
76 */
77 template<dispatcher D>
78 requires (!std::same_as<std::decay_t<D>, any_dispatcher>)
79 244 any_dispatcher(D const& d)
80 244 : d_(&d)
81 358 , f_([](void const* pd, any_coro h) {
82 114 return static_cast<D const*>(pd)->operator()(h);
83 })
84 {
85 244 }
86
87 /** Returns true if this instance holds a valid dispatcher.
88
89 @return `true` if constructed with a dispatcher, `false` if
90 default-constructed.
91 */
92 explicit operator bool() const noexcept
93 {
94 return d_ != nullptr;
95 }
96
97 /** Compares two dispatchers for identity.
98
99 Two `any_dispatcher` instances are equal if they wrap the same
100 underlying dispatcher object (pointer equality). This enables
101 the affinity optimization: when `caller_dispatcher == my_dispatcher`,
102 symmetric transfer can proceed without a `running_in_this_thread()`
103 check.
104
105 @param other The dispatcher to compare against.
106
107 @return `true` if both wrap the same dispatcher object.
108 */
109 bool operator==(any_dispatcher const& other) const noexcept
110 {
111 return d_ == other.d_;
112 }
113
114 /** Dispatches a coroutine handle through the wrapped dispatcher.
115
116 Invokes the stored dispatcher with the given coroutine handle,
117 returning a handle suitable for symmetric transfer.
118
119 @param h The coroutine handle to dispatch for resumption.
120
121 @return A coroutine handle that the caller may use for symmetric
122 transfer, or `std::noop_coroutine()` if the dispatcher
123 posted the work for later execution.
124
125 @pre This instance was constructed with a valid dispatcher
126 (not default-constructed).
127 */
128 114 any_coro operator()(any_coro h) const
129 {
130 114 return f_(d_, h);
131 }
132 };
133
134 } // capy
135 } // boost
136
137 #endif
138