#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 namespace pushmi { template class any_flow_receiver { bool done_ = false; bool started_ = false; union data { void* pobj_ = nullptr; char buffer_[sizeof(std::tuple)]; // can hold 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 void s_done(data&) {} static void s_error(data&, E) noexcept { std::terminate(); } static void s_value(data&, VN...) {} static void s_starting(data&, any_receiver) {} void (*op_)(data&, data*) = vtable::s_op; void (*done_)(data&) = vtable::s_done; void (*error_)(data&, E) noexcept = vtable::s_error; void (*value_)(data&, VN...) = vtable::s_value; void (*starting_)(data&, any_receiver) = vtable::s_starting; }; static constexpr vtable const noop_ {}; vtable const* vptr_ = &noop_; template any_flow_receiver(Wrapped obj, std::false_type) : any_flow_receiver() { struct s { static void op(data& src, data* dst) { if (dst) dst->pobj_ = std::exchange(src.pobj_, nullptr); delete static_cast(src.pobj_); } static void done(data& src) { ::pushmi::set_done(*static_cast(src.pobj_)); } static void error(data& src, E e) noexcept { ::pushmi::set_error(*static_cast(src.pobj_), std::move(e)); } static void value(data& src, VN... vn) { ::pushmi::set_value(*static_cast(src.pobj_), std::move(vn)...); } static void starting(data& src, any_receiver up) { ::pushmi::set_starting(*static_cast(src.pobj_), std::move(up)); } }; static const vtable vtbl{s::op, s::done, s::error, s::value, s::starting}; data_.pobj_ = new Wrapped(std::move(obj)); vptr_ = &vtbl; } template any_flow_receiver(Wrapped obj, std::true_type) noexcept : any_flow_receiver() { 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 void done(data& src) { ::pushmi::set_done(*static_cast((void*)src.buffer_)); } static void error(data& src, E e) noexcept {::pushmi::set_error( *static_cast((void*)src.buffer_), std::move(e)); } static void value(data& src, VN... vn) { ::pushmi::set_value( *static_cast((void*)src.buffer_), std::move(vn)...); } static void starting(data& src, any_receiver up) { ::pushmi::set_starting(*static_cast((void*)src.buffer_), std::move(up)); } }; static const vtable vtbl{s::op, s::done, s::error, s::value, s::starting}; 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<>>; any_flow_receiver() = default; any_flow_receiver(any_flow_receiver&& that) noexcept : any_flow_receiver() { that.vptr_->op_(that.data_, &data_); std::swap(that.vptr_, vptr_); } PUSHMI_TEMPLATE(class Wrapped) (requires FlowUpTo, any_receiver> && ReceiveValue, VN...> && ReceiveError, E>) explicit any_flow_receiver(Wrapped obj) noexcept(insitu()) : any_flow_receiver{std::move(obj), bool_()>{}} {} ~any_flow_receiver() { vptr_->op_(data_, nullptr); } any_flow_receiver& operator=(any_flow_receiver&& that) noexcept { this->~any_flow_receiver(); new ((void*)this) any_flow_receiver(std::move(that)); return *this; } void value(VN... vn) { if (!started_) {std::abort();} if (done_){ return; } vptr_->value_(data_, std::move(vn)...); } void error(E e) noexcept { if (!started_) {std::abort();} if (done_){ return; } done_ = true; vptr_->error_(data_, std::move(e)); } void done() { if (!started_) {std::abort();} if (done_){ return; } done_ = true; vptr_->done_(data_); } void starting(any_receiver up) { if (started_) {std::abort();} started_ = true; vptr_->starting_(data_, std::move(up)); } }; // Class static definitions: template constexpr typename any_flow_receiver::vtable const any_flow_receiver::noop_; template #if __cpp_concepts requires Invocable #endif class flow_receiver { bool done_ = false; bool started_ = false; VF nf_; EF ef_; DF df_; StrtF strtf_; public: using properties = property_set, is_flow<>>; static_assert( !detail::is_v, "the first parameter is the value implementation, but on_error{} was passed"); static_assert( !detail::is_v, "the second parameter is the error implementation, but on_value{} was passed"); flow_receiver() = default; constexpr explicit flow_receiver(VF nf) : flow_receiver(std::move(nf), EF{}, DF{}) {} constexpr explicit flow_receiver(EF ef) : flow_receiver(VF{}, std::move(ef), DF{}) {} constexpr explicit flow_receiver(DF df) : flow_receiver(VF{}, EF{}, std::move(df)) {} constexpr flow_receiver(EF ef, DF df) : nf_(), ef_(std::move(ef)), df_(std::move(df)) {} constexpr flow_receiver( VF nf, EF ef, DF df = DF{}, StrtF strtf = StrtF{}) : nf_(std::move(nf)), ef_(std::move(ef)), df_(std::move(df)), strtf_(std::move(strtf)) {} PUSHMI_TEMPLATE (class V) (requires Invocable) void value(V&& v) { if (!started_) {std::abort();} if (done_){ return; } nf_((V&&) v); } PUSHMI_TEMPLATE (class E) (requires Invocable) void error(E e) noexcept { static_assert(NothrowInvocable, "error function must be noexcept"); if (!started_) {std::abort();} if (done_){ return; } done_ = true; ef_(std::move(e)); } void done() { if (!started_) {std::abort();} if (done_){ return; } done_ = true; df_(); } PUSHMI_TEMPLATE(class Up) (requires Invocable) void starting(Up&& up) { if (started_) {std::abort();} started_ = true; strtf_( (Up &&) up); } }; template< PUSHMI_TYPE_CONSTRAINT(Receiver) Data, class DVF, class DEF, class DDF, class DStrtF> #if __cpp_concepts requires Invocable #endif class flow_receiver { bool done_ = false; bool started_ = false; Data data_; DVF nf_; DEF ef_; DDF df_; DStrtF strtf_; public: using properties = property_set_insert_t, property_set, is_flow<>>>; static_assert( !detail::is_v, "the first parameter is the value implementation, but on_error{} was passed"); static_assert( !detail::is_v, "the second parameter is the error implementation, but on_value{} was passed"); constexpr explicit flow_receiver(Data d) : flow_receiver(std::move(d), DVF{}, DEF{}, DDF{}) {} constexpr flow_receiver(Data d, DDF df) : data_(std::move(d)), nf_(), ef_(), df_(df) {} constexpr flow_receiver(Data d, DEF ef, DDF df = DDF{}) : data_(std::move(d)), nf_(), ef_(ef), df_(df) {} constexpr flow_receiver( Data d, DVF nf, DEF ef = DEF{}, DDF df = DDF{}, DStrtF strtf = DStrtF{}) : data_(std::move(d)), nf_(nf), ef_(ef), df_(df), strtf_(std::move(strtf)) {} Data& data() { return data_; } PUSHMI_TEMPLATE (class V) (requires Invocable) void value(V&& v) { if (!started_) {std::abort();} if (done_){ return; } nf_(data_, (V&&) v); } PUSHMI_TEMPLATE (class E) (requires Invocable) void error(E&& e) noexcept { static_assert( NothrowInvocable, "error function must be noexcept"); if (!started_) {std::abort();} if (done_){ return; } done_ = true; ef_(data_, (E&&) e); } void done() { if (!started_) {std::abort();} if (done_){ return; } done_ = true; df_(data_); } PUSHMI_TEMPLATE (class Up) (requires Invocable) void starting(Up&& up) { if (started_) {std::abort();} started_ = true; strtf_(data_, (Up &&) up); } }; template <> class flow_receiver<> : public flow_receiver { }; PUSHMI_CONCEPT_DEF( template (class T) concept FlowReceiverDataArg, Receiver> && not Invocable ); // TODO winnow down the number of make_flow_receiver overloads and deduction // guides here, as was done for make_many. //////////////////////////////////////////////////////////////////////////////// // make_flow_receiver PUSHMI_INLINE_VAR constexpr struct make_flow_receiver_fn { inline auto operator()() const { return flow_receiver<>{}; } PUSHMI_TEMPLATE (class VF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) auto operator()(VF nf) const { return flow_receiver{ std::move(nf)}; } template auto operator()(on_error_fn ef) const { return flow_receiver, ignoreDF, ignoreStrtF>{ std::move(ef)}; } template auto operator()(on_done_fn df) const { return flow_receiver, ignoreStrtF>{ std::move(df)}; } PUSHMI_TEMPLATE (class VF, class EF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg PUSHMI_AND not lazy::Invocable))) auto operator()(VF nf, EF ef) const { return flow_receiver{std::move(nf), std::move(ef)}; } PUSHMI_TEMPLATE(class EF, class DF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_AND lazy::Invocable PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) auto operator()(EF ef, DF df) const { return flow_receiver{std::move(ef), std::move(df)}; } PUSHMI_TEMPLATE (class VF, class EF, class DF) (requires PUSHMI_EXP( lazy::Invocable PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) auto operator()(VF nf, EF ef, DF df) const { return flow_receiver{std::move(nf), std::move(ef), std::move(df)}; } PUSHMI_TEMPLATE (class VF, class EF, class DF, class StrtF) (requires PUSHMI_EXP( lazy::Invocable PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) auto operator()(VF nf, EF ef, DF df, StrtF strtf) const { return flow_receiver{std::move(nf), std::move(ef), std::move(df), std::move(strtf)}; } PUSHMI_TEMPLATE(class Data) (requires PUSHMI_EXP( lazy::True<> PUSHMI_AND lazy::FlowReceiverDataArg)) auto operator()(Data d) const { return flow_receiver{ std::move(d)}; } PUSHMI_TEMPLATE(class Data, class DVF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_AND lazy::FlowReceiverDataArg)) auto operator()(Data d, DVF nf) const { return flow_receiver{ std::move(d), std::move(nf)}; } PUSHMI_TEMPLATE(class Data, class... DEFN) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg)) auto operator()(Data d, on_error_fn ef) const { return flow_receiver, passDDF, passDStrtF>{ std::move(d), std::move(ef)}; } PUSHMI_TEMPLATE(class Data, class... DDFN) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg)) auto operator()(Data d, on_done_fn df) const { return flow_receiver, passDStrtF>{ std::move(d), std::move(df)}; } PUSHMI_TEMPLATE(class Data, class DVF, class DEF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::Invocable))) auto operator()(Data d, DVF nf, DEF ef) const { return flow_receiver{std::move(d), std::move(nf), std::move(ef)}; } PUSHMI_TEMPLATE(class Data, class DEF, class DDF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_AND lazy::Invocable)) auto operator()(Data d, DEF ef, DDF df) const { return flow_receiver{ std::move(d), std::move(ef), std::move(df)}; } PUSHMI_TEMPLATE(class Data, class DVF, class DEF, class DDF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_AND lazy::Invocable)) auto operator()(Data d, DVF nf, DEF ef, DDF df) const { return flow_receiver{std::move(d), std::move(nf), std::move(ef), std::move(df)}; } PUSHMI_TEMPLATE(class Data, class DVF, class DEF, class DDF, class DStrtF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_AND lazy::Invocable)) auto operator()(Data d, DVF nf, DEF ef, DDF df, DStrtF strtf) const { return flow_receiver{std::move(d), std::move(nf), std::move(ef), std::move(df), std::move(strtf)}; } } const make_flow_receiver {}; //////////////////////////////////////////////////////////////////////////////// // deduction guides #if __cpp_deduction_guides >= 201703 flow_receiver() -> flow_receiver<>; PUSHMI_TEMPLATE(class VF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) flow_receiver(VF) -> flow_receiver; template flow_receiver(on_error_fn) -> flow_receiver, ignoreDF, ignoreStrtF>; template flow_receiver(on_done_fn) -> flow_receiver, ignoreStrtF>; PUSHMI_TEMPLATE(class VF, class EF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg PUSHMI_AND not lazy::Invocable))) flow_receiver(VF, EF) -> flow_receiver; PUSHMI_TEMPLATE(class EF, class DF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_AND lazy::Invocable PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) flow_receiver(EF, DF) -> flow_receiver; PUSHMI_TEMPLATE(class VF, class EF, class DF) (requires PUSHMI_EXP( lazy::Invocable PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) flow_receiver(VF, EF, DF) -> flow_receiver; PUSHMI_TEMPLATE(class VF, class EF, class DF, class StrtF) (requires PUSHMI_EXP( lazy::Invocable PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::FlowReceiverDataArg))) flow_receiver(VF, EF, DF, StrtF) -> flow_receiver; PUSHMI_TEMPLATE(class Data) (requires PUSHMI_EXP( lazy::True<> PUSHMI_AND lazy::FlowReceiverDataArg)) flow_receiver(Data d) -> flow_receiver; PUSHMI_TEMPLATE(class Data, class DVF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_AND lazy::FlowReceiverDataArg)) flow_receiver(Data d, DVF nf) -> flow_receiver; PUSHMI_TEMPLATE(class Data, class... DEFN) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg)) flow_receiver(Data d, on_error_fn) -> flow_receiver, passDDF, passDStrtF>; PUSHMI_TEMPLATE(class Data, class... DDFN) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg)) flow_receiver(Data d, on_done_fn) -> flow_receiver, passDStrtF>; PUSHMI_TEMPLATE(class Data, class DDF) (requires PUSHMI_EXP( lazy::True<> PUSHMI_AND lazy::FlowReceiverDataArg PUSHMI_AND lazy::Invocable)) flow_receiver(Data d, DDF) -> flow_receiver; PUSHMI_TEMPLATE(class Data, class DVF, class DEF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_BROKEN_SUBSUMPTION(PUSHMI_AND not lazy::Invocable))) flow_receiver(Data d, DVF nf, DEF ef) -> flow_receiver; PUSHMI_TEMPLATE(class Data, class DEF, class DDF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_AND lazy::Invocable)) flow_receiver(Data d, DEF, DDF) -> flow_receiver; PUSHMI_TEMPLATE(class Data, class DVF, class DEF, class DDF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_AND lazy::Invocable)) flow_receiver(Data d, DVF nf, DEF ef, DDF df) -> flow_receiver; PUSHMI_TEMPLATE(class Data, class DVF, class DEF, class DDF, class DStrtF) (requires PUSHMI_EXP( lazy::FlowReceiverDataArg PUSHMI_AND lazy::Invocable )) flow_receiver(Data d, DVF nf, DEF ef, DDF df, DStrtF strtf) -> flow_receiver; #endif template<> struct construct_deduced : make_flow_receiver_fn {}; } // namespace pushmi