#pragma once /* * Copyright 2018-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ #include #include #include namespace pushmi { template class any_flow_many_sender { union data { void* pobj_ = nullptr; char buffer_[sizeof(std::tuple)]; // can hold a V in-situ } data_{}; template static constexpr bool insitu() { return sizeof(Wrapped) <= sizeof(data::buffer_) && std::is_nothrow_move_constructible::value; } struct vtable { static void s_op(data&, data*) {} static any_executor s_executor(data&) { return {}; } static void s_submit(data&, any_flow_receiver) {} void (*op_)(data&, data*) = vtable::s_op; any_executor (*executor_)(data&) = vtable::s_executor; void (*submit_)(data&, any_flow_receiver) = vtable::s_submit; }; static constexpr vtable const noop_ {}; vtable const* vptr_ = &noop_; template any_flow_many_sender(Wrapped obj, std::false_type) : any_flow_many_sender() { struct s { static void op(data& src, data* dst) { if (dst) dst->pobj_ = std::exchange(src.pobj_, nullptr); delete static_cast(src.pobj_); } static any_executor executor(data& src) { return any_executor{::pushmi::executor(*static_cast(src.pobj_))}; } static void submit(data& src, any_flow_receiver out) { ::pushmi::submit(*static_cast(src.pobj_), std::move(out)); } }; static const vtable vtbl{s::op, s::executor, s::submit}; data_.pobj_ = new Wrapped(std::move(obj)); vptr_ = &vtbl; } template any_flow_many_sender(Wrapped obj, std::true_type) noexcept : any_flow_many_sender() { struct s { static void op(data& src, data* dst) { if (dst) new (dst->buffer_) Wrapped( std::move(*static_cast((void*)src.buffer_))); static_cast((void*)src.buffer_)->~Wrapped(); } static any_executor executor(data& src) { return any_executor{::pushmi::executor(*static_cast((void*)src.buffer_))}; } static void submit(data& src, any_flow_receiver out) { ::pushmi::submit( *static_cast((void*)src.buffer_), std::move(out)); } }; static const vtable vtbl{s::op, s::executor, s::submit}; new (data_.buffer_) Wrapped(std::move(obj)); vptr_ = &vtbl; } template > using wrapped_t = std::enable_if_t::value, U>; public: using properties = property_set, is_flow<>, is_many<>>; any_flow_many_sender() = default; any_flow_many_sender(any_flow_many_sender&& that) noexcept : any_flow_many_sender() { that.vptr_->op_(that.data_, &data_); std::swap(that.vptr_, vptr_); } PUSHMI_TEMPLATE (class Wrapped) (requires FlowSender, is_many<>>) explicit any_flow_many_sender(Wrapped obj) noexcept(insitu()) : any_flow_many_sender{std::move(obj), bool_()>{}} {} ~any_flow_many_sender() { vptr_->op_(data_, nullptr); } any_flow_many_sender& operator=(any_flow_many_sender&& that) noexcept { this->~any_flow_many_sender(); new ((void*)this) any_flow_many_sender(std::move(that)); return *this; } any_executor executor() { return vptr_->executor_(data_); } void submit(any_flow_receiver out) { vptr_->submit_(data_, std::move(out)); } }; // Class static definitions: template constexpr typename any_flow_many_sender::vtable const any_flow_many_sender::noop_; template class flow_many_sender { SF sf_; EXF exf_; public: using properties = property_set, is_flow<>, is_many<>>; constexpr flow_many_sender() = default; constexpr explicit flow_many_sender(SF sf) : sf_(std::move(sf)) {} constexpr flow_many_sender(SF sf, EXF exf) : sf_(std::move(sf)), exf_(std::move(exf)) {} auto executor() { return exf_(); } PUSHMI_TEMPLATE(class Out) (requires FlowReceiver && Invocable) void submit(Out out) { sf_(std::move(out)); } }; template , is_flow<>>) Data, class DSF, class DEXF> class flow_many_sender { Data data_; DSF sf_; DEXF exf_; public: using properties = property_set_insert_t, property_set, is_flow<>, is_many<>>>; constexpr flow_many_sender() = default; constexpr explicit flow_many_sender(Data data) : data_(std::move(data)) {} constexpr flow_many_sender(Data data, DSF sf) : data_(std::move(data)), sf_(std::move(sf)) {} constexpr flow_many_sender(Data data, DSF sf, DEXF exf) : data_(std::move(data)), sf_(std::move(sf)), exf_(std::move(exf)) {} auto executor() { return exf_(data_); } PUSHMI_TEMPLATE(class Out) (requires PUSHMI_EXP(lazy::FlowReceiver PUSHMI_AND lazy::Invocable)) void submit(Out out) { sf_(data_, std::move(out)); } }; template <> class flow_many_sender<> : public flow_many_sender { public: flow_many_sender() = default; }; //////////////////////////////////////////////////////////////////////////////// // make_flow_many_sender PUSHMI_INLINE_VAR constexpr struct make_flow_many_sender_fn { inline auto operator()() const { return flow_many_sender{}; } PUSHMI_TEMPLATE(class SF) (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) auto operator()(SF sf) const { return flow_many_sender{std::move(sf)}; } PUSHMI_TEMPLATE(class SF, class EXF) (requires True<> && Invocable PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) auto operator()(SF sf, EXF exf) const { return flow_many_sender{std::move(sf), std::move(exf)}; } PUSHMI_TEMPLATE(class Data) (requires True<> && Sender, is_flow<>>) auto operator()(Data d) const { return flow_many_sender{std::move(d)}; } PUSHMI_TEMPLATE(class Data, class DSF) (requires Sender, is_flow<>>) auto operator()(Data d, DSF sf) const { return flow_many_sender{std::move(d), std::move(sf)}; } PUSHMI_TEMPLATE(class Data, class DSF, class DEXF) (requires Sender, is_flow<>> && Invocable) auto operator()(Data d, DSF sf, DEXF exf) const { return flow_many_sender{std::move(d), std::move(sf), std::move(exf)}; } } const make_flow_many_sender {}; //////////////////////////////////////////////////////////////////////////////// // deduction guides #if __cpp_deduction_guides >= 201703 flow_many_sender() -> flow_many_sender; PUSHMI_TEMPLATE(class SF) (requires True<> PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) flow_many_sender(SF) -> flow_many_sender; PUSHMI_TEMPLATE(class SF, class EXF) (requires True<> && Invocable PUSHMI_BROKEN_SUBSUMPTION(&& not Sender)) flow_many_sender(SF, EXF) -> flow_many_sender; PUSHMI_TEMPLATE(class Data) (requires True<> && Sender, is_flow<>>) flow_many_sender(Data) -> flow_many_sender; PUSHMI_TEMPLATE(class Data, class DSF) (requires Sender, is_flow<>>) flow_many_sender(Data, DSF) -> flow_many_sender; PUSHMI_TEMPLATE(class Data, class DSF, class DEXF) (requires Sender, is_flow<>> && Invocable) flow_many_sender(Data, DSF, DEXF) -> flow_many_sender; #endif template<> struct construct_deduced : make_flow_many_sender_fn {}; // // TODO constrain me // template // auto erase_cast(Wrapped w) { // return flow_many_sender{std::move(w)}; // } } // namespace pushmi