Skip to content

Commit ff50002

Browse files
committed
Implement execution::on adaptor
1 parent 0c18272 commit ff50002

File tree

5 files changed

+562
-113
lines changed

5 files changed

+562
-113
lines changed

stl/inc/execution

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5338,6 +5338,25 @@ namespace execution {
53385338
template <class _Sender>
53395339
struct sender_traits : decltype(_Get_sender_traits_base<_Sender>()) {};
53405340

5341+
template <class _Sender>
5342+
_NODISCARD _CONSTEVAL auto _Get_sender_traits_derived() noexcept {
5343+
if constexpr (_Has_sender_types<sender_traits<_Sender>>) {
5344+
return _Typed_sender<sender_traits<_Sender>>{};
5345+
} else if constexpr (derived_from<_Sender, sender_base>) {
5346+
return _Sender_traits_base{};
5347+
#if 0 // TRANSITION: coroutine support
5348+
} else if constexpr (_Is_awaitable<_Sender>) {
5349+
if constexpr (is_void_v<_Await_result_t<_Sender>>) {
5350+
return _Void_sender<false>{};
5351+
} else {
5352+
return _Sender_of<false, _Await_result_t<_Sender>>{};
5353+
}
5354+
#endif // 0 TRANSITION: coroutine support
5355+
} else {
5356+
return _No_sender_traits{};
5357+
}
5358+
}
5359+
53415360
template <class _Sender>
53425361
concept sender = move_constructible<remove_cvref_t<_Sender>> && !requires {
53435362
typename sender_traits<remove_cvref_t<_Sender>>::_Unspecialized;
@@ -5540,6 +5559,127 @@ namespace execution {
55405559
_NODISCARD constexpr _Just_done_sender just_done() noexcept {
55415560
return {};
55425561
};
5562+
5563+
template <class _Tag, class... _Args>
5564+
concept _Tag_invocable_to_sender = tag_invocable<_Tag, _Args...> && sender<tag_invoke_result_t<_Tag, _Args...>>;
5565+
5566+
namespace _On {
5567+
template <scheduler _Scheduler, receiver _InputReceiver>
5568+
struct _WrappedReceiver {
5569+
_Scheduler _Sched;
5570+
_InputReceiver _Input_receiver;
5571+
5572+
friend constexpr void tag_invoke(set_done_t, _WrappedReceiver _Wrapped_receiver) noexcept {
5573+
_EXEC set_done(_STD move(_Wrapped_receiver._Input_receiver));
5574+
}
5575+
template <class _Error>
5576+
friend constexpr void tag_invoke(set_error_t, _WrappedReceiver _Wrapped_receiver, _Error&& _Err) noexcept {
5577+
_EXEC set_error(_STD move(_Wrapped_receiver._Input_receiver), _STD forward<_Error>(_Err));
5578+
}
5579+
template <class... _ArgTys>
5580+
friend constexpr void tag_invoke(
5581+
set_value_t, _WrappedReceiver _Wrapped_receiver, _ArgTys&&... _Args) noexcept {
5582+
_EXEC set_value(_STD move(_Wrapped_receiver._Input_receiver), _STD forward<_ArgTys>(_Args)...);
5583+
}
5584+
_NODISCARD friend constexpr auto tag_invoke(
5585+
get_scheduler_t, const _WrappedReceiver& _Wrapped_receiver) noexcept {
5586+
return _Wrapped_receiver._Sched;
5587+
}
5588+
};
5589+
5590+
template <scheduler _Scheduler, sender _InputSender, receiver _InputReceiver>
5591+
struct _InnerReceiver {
5592+
using _WrappedReceiver_t = _WrappedReceiver<_Scheduler, _InputReceiver>;
5593+
5594+
// Clang complains about incomplete type, so for now no _OutputSenderState*
5595+
_WrappedReceiver_t* _Wrapped_receiver;
5596+
_InputSender* _Input_sender;
5597+
invoke_result_t<connect_t, _InputSender, _WrappedReceiver_t>* _Op_state3;
5598+
5599+
friend constexpr void tag_invoke(set_done_t, _InnerReceiver _Inner_receiver) noexcept {
5600+
_EXEC set_done(_STD move(_Inner_receiver._Wrapped_receiver->_Input_receiver));
5601+
}
5602+
template <class _Error>
5603+
friend constexpr void tag_invoke(set_error_t, _InnerReceiver _Inner_receiver, _Error&& _Err) noexcept {
5604+
_EXEC set_error(
5605+
_STD move(_Inner_receiver._Wrapped_receiver->_Input_receiver), _STD forward<_Error>(_Err));
5606+
}
5607+
friend constexpr void tag_invoke(set_value_t, _InnerReceiver _Inner_receiver) noexcept {
5608+
auto* _Wrapped_receiver = _Inner_receiver._Wrapped_receiver;
5609+
_TRY_BEGIN
5610+
// *this is stored inside of `_Op_state2` so the call below will invalidate it. store the members
5611+
auto* _Input_sender = _Inner_receiver._Input_sender;
5612+
auto* _Op_state3 = _Inner_receiver._Op_state3;
5613+
_STD construct_at(_Op_state3, _EXEC connect(_STD move(*_Input_sender), _STD move(*_Wrapped_receiver)));
5614+
_EXEC start(*_Op_state3);
5615+
_CATCH_ALL
5616+
_EXEC set_error(_STD move(_Wrapped_receiver->_Input_receiver), _STD current_exception());
5617+
_CATCH_END
5618+
}
5619+
};
5620+
5621+
template <scheduler _Scheduler, sender _InputSender, receiver _InputReceiver>
5622+
struct _OutputSenderState {
5623+
using _WrappedReceiver_t = _WrappedReceiver<_Scheduler, _InputReceiver>;
5624+
using _InnerReceiver_t = _InnerReceiver<_Scheduler, _InputSender, _InputReceiver>;
5625+
5626+
_WrappedReceiver_t _Wrapped_receiver;
5627+
_InputSender _Input_sender;
5628+
union {
5629+
invoke_result_t<connect_t, invoke_result_t<schedule_t, _Scheduler>, _InnerReceiver_t> _Op_state2;
5630+
invoke_result_t<connect_t, _InputSender, _WrappedReceiver_t> _Op_state3;
5631+
};
5632+
5633+
explicit constexpr _OutputSenderState(_WrappedReceiver_t _Wrapped_receiver_, _InputSender _Input_sender_)
5634+
: _Wrapped_receiver(_STD move(_Wrapped_receiver_)), _Input_sender(_STD move(_Input_sender_)),
5635+
_Op_state2(_EXEC connect(_EXEC schedule(_Wrapped_receiver._Sched),
5636+
_InnerReceiver_t{_STD addressof(_Wrapped_receiver), _STD addressof(_Input_sender),
5637+
_STD addressof(_Op_state3)})) {}
5638+
5639+
friend constexpr void tag_invoke(start_t, _OutputSenderState& _State) {
5640+
_EXEC start(_State._Op_state2);
5641+
}
5642+
};
5643+
5644+
template <scheduler _Scheduler, sender _InputSender>
5645+
struct _OutputSender {
5646+
_Scheduler _Sched;
5647+
_InputSender _Input_sender;
5648+
5649+
template <receiver _InputReceiver>
5650+
_NODISCARD friend constexpr auto
5651+
tag_invoke(connect_t, _OutputSender _Output_sender, _InputReceiver&& _Input_receiver) noexcept(
5652+
is_nothrow_move_constructible_v<_Scheduler>&& is_nothrow_move_constructible_v<_InputSender>&&
5653+
is_nothrow_constructible_v<remove_cvref_t<_InputReceiver>, _InputReceiver>) {
5654+
return _OutputSenderState<_Scheduler, _InputSender, _InputReceiver>{
5655+
_WrappedReceiver<_Scheduler, _InputReceiver>{
5656+
_STD move(_Output_sender._Sched), _STD forward<_InputReceiver>(_Input_receiver)},
5657+
_STD move(_Output_sender._Input_sender)};
5658+
}
5659+
};
5660+
5661+
struct _Cpo {
5662+
template <scheduler _Scheduler, sender _InputSender>
5663+
_NODISCARD constexpr auto operator()(_Scheduler&& _Sched, _InputSender&& _Input_sender) const noexcept {
5664+
if constexpr (_Tag_invocable_to_sender<_Cpo, _Scheduler, _InputSender>) {
5665+
return tag_invoke(
5666+
*this, _STD forward<_Scheduler>(_Sched), _STD forward<_InputSender>(_Input_sender));
5667+
} else {
5668+
return _OutputSender<_Scheduler, _InputSender>{
5669+
_STD forward<_Scheduler>(_Sched), _STD forward<_InputSender>(_Input_sender)};
5670+
}
5671+
}
5672+
};
5673+
} // namespace _On
5674+
using on_t = _On::_Cpo;
5675+
5676+
inline namespace _Cpos {
5677+
inline constexpr _On::_Cpo on;
5678+
}
5679+
5680+
template <scheduler _Scheduler, sender _InputSender>
5681+
struct sender_traits<_On::_OutputSender<_Scheduler, _InputSender>>
5682+
: decltype(_Get_sender_traits_derived<_InputSender>()) {};
55435683
} // namespace execution
55445684
#endif // __cpp_lib_executors
55455685
_STD_END

stl/inc/ranges

Lines changed: 0 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -55,119 +55,6 @@ namespace ranges {
5555
template <bool _IsWrapped, class _Ty>
5656
using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>;
5757

58-
namespace _Pipe {
59-
// clang-format off
60-
template <class _Left, class _Right>
61-
concept _Can_pipe = requires(_Left&& __l, _Right&& __r) {
62-
static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l));
63-
};
64-
65-
template <class _Left, class _Right>
66-
concept _Can_compose = constructible_from<remove_cvref_t<_Left>, _Left>
67-
&& constructible_from<remove_cvref_t<_Right>, _Right>;
68-
// clang-format on
69-
70-
template <class, class>
71-
struct _Pipeline;
72-
73-
template <class _Derived>
74-
struct _Base {
75-
// clang-format off
76-
template <class _Other>
77-
requires _Can_compose<_Derived, _Other>
78-
constexpr auto operator|(_Base<_Other>&& __r) && noexcept(
79-
noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)})) {
80-
// clang-format on
81-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
82-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
83-
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)};
84-
}
85-
86-
// clang-format off
87-
template <class _Other>
88-
requires _Can_compose<_Derived, const _Other&>
89-
constexpr auto operator|(const _Base<_Other>& __r) && noexcept(
90-
noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)})) {
91-
// clang-format on
92-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
93-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
94-
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)};
95-
}
96-
97-
// clang-format off
98-
template <class _Other>
99-
requires _Can_compose<const _Derived&, _Other>
100-
constexpr auto operator|(_Base<_Other>&& __r) const& noexcept(
101-
noexcept(_Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)})) {
102-
// clang-format on
103-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
104-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
105-
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)};
106-
}
107-
108-
// clang-format off
109-
template <class _Other>
110-
requires _Can_compose<const _Derived&, const _Other&>
111-
constexpr auto operator|(const _Base<_Other>& __r) const& noexcept(
112-
noexcept(_Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)})) {
113-
// clang-format on
114-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
115-
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
116-
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)};
117-
}
118-
119-
template <_Can_pipe<const _Derived&> _Left>
120-
friend constexpr auto operator|(_Left&& __l, const _Base& __r)
121-
#ifdef __EDG__ // TRANSITION, VSO-1222776
122-
noexcept(noexcept(_STD declval<const _Derived&>()(_STD forward<_Left>(__l))))
123-
#else // ^^^ workaround / no workaround vvv
124-
noexcept(noexcept(static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l))))
125-
#endif // TRANSITION, VSO-1222776
126-
{
127-
return static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l));
128-
}
129-
130-
template <_Can_pipe<_Derived> _Left>
131-
friend constexpr auto operator|(_Left&& __l, _Base&& __r)
132-
#ifdef __EDG__ // TRANSITION, VSO-1222776
133-
noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Left>(__l))))
134-
#else // ^^^ workaround / no workaround vvv
135-
noexcept(noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l))))
136-
#endif // TRANSITION, VSO-1222776
137-
{
138-
return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l));
139-
}
140-
};
141-
142-
template <class _Left, class _Right>
143-
struct _Pipeline : _Base<_Pipeline<_Left, _Right>> {
144-
/* [[no_unique_address]] */ _Left __l;
145-
/* [[no_unique_address]] */ _Right __r;
146-
147-
template <class _Ty1, class _Ty2>
148-
constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept(
149-
is_nothrow_convertible_v<_Ty1, _Left>&& is_nothrow_convertible_v<_Ty2, _Right>)
150-
: __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {}
151-
152-
template <class _Ty>
153-
_NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept(
154-
noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
155-
__r(__l(static_cast<_Ty&&>(_Val)));
156-
}
157-
{ return __r(__l(_STD forward<_Ty>(_Val))); }
158-
159-
template <class _Ty>
160-
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
161-
noexcept(noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
162-
__r(__l(static_cast<_Ty&&>(_Val)));
163-
}
164-
{ return __r(__l(_STD forward<_Ty>(_Val))); }
165-
};
166-
167-
template <class _Ty1, class _Ty2>
168-
_Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>;
169-
} // namespace _Pipe
170-
17158
template <range _Rng, class _Derived>
17259
class _Cached_position : public view_interface<_Derived> {
17360
static_assert(_Always_false<_Rng>, "A range must be at least forward for position caching to be worthwhile.");

stl/inc/xmemory

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2166,6 +2166,121 @@ constexpr _Ty* uninitialized_construct_using_allocator(_Ty* _Ptr, const _Alloc&
21662166
_STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...));
21672167
}
21682168
#endif // _HAS_CXX20
2169+
2170+
#ifdef __cpp_lib_ranges
2171+
namespace ranges::_Pipe {
2172+
// clang-format off
2173+
template <class _Left, class _Right>
2174+
concept _Can_pipe = requires(_Left&& __l, _Right&& __r) {
2175+
static_cast<_Right&&>(__r)(static_cast<_Left&&>(__l));
2176+
};
2177+
2178+
template <class _Left, class _Right>
2179+
concept _Can_compose = constructible_from<remove_cvref_t<_Left>, _Left>
2180+
&& constructible_from<remove_cvref_t<_Right>, _Right>;
2181+
// clang-format on
2182+
2183+
template <class, class>
2184+
struct _Pipeline;
2185+
2186+
template <class _Derived>
2187+
struct _Base {
2188+
// clang-format off
2189+
template <class _Other>
2190+
requires _Can_compose<_Derived, _Other>
2191+
constexpr auto operator|(_Base<_Other>&& __r) && noexcept(
2192+
noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)})) {
2193+
// clang-format on
2194+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
2195+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
2196+
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<_Other&&>(__r)};
2197+
}
2198+
2199+
// clang-format off
2200+
template <class _Other>
2201+
requires _Can_compose<_Derived, const _Other&>
2202+
constexpr auto operator|(const _Base<_Other>& __r) && noexcept(
2203+
noexcept(_Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)})) {
2204+
// clang-format on
2205+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
2206+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
2207+
return _Pipeline{static_cast<_Derived&&>(*this), static_cast<const _Other&>(__r)};
2208+
}
2209+
2210+
// clang-format off
2211+
template <class _Other>
2212+
requires _Can_compose<const _Derived&, _Other>
2213+
constexpr auto operator|(_Base<_Other>&& __r) const& noexcept(
2214+
noexcept(_Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)})) {
2215+
// clang-format on
2216+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
2217+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
2218+
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<_Other&&>(__r)};
2219+
}
2220+
2221+
// clang-format off
2222+
template <class _Other>
2223+
requires _Can_compose<const _Derived&, const _Other&>
2224+
constexpr auto operator|(const _Base<_Other>& __r) const& noexcept(
2225+
noexcept(_Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)})) {
2226+
// clang-format on
2227+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Derived, _Base<_Derived>>);
2228+
_STL_INTERNAL_STATIC_ASSERT(derived_from<_Other, _Base<_Other>>);
2229+
return _Pipeline{static_cast<const _Derived&>(*this), static_cast<const _Other&>(__r)};
2230+
}
2231+
2232+
template <_Can_pipe<const _Derived&> _Left>
2233+
friend constexpr auto operator|(_Left&& __l, const _Base& __r)
2234+
#ifdef __EDG__ // TRANSITION, VSO-1222776
2235+
noexcept(noexcept(_STD declval<const _Derived&>()(_STD forward<_Left>(__l))))
2236+
#else // ^^^ workaround / no workaround vvv
2237+
noexcept(noexcept(static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l))))
2238+
#endif // TRANSITION, VSO-1222776
2239+
{
2240+
return static_cast<const _Derived&>(__r)(_STD forward<_Left>(__l));
2241+
}
2242+
2243+
template <_Can_pipe<_Derived> _Left>
2244+
friend constexpr auto operator|(_Left&& __l, _Base&& __r)
2245+
#ifdef __EDG__ // TRANSITION, VSO-1222776
2246+
noexcept(noexcept(_STD declval<_Derived>()(_STD forward<_Left>(__l))))
2247+
#else // ^^^ workaround / no workaround vvv
2248+
noexcept(noexcept(static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l))))
2249+
#endif // TRANSITION, VSO-1222776
2250+
{
2251+
return static_cast<_Derived&&>(__r)(_STD forward<_Left>(__l));
2252+
}
2253+
};
2254+
2255+
template <class _Left, class _Right>
2256+
struct _Pipeline : _Base<_Pipeline<_Left, _Right>> {
2257+
/* [[no_unique_address]] */ _Left __l;
2258+
/* [[no_unique_address]] */ _Right __r;
2259+
2260+
template <class _Ty1, class _Ty2>
2261+
constexpr explicit _Pipeline(_Ty1&& _Val1, _Ty2&& _Val2) noexcept(
2262+
is_nothrow_convertible_v<_Ty1, _Left>&& is_nothrow_convertible_v<_Ty2, _Right>)
2263+
: __l(_STD forward<_Ty1>(_Val1)), __r(_STD forward<_Ty2>(_Val2)) {}
2264+
2265+
template <class _Ty>
2266+
_NODISCARD constexpr auto operator()(_Ty&& _Val) noexcept(
2267+
noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
2268+
__r(__l(static_cast<_Ty&&>(_Val)));
2269+
}
2270+
{ return __r(__l(_STD forward<_Ty>(_Val))); }
2271+
2272+
template <class _Ty>
2273+
_NODISCARD constexpr auto operator()(_Ty&& _Val) const
2274+
noexcept(noexcept(__r(__l(_STD forward<_Ty>(_Val))))) requires requires {
2275+
__r(__l(static_cast<_Ty&&>(_Val)));
2276+
}
2277+
{ return __r(__l(_STD forward<_Ty>(_Val))); }
2278+
};
2279+
2280+
template <class _Ty1, class _Ty2>
2281+
_Pipeline(_Ty1, _Ty2) -> _Pipeline<_Ty1, _Ty2>;
2282+
} // namespace ranges::_Pipe
2283+
#endif // __cpp_lib_ranges
21692284
_STD_END
21702285

21712286
#pragma pop_macro("new")

0 commit comments

Comments
 (0)