@@ -14,41 +14,188 @@ import std;
1414#endif
1515#ifdef BEMAN_HAS_MODULES
1616import beman.execution.detail.sender;
17+ import beman.execution.detail.call_result_t ;
18+ import beman.execution.detail.callable;
19+ import beman.execution.detail.class_type;
20+ import beman.execution.detail.nothrow_callable;
21+ import beman.execution.detail.movable_value;
22+ import beman.execution.detail.product_type;
23+
1724#else
1825#include < beman/execution/detail/sender.hpp>
26+ #include < beman/execution/detail/class_type.hpp>
27+ #include < beman/execution/detail/call_result_t.hpp>
28+ #include < beman/execution/detail/callable.hpp>
29+ #include < beman/execution/detail/nothrow_callable.hpp>
30+ #include < beman/execution/detail/movable_value.hpp>
31+ #include < beman/execution/detail/product_type.hpp>
32+
1933#endif
2034
2135// ----------------------------------------------------------------------------
2236
2337namespace beman ::execution::detail::pipeable {
24- struct sender_adaptor_closure_base {};
38+ /* !
39+ * \brief ADL anchor tag type inherited by sender_adaptor_closure.
40+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
41+ * \internal
42+ */
43+ struct closure_t {};
2544} // namespace beman::execution::detail::pipeable
2645
2746namespace beman ::execution {
28- // NOLINTBEGIN(bugprone-crtp-constructor-accessibility)
29- template <typename >
30- struct sender_adaptor_closure : ::beman::execution::detail::pipeable::sender_adaptor_closure_base {};
31- // NOLINTEND(bugprone-crtp-constructor-accessibility)
47+ /* !
48+ * \brief CRTP base class for pipeable sender adaptor closure objects.
49+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
50+ */
51+ template <detail::class_type D>
52+ struct sender_adaptor_closure : detail::pipeable::closure_t {};
3253
3354} // namespace beman::execution
3455
3556namespace beman ::execution::detail {
36- template <typename Closure>
57+
58+ /* !
59+ * \brief Helper to detect a unique sender_adaptor_closure base class.
60+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
61+ * \internal
62+ */
63+ template <class T >
64+ auto get_sender_adaptor_closure_base (const sender_adaptor_closure<T>&) -> T;
65+
66+ /* !
67+ * \brief Checks that T has exactly one sender_adaptor_closure base where U == decay_t<T>.
68+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
69+ * \internal
70+ */
71+ template <class T >
72+ concept has_unique_sender_adaptor_closure_base = requires (const T& s) {
73+ { get_sender_adaptor_closure_base (s) } -> std::same_as<std::decay_t <T>>;
74+ };
75+
76+ /* !
77+ * \brief Determine if a type is a pipeable sender adaptor closure.
78+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
79+ * \internal
80+ */
81+ template <class T >
3782concept is_sender_adaptor_closure =
38- ::std::derived_from<::std::decay_t <Closure>, ::beman::execution::sender_adaptor_closure<::std::decay_t <Closure>>>;
83+ std::derived_from<std::decay_t <T>, sender_adaptor_closure<std::decay_t <T>>> and
84+ has_unique_sender_adaptor_closure_base<std::decay_t <T>> and (not sender<std::decay_t <T>>);
85+
86+ /* !
87+ * \brief Checks that Closure is a pipeable sender adaptor closure invocable with Sender.
88+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
89+ * \internal
90+ */
91+ template <class Closure , class Sender >
92+ concept sender_adaptor_closure_for =
93+ is_sender_adaptor_closure<Closure> and sender<Sender> and requires (Closure&& closure, Sender&& sndr) {
94+ { std::forward<Closure>(closure)(std::forward<Sender>(sndr)) } -> sender;
95+ };
96+
97+ /* !
98+ * \brief Utility alias to copy cv-ref qualifiers from one type onto another.
99+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
100+ * \internal
101+ */
102+ template <class As , class Reqs >
103+ using apply_cvref_t = decltype (std::forward_like<As>(std::declval<Reqs&>()));
104+
105+ /* !
106+ * \brief Perfect forwarding call wrapper produced by closure-closure composition via operator|.
107+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
108+ * \internal
109+ */
110+ template <class Inner , class Outer >
111+ struct composed_sender_adaptor_closure : sender_adaptor_closure<composed_sender_adaptor_closure<Inner, Outer>> {
112+ [[no_unique_address]] Inner inner;
113+ [[no_unique_address]] Outer outer;
114+
115+ template <class Self , sender Sender>
116+ requires callable<apply_cvref_t <Self, Inner>, Sender> and
117+ callable<apply_cvref_t <Self, Outer>, call_result_t <apply_cvref_t <Self, Inner>, Sender>>
118+ constexpr auto operator ()(this Self&& self, Sender&& sndr) noexcept (
119+ nothrow_callable<apply_cvref_t <Self, Inner>, Sender> and
120+ nothrow_callable<apply_cvref_t <Self, Outer>, call_result_t <apply_cvref_t <Self, Inner>, Sender>>)
121+ -> call_result_t<apply_cvref_t<Self, Outer>, call_result_t<apply_cvref_t<Self, Inner>, Sender>> {
122+ return std::forward_like<Self>(self.outer )(std::forward_like<Self>(self.inner )(std::forward<Sender>(sndr)));
123+ }
124+ };
125+
126+ // ctad
127+ template <class Inner , class Outer >
128+ composed_sender_adaptor_closure (Inner&&, Outer&&)
129+ -> composed_sender_adaptor_closure<std::decay_t <Inner>, std::decay_t <Outer>>;
130+
131+ /* !
132+ * \brief Perfect forwarding call wrapper produced by adaptor(args...) for multi-argument adaptors.
133+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
134+ * \internal
135+ */
136+ template <class Adaptor , movable_value... BoundArgs>
137+ struct bound_sender_adaptor_closure : detail::product_type<std::decay_t <BoundArgs>...>,
138+ sender_adaptor_closure<bound_sender_adaptor_closure<Adaptor, BoundArgs...>> {
139+
140+ [[no_unique_address]] Adaptor adaptor;
141+
142+ template <class Self , sender Sender>
143+ requires callable<apply_cvref_t <Self, Adaptor>, Sender, apply_cvref_t <Self, BoundArgs>...>
144+ constexpr auto operator ()(this Self&& self, Sender&& sndr) noexcept (
145+ nothrow_callable<apply_cvref_t <Self, Adaptor>, Sender, apply_cvref_t <Self, BoundArgs>...>)
146+ -> call_result_t<apply_cvref_t<Self, Adaptor>, Sender, apply_cvref_t<Self, BoundArgs>...> {
147+ return self.apply ([&](auto &&... bound_args) {
148+ return std::forward_like<Self>(self.adaptor )(std::forward<Sender>(sndr),
149+ std::forward_like<Self>(bound_args)...);
150+ });
151+ }
152+ };
153+
154+ template <class Tag , class ... Args>
155+ bound_sender_adaptor_closure (Tag&&, Args&&...)
156+ -> bound_sender_adaptor_closure<std::decay_t <Tag>, std::decay_t <Args>...>;
157+
158+ /* !
159+ * \brief Factory function producing a bound_sender_adaptor_closure from an adaptor and arguments.
160+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
161+ * \internal
162+ */
163+ template <class Tag , class ... Args>
164+ requires (movable_value<Args> && ...)
165+ constexpr auto
166+ make_sender_adaptor (Tag&& tag,
167+ Args&&... args) noexcept (std::is_nothrow_constructible_v<std::decay_t <Tag>, Tag> and
168+ (std::is_nothrow_constructible_v<std::decay_t <Args>, Args> and ...))
169+ -> bound_sender_adaptor_closure<std::decay_t <Tag>, std::decay_t <Args>...> {
170+ return {{std::forward<Args>(args)...}, {}, tag};
39171}
172+ } // namespace beman::execution::detail
40173
41174namespace beman ::execution::detail::pipeable {
42- template <::beman::execution::sender Sender, typename Adaptor>
43- requires (!::beman::execution::sender<Adaptor>) &&
44- ::std::derived_from<::std::decay_t <Adaptor>,
45- ::beman::execution::sender_adaptor_closure<::std::decay_t <Adaptor>>> &&
46- requires (Sender&& sender, Adaptor&& adaptor) {
47- { adaptor (::std::forward<Sender>(sender)) } -> ::beman::execution::sender;
48- }
49- auto operator |(Sender&& sender, Adaptor&& adaptor) {
50- return adaptor (::std::forward<Sender>(sender));
175+
176+ /* !
177+ * \brief Pipe operator connecting a sender to a pipeable sender adaptor closure.
178+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
179+ */
180+ template <sender Sender, detail::sender_adaptor_closure_for<Sender> Closure>
181+ constexpr auto operator |(Sender&& sndr, Closure&& cl) noexcept (detail::nothrow_callable<Closure, Sender>)
182+ -> detail::call_result_t <Closure, Sender> {
183+ return std::forward<Closure>(cl)(std::forward<Sender>(sndr));
184+ }
185+
186+ /* !
187+ * \brief Pipe operator composing two pipeable sender adaptor closure objects.
188+ * \headerfile beman/execution/execution.hpp <beman/execution/execution.hpp>
189+ */
190+ template <detail::is_sender_adaptor_closure Inner, detail::is_sender_adaptor_closure Outer>
191+ requires std::constructible_from<std::decay_t <Inner>, Inner> && std::constructible_from<std::decay_t <Outer>, Outer>
192+ constexpr auto operator |(Inner&& inner,
193+ Outer&& outer) noexcept (std::is_nothrow_constructible_v<std::decay_t <Inner>, Inner> &&
194+ std::is_nothrow_constructible_v<std::decay_t <Outer>, Outer>)
195+ -> detail::composed_sender_adaptor_closure<std::decay_t <Inner>, std::decay_t <Outer>> {
196+ return {{}, std::forward<Inner>(inner), std::forward<Outer>(outer)};
51197}
198+
52199} // namespace beman::execution::detail::pipeable
53200
54201// ----------------------------------------------------------------------------
0 commit comments