/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License Version 2.0 with LLVM Exceptions * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * https://llvm.org/LICENSE.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include namespace unifex { namespace _bulk_tfx { template struct _tfx_receiver { class type; }; template using tfx_receiver = typename _tfx_receiver::type; template class _tfx_receiver::type { public: template explicit type(Func2&& f, Policy policy, Receiver2&& r) : receiver_((Receiver2&&)r) , func_((Func2&&)f) , policy_(std::move(policy)) {} template(typename... Values) (requires invocable AND std::is_void_v>) void set_next(Values&&... values) & noexcept( std::is_nothrow_invocable_v && is_nothrow_next_receiver_v) { std::invoke(func_, (Values&&)values...); unifex::set_next(receiver_); } template(typename... Values) (requires invocable AND (!std::is_void_v>)) void set_next(Values&&... values) & noexcept( std::is_nothrow_invocable_v && is_nothrow_next_receiver_v>) { unifex::set_next(receiver_, std::invoke(func_, (Values&&)values...)); } template(typename... Values) (requires receiver_of) void set_value(Values&&... values) noexcept(is_nothrow_receiver_of_v) { unifex::set_value(std::move(receiver_), (Values&&)values...); } template(typename Error) (requires receiver) void set_error(Error&& error) noexcept { unifex::set_error(std::move(receiver_), (Error&&)error); } void set_done() noexcept { unifex::set_done(std::move(receiver_)); } friend auto tag_invoke(tag_t, const type& r) noexcept { using receiver_policy = decltype(get_execution_policy(r.receiver_)); constexpr bool allowUnsequenced = is_one_of_v && is_one_of_v; constexpr bool allowParallel = is_one_of_v && is_one_of_v; if constexpr (allowUnsequenced && allowParallel) { return unifex::par_unseq; } else if constexpr (allowUnsequenced) { return unifex::unseq; } else if constexpr (allowParallel) { return unifex::par; } else { return unifex::seq; } } template(typename CPO, typename Self) (requires is_receiver_query_cpo_v AND same_as) friend auto tag_invoke(CPO cpo, const Self& self) noexcept(is_nothrow_callable_v) -> callable_result_t { return cpo(self.receiver_); } private: UNIFEX_NO_UNIQUE_ADDRESS Receiver receiver_; UNIFEX_NO_UNIQUE_ADDRESS Func func_; UNIFEX_NO_UNIQUE_ADDRESS Policy policy_; }; template struct _tfx_sender { class type; }; template using tfx_sender = typename _tfx_sender::type; template struct result_overload { using type = type_list; }; template struct result_overload>> { using type = type_list<>; }; template class _tfx_sender::type { template using result = type_list< typename result_overload>::type>; public: template< template class Variant, template class Tuple> using next_types = type_list_nested_apply_t< typename sender_traits::template next_types, Variant, Tuple>; template class Variant, template class Tuple> using value_types = sender_value_types_t; template class Variant> using error_types = sender_error_types_t; static constexpr bool sends_done = sender_traits::sends_done; template explicit type(Source2&& source, Func2&& func, Policy policy) : source_((Source2&&)source) , func_((Func2&&)func) , policy_(std::move(policy)) {} template(typename Self, typename Receiver) (requires same_as, type> AND constructible_from> AND receiver AND sender_to, tfx_receiver>>) friend auto tag_invoke(tag_t, Self&& self, Receiver&& r) noexcept( std::is_nothrow_constructible_v> && std::is_nothrow_constructible_v> && std::is_nothrow_constructible_v> && std::is_nothrow_constructible_v, Receiver>) { return unifex::connect( static_cast(self).source_, tfx_receiver>{ static_cast(self).func_, static_cast(self).policy_, static_cast(r)}); } private: UNIFEX_NO_UNIQUE_ADDRESS Source source_; UNIFEX_NO_UNIQUE_ADDRESS Func func_; UNIFEX_NO_UNIQUE_ADDRESS Policy policy_; }; struct _fn { template(typename Source, typename Func, typename FuncPolicy = decltype(get_execution_policy(UNIFEX_DECLVAL(Func&)))) (requires typed_bulk_sender) auto operator()(Source&& s, Func&& f) const noexcept(is_nothrow_callable_v<_fn, Source, Func, FuncPolicy>) -> callable_result_t<_fn, Source, Func, FuncPolicy> { return operator()((Source&&)s, (Func&&)f, get_execution_policy(f)); } template(typename Source, typename Func, typename FuncPolicy) (requires typed_bulk_sender AND tag_invocable<_fn, Source, Func, FuncPolicy>) auto operator()(Source&& s, Func&& f, FuncPolicy policy) const noexcept(is_nothrow_tag_invocable_v<_fn, Source, Func, FuncPolicy>) -> tag_invoke_result_t<_fn, Source, Func, FuncPolicy> { return tag_invoke(_fn{}, (Source&&)s, (Func&&)f, (FuncPolicy&&)policy); } template(typename Source, typename Func, typename FuncPolicy) (requires typed_bulk_sender AND (!tag_invocable<_fn, Source, Func, FuncPolicy>)) auto operator()(Source&& s, Func&& f, FuncPolicy policy) const noexcept( std::is_nothrow_constructible_v, Source> && std::is_nothrow_constructible_v, Func> && std::is_nothrow_move_constructible_v) -> tfx_sender, remove_cvref_t, FuncPolicy> { return tfx_sender, remove_cvref_t, FuncPolicy>{ (Source&&)s, (Func&&)f, std::move(policy) }; } template constexpr auto operator()(Func&& f) const noexcept(is_nothrow_callable_v< tag_t, _fn, Func>) -> bind_back_result_t<_fn, Func> { return bind_back(*this, (Func&&)f); } template constexpr auto operator()(Func&& f, FuncPolicy policy) const noexcept(is_nothrow_callable_v< tag_t, _fn, Func, FuncPolicy>) -> bind_back_result_t<_fn, Func, FuncPolicy> { return bind_back(*this, (Func&&)f, (FuncPolicy&&)policy); } }; } // namespace _bulk_transform inline constexpr _bulk_tfx::_fn bulk_transform{}; } // namespace unifex #include