GCC Code Coverage Report


Directory: ./
File: libs/capy/include/boost/capy/ex/any_dispatcher.hpp
Date: 2026-01-17 11:19:32
Exec Total Coverage
Lines: 15 15 100.0%
Functions: 14 15 93.3%
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 #include <boost/capy/concept/executor.hpp>
17
18 #include <concepts>
19 #include <type_traits>
20 #include <utility>
21
22 namespace boost {
23 namespace capy {
24
25 /** A type-erased wrapper for dispatcher objects.
26
27 This class provides type erasure for any type satisfying the `dispatcher`
28 concept, enabling runtime polymorphism without virtual functions. It stores
29 a pointer to the original dispatcher and a function pointer to invoke it,
30 allowing dispatchers of different types to be stored uniformly.
31
32 @par Thread Safety
33 The `any_dispatcher` itself is not thread-safe for concurrent modification,
34 but `operator()` is const and safe to call concurrently if the underlying
35 dispatcher supports concurrent dispatch.
36
37 @par Lifetime
38 The `any_dispatcher` stores a pointer to the original dispatcher object.
39 The caller must ensure the referenced dispatcher outlives the `any_dispatcher`
40 instance. This is typically satisfied when the dispatcher is an executor
41 stored in a coroutine promise or service provider.
42
43 @see dispatcher
44 */
45 class any_dispatcher
46 {
47 void const* d_ = nullptr;
48 any_coro(*f_)(void const*, any_coro) = nullptr;
49
50 public:
51 /** Default constructor.
52
53 Constructs an empty `any_dispatcher`. Calling `operator()` on a
54 default-constructed instance results in undefined behavior.
55 */
56 533 any_dispatcher() = default;
57
58 /** Copy constructor.
59
60 Copies the internal pointer and function, preserving identity.
61 This enables the same-dispatcher optimization when passing
62 any_dispatcher through coroutine chains.
63 */
64 any_dispatcher(any_dispatcher const&) = default;
65
66 /** Copy assignment operator. */
67 any_dispatcher& operator=(any_dispatcher const&) = default;
68
69 /** Constructs from any dispatcher type.
70
71 Captures a reference to the given dispatcher and stores a type-erased
72 invocation function. The dispatcher must remain valid for the lifetime
73 of this `any_dispatcher` instance.
74
75 @param d The dispatcher to wrap. Must satisfy the `dispatcher` concept.
76 A pointer to this object is stored internally; the dispatcher
77 must outlive this wrapper.
78 */
79 template<dispatcher D>
80 requires (!std::same_as<std::decay_t<D>, any_dispatcher>)
81 596 any_dispatcher(D const& d)
82 596 : d_(&d)
83 705 , f_([](void const* pd, any_coro h) {
84 121 return static_cast<D const*>(pd)->operator()(h);
85 })
86 {
87 596 }
88
89 /** Returns true if this instance holds a valid dispatcher.
90
91 @return `true` if constructed with a dispatcher, `false` if
92 default-constructed.
93 */
94 explicit operator bool() const noexcept
95 {
96 return d_ != nullptr;
97 }
98
99 /** Compares two dispatchers for identity.
100
101 Two `any_dispatcher` instances are equal if they wrap the same
102 underlying dispatcher object (pointer equality). This enables
103 the affinity optimization: when `caller_dispatcher == my_dispatcher`,
104 symmetric transfer can proceed without a `running_in_this_thread()`
105 check.
106
107 @param other The dispatcher to compare against.
108
109 @return `true` if both wrap the same dispatcher object.
110 */
111 bool operator==(any_dispatcher const& other) const noexcept
112 {
113 return d_ == other.d_;
114 }
115
116 /** Dispatches a coroutine handle through the wrapped dispatcher.
117
118 Invokes the stored dispatcher with the given coroutine handle,
119 returning a handle suitable for symmetric transfer.
120
121 @param h The coroutine handle to dispatch for resumption.
122
123 @return A coroutine handle that the caller may use for symmetric
124 transfer, or `std::noop_coroutine()` if the dispatcher
125 posted the work for later execution.
126
127 @pre This instance was constructed with a valid dispatcher
128 (not default-constructed).
129 */
130 121 any_coro operator()(any_coro h) const
131 {
132 121 return f_(d_, h);
133 }
134 };
135
136 //------------------------------------------------------------------------------
137
138 /** A dispatcher that calls executor::post().
139
140 Adapts an executor's post() operation to the dispatcher
141 interface. When invoked, posts the coroutine and returns
142 noop_coroutine for the caller to transfer to.
143
144 @tparam Executor The executor type.
145 */
146 template<executor Executor>
147 class post_dispatcher
148 {
149 Executor ex_;
150
151 public:
152 24 explicit post_dispatcher(Executor ex) noexcept
153 24 : ex_(std::move(ex))
154 24 {}
155
156 6 Executor const& get_inner_executor() const noexcept { return ex_; }
157
158 12 any_coro operator()(any_coro h) const
159 {
160 12 ex_.post(h);
161 12 return std::noop_coroutine();
162 }
163 };
164
165 /** A dispatcher that calls executor::defer().
166
167 Adapts an executor's defer() operation to the dispatcher
168 interface. When invoked, defers the coroutine and returns
169 noop_coroutine for the caller to transfer to.
170
171 @tparam Executor The executor type.
172 */
173 template<executor Executor>
174 class defer_dispatcher
175 {
176 Executor ex_;
177
178 public:
179 explicit defer_dispatcher(Executor ex) noexcept
180 : ex_(std::move(ex))
181 {}
182
183 Executor const& get_inner_executor() const noexcept { return ex_; }
184
185 any_coro operator()(any_coro h) const
186 {
187 ex_.defer(h);
188 return std::noop_coroutine();
189 }
190 };
191
192 template<executor E> post_dispatcher(E) -> post_dispatcher<E>;
193 template<executor E> defer_dispatcher(E) -> defer_dispatcher<E>;
194
195 } // capy
196 } // boost
197
198 #endif
199