Line data Source code
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 439 : 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 88 : any_dispatcher(D const& d)
80 88 : : d_(&d)
81 196 : , f_([](void const* pd, any_coro h) {
82 108 : return static_cast<D const*>(pd)->operator()(h);
83 : })
84 : {
85 88 : }
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 108 : any_coro operator()(any_coro h) const
129 : {
130 108 : return f_(d_, h);
131 : }
132 : };
133 :
134 : } // capy
135 : } // boost
136 :
137 : #endif
|