|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| 3 | + * Copyright (c) 2025 Robert Leahy. All rights reserved. |
| 4 | + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 5 | + * |
| 6 | + * Licensed under the Apache License, Version 2.0 with LLVM Exceptions (the "License"); |
| 7 | + * you may not use this file except in compliance with the License. |
| 8 | + * You may obtain a copy of the License at |
| 9 | + * |
| 10 | + * https://llvm.org/LICENSE.txt |
| 11 | + * |
| 12 | + * Unless required by applicable law or agreed to in writing, software |
| 13 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 14 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 15 | + * See the License for the specific language governing permissions and |
| 16 | + * limitations under the License. |
| 17 | + */ |
| 18 | + |
| 19 | +#pragma once |
| 20 | + |
| 21 | +#include <cstddef> |
| 22 | +#include <functional> |
| 23 | +#include <memory> |
| 24 | +#include <tuple> |
| 25 | +#include <type_traits> |
| 26 | +#include <utility> |
| 27 | + |
| 28 | +#include "enter_scope_sender.hpp" |
| 29 | +#include "enter_scopes.hpp" |
| 30 | +#include "invoke.hpp" |
| 31 | +#include "like_t.hpp" |
| 32 | +#include "object.hpp" |
| 33 | +#include "within.hpp" |
| 34 | +#include "../stdexec/execution.hpp" |
| 35 | + |
| 36 | +namespace exec { |
| 37 | + |
| 38 | +namespace detail::lifetime { |
| 39 | + |
| 40 | +template<typename F, typename... Objects> |
| 41 | +concept function = ::STDEXEC::sender< |
| 42 | + std::invoke_result_t< |
| 43 | + F, |
| 44 | + ::exec::type_of_object_t<Objects>&...>>; |
| 45 | + |
| 46 | +template<typename SenderFactory, typename... Objects> |
| 47 | +concept sender_factory = ::exec::enter_scope_sender< |
| 48 | + std::invoke_result_t< |
| 49 | + SenderFactory, |
| 50 | + ::exec::enter_scope_sender_of_object_t<Objects>...>>; |
| 51 | + |
| 52 | +struct t { |
| 53 | + template<typename F, ::exec::object... Objects> |
| 54 | + requires function<F, Objects...> |
| 55 | + constexpr ::STDEXEC::sender auto operator()(F&& f, Objects&&... objects) const |
| 56 | + noexcept( |
| 57 | + std::is_nothrow_constructible_v< |
| 58 | + std::remove_cvref_t<F>, |
| 59 | + F> && |
| 60 | + (std::is_nothrow_constructible_v< |
| 61 | + std::remove_cvref_t<Objects>, |
| 62 | + Objects> && ...)) |
| 63 | + { |
| 64 | + return (*this)( |
| 65 | + ::exec::enter_scopes, |
| 66 | + (F&&)f, |
| 67 | + (Objects&&)objects...); |
| 68 | + } |
| 69 | + template<typename SenderFactory, typename F, ::exec::object... Objects> |
| 70 | + requires |
| 71 | + sender_factory<SenderFactory, Objects...> && |
| 72 | + function<F, Objects...> |
| 73 | + constexpr ::STDEXEC::sender auto operator()( |
| 74 | + SenderFactory&& sender_factory, |
| 75 | + F&& f, |
| 76 | + Objects&&... objects) const noexcept( |
| 77 | + std::is_nothrow_constructible_v< |
| 78 | + std::remove_cvref_t<SenderFactory>, |
| 79 | + SenderFactory> && |
| 80 | + std::is_nothrow_constructible_v< |
| 81 | + std::remove_cvref_t<F>, |
| 82 | + F> && |
| 83 | + (std::is_nothrow_constructible_v< |
| 84 | + std::remove_cvref_t<Objects>, |
| 85 | + Objects> && ...)) |
| 86 | + { |
| 87 | + return ::STDEXEC::__make_sexpr<t>( |
| 88 | + std::tuple( |
| 89 | + (SenderFactory&&)sender_factory, |
| 90 | + (F&&)f, |
| 91 | + (Objects&&)objects...)); |
| 92 | + } |
| 93 | +}; |
| 94 | + |
| 95 | +template<typename T> |
| 96 | +class storage_for_object { |
| 97 | + alignas(T) std::byte buffer_[sizeof(T)]; |
| 98 | +public: |
| 99 | + constexpr T* get_storage() noexcept { |
| 100 | + return reinterpret_cast<T*>(buffer_); |
| 101 | + } |
| 102 | + constexpr T& get_object() noexcept { |
| 103 | + return *std::launder(get_storage()); |
| 104 | + } |
| 105 | +}; |
| 106 | + |
| 107 | +template<typename Object> |
| 108 | +using storage_for_object_t = storage_for_object< |
| 109 | + ::exec::type_of_object_t<Object>>; |
| 110 | + |
| 111 | +template<typename... Objects> |
| 112 | +using storage_for_objects_t = std::tuple< |
| 113 | + storage_for_object_t<Objects>...>; |
| 114 | + |
| 115 | +template<typename Tuple, typename = std::remove_cvref_t<Tuple>> |
| 116 | +class make_sender; |
| 117 | + |
| 118 | +template< |
| 119 | + typename Tuple, |
| 120 | + typename SenderFactory, |
| 121 | + typename F, |
| 122 | + typename... Objects> |
| 123 | +class make_sender<Tuple, std::tuple<SenderFactory, F, Objects...>> { |
| 124 | + static constexpr auto impl_( |
| 125 | + ::exec::like_t<Tuple, F>&& f, |
| 126 | + storage_for_object_t<Objects>&... storage) noexcept( |
| 127 | + std::is_nothrow_constructible_v< |
| 128 | + F, |
| 129 | + ::exec::like_t<Tuple, F>>) |
| 130 | + { |
| 131 | + return [f = (::exec::like_t<Tuple, F>&&)f, &storage...]() mutable noexcept( |
| 132 | + std::is_nothrow_invocable_v< |
| 133 | + F, |
| 134 | + ::exec::type_of_object_t<Objects>&...>) |
| 135 | + { |
| 136 | + return std::invoke( |
| 137 | + std::move(f), |
| 138 | + storage.get_object()...); |
| 139 | + }; |
| 140 | + } |
| 141 | +public: |
| 142 | + static constexpr bool nothrow = noexcept( |
| 143 | + ::exec::within( |
| 144 | + std::invoke( |
| 145 | + std::declval<::exec::like_t<Tuple, SenderFactory>>(), |
| 146 | + std::invoke( |
| 147 | + std::declval<::exec::like_t<Tuple, Objects>>(), |
| 148 | + std::declval<::exec::type_of_object_t<Objects>*>())...), |
| 149 | + ::STDEXEC::just() | ::exec::invoke( |
| 150 | + impl_( |
| 151 | + std::declval<::exec::like_t<Tuple, F>>(), |
| 152 | + std::declval<storage_for_object_t<Objects>&>()...)))); |
| 153 | + using storage_type = storage_for_objects_t<Objects...>; |
| 154 | + static constexpr ::STDEXEC::sender auto impl( |
| 155 | + Tuple&& t, |
| 156 | + storage_type& storage) noexcept(nothrow) |
| 157 | + { |
| 158 | + return std::apply( |
| 159 | + [&](auto&& sender_factory, auto&& f, auto&&... objects) noexcept(nothrow) |
| 160 | + { |
| 161 | + return std::apply( |
| 162 | + [&](auto&... storage_for_object) noexcept(nothrow) { |
| 163 | + return ::exec::within( |
| 164 | + std::invoke( |
| 165 | + (decltype(sender_factory)&&)sender_factory, |
| 166 | + std::invoke( |
| 167 | + (decltype(objects)&&)objects, |
| 168 | + storage_for_object.get_storage())...), |
| 169 | + ::STDEXEC::just() | ::exec::invoke( |
| 170 | + impl_((decltype(f)&&)f, storage_for_object...))); |
| 171 | + }, |
| 172 | + storage); |
| 173 | + }, |
| 174 | + (Tuple&&)t); |
| 175 | + } |
| 176 | + using sender_type = decltype( |
| 177 | + impl( |
| 178 | + std::declval<Tuple>(), |
| 179 | + std::declval<storage_for_objects_t<Objects...>&>())); |
| 180 | +}; |
| 181 | + |
| 182 | +template<typename Tuple, typename Receiver> |
| 183 | +class state { |
| 184 | + using impl_ = make_sender<Tuple>; |
| 185 | + typename impl_::storage_type storage_; |
| 186 | + ::STDEXEC::connect_result_t< |
| 187 | + typename impl_::sender_type, |
| 188 | + Receiver> op_; |
| 189 | +public: |
| 190 | + explicit constexpr state(Tuple&& t, Receiver r) noexcept(impl_::nothrow) |
| 191 | + : op_( |
| 192 | + ::STDEXEC::connect( |
| 193 | + impl_::impl((Tuple&&)t, storage_), |
| 194 | + (Receiver&&)r)) |
| 195 | + {} |
| 196 | + constexpr void start() & noexcept { |
| 197 | + ::STDEXEC::start(op_); |
| 198 | + } |
| 199 | +}; |
| 200 | + |
| 201 | +// TODO: We should be able to get a better "message" than this |
| 202 | +struct FAILED_TO_FORM_COMPLETION_SIGNATURES {}; |
| 203 | + |
| 204 | +class impl : public ::STDEXEC::__sexpr_defaults { |
| 205 | + template<typename Self, typename... Env> |
| 206 | + using completions_ = ::STDEXEC::completion_signatures_of_t< |
| 207 | + typename make_sender<::STDEXEC::__data_of<Self>>::sender_type, |
| 208 | + Env...>; |
| 209 | +public: |
| 210 | + template<typename Self, typename... Env> |
| 211 | + static consteval auto get_completion_signatures() { |
| 212 | + if constexpr (::STDEXEC::__mvalid<completions_, Self, Env...>) { |
| 213 | + return completions_<Self, Env...>{}; |
| 214 | + } else { |
| 215 | + return ::STDEXEC::__throw_compile_time_error< |
| 216 | + FAILED_TO_FORM_COMPLETION_SIGNATURES, |
| 217 | + ::STDEXEC::_WITH_PRETTY_SENDER_<Self>>(); |
| 218 | + } |
| 219 | + } |
| 220 | + static constexpr auto get_state = []<typename Sender, typename Receiver>( |
| 221 | + Sender&& sender, Receiver r) noexcept( |
| 222 | + std::is_nothrow_constructible_v< |
| 223 | + state<::STDEXEC::__data_of<Sender>, Receiver>, |
| 224 | + ::STDEXEC::__data_of<Sender>, |
| 225 | + Receiver>) -> state<::STDEXEC::__data_of<Sender>, Receiver> |
| 226 | + { |
| 227 | + auto&& [_, tuple] = (Sender&&)sender; |
| 228 | + return state<decltype(tuple), Receiver>( |
| 229 | + (decltype(tuple)&&)tuple, |
| 230 | + (Receiver&&)r); |
| 231 | + }; |
| 232 | + static constexpr auto start = [](auto& state) noexcept { |
| 233 | + state.start(); |
| 234 | + }; |
| 235 | +}; |
| 236 | + |
| 237 | +} |
| 238 | + |
| 239 | +using lifetime_t = detail::lifetime::t; |
| 240 | +inline constexpr lifetime_t lifetime; |
| 241 | + |
| 242 | +} // namespace exec |
| 243 | + |
| 244 | +namespace STDEXEC { |
| 245 | + |
| 246 | +template<> |
| 247 | +struct __sexpr_impl<::exec::lifetime_t> : ::exec::detail::lifetime::impl {}; |
| 248 | + |
| 249 | +} |
0 commit comments