Commit 38a4477a authored by Eric Niebler's avatar Eric Niebler Committed by Facebook Github Bot

more cleanup and concept-ification

fbshipit-source-id: 07e042879b008fbd34f583ae4351459bd5ae84a9
parent f1bd6185
...@@ -25,74 +25,27 @@ struct construct { ...@@ -25,74 +25,27 @@ struct construct {
}; };
template<template <class...> class T> template<template <class...> class T>
struct construct_deduced { struct construct_deduced;
template<>
struct construct_deduced<none> {
template<class... AN> template<class... AN>
requires requires (AN&&... an) { T{(AN&&) an...}; } auto operator()(AN&&... an) const -> decltype(none{(AN&&) an...}) {
auto operator()(AN&&... an) const { return none{(AN&&) an...};
return T{std::forward<AN>(an)...}; }
};
template<>
struct construct_deduced<single> {
template<class... AN>
auto operator()(AN&&... an) const -> decltype(single{(AN&&) an...}) {
return single{(AN&&) an...};
} }
}; };
template <template <class...> class T, class... AN> template <template <class...> class T, class... AN>
using deduced_type_t = std::invoke_result_t<construct_deduced<T>, AN...>; using deduced_type_t = std::invoke_result_t<construct_deduced<T>, AN...>;
// template <class Fn>
// struct apply {
// template <class Tup>
// requires requires (Tup&& tup) { std::apply(Fn{}, (Tup&&) tup); }
// decltype(auto) operator()(Tup&& tup) const {
// return std::apply(Fn{}, (Tup&&) tup);
// }
// };
//
// template <class Fn, class Gn>
// struct compose {
// template <class... AN>
// requires Invocable<Gn, AN...> &&
// Invocable<Fn, std::invoke_result_t<Gn, AN...>>
// decltype(auto) operator()(AN&&... an) const {
// return std::invoke(Fn{}, std::invoke(Gn{}, (AN&&) an...));
// }
// };
//
// template <class T>
// inline constexpr apply<construct<T>> from_tuple {};
//
// template <template <class...> class T>
// inline constexpr apply<construct_deduced<T>> from_tuple_deduced {};
template <class T, class... AN>
auto from_tuple(std::tuple<AN...>&& t) {
return std::apply(construct<T>{}, std::move(t));
}
template <template<class...> class T, class... AN>
auto from_tuple(std::tuple<AN...>&& t) {
using Deduced = decltype(T{std::declval<AN&&>()...});
return std::apply(construct<Deduced>{}, std::move(t));
}
template <class T>
void sfinae_from_tuple(...);
template <template<class... TN> class T>
void sfinae_from_tuple(...);
template <class T, class... AN,
class Constructor = construct<T>>
auto sfinae_from_tuple(std::tuple<AN...>&& t) ->
decltype(std::apply(Constructor{}, std::move(t))) {
return std::apply(Constructor{}, std::move(t));
}
template <template<class...> class T, class... AN,
class Deduced = decltype(T{std::declval<AN>()...}),
class Constructor = construct<Deduced>>
auto sfinae_from_tuple(std::tuple<AN...>&& t) ->
decltype(std::apply(Constructor{}, std::move(t))) {
return std::apply(Constructor{}, std::move(t));
}
struct ignoreVF { struct ignoreVF {
template <class V> template <class V>
void operator()(V&&) {} void operator()(V&&) {}
......
...@@ -16,20 +16,6 @@ struct none_tag : silent_tag {}; ...@@ -16,20 +16,6 @@ struct none_tag : silent_tag {};
struct single_tag : none_tag {}; struct single_tag : none_tag {};
struct flow_tag : single_tag {}; struct flow_tag : single_tag {};
template <class Tag>
concept bool Silent = Derived<Tag, silent_tag>;
template <class Tag>
concept bool None = Silent<Tag> && Derived<Tag, none_tag>;
template <class Tag>
concept bool Single = None<Tag> && Derived<Tag, single_tag>;
template <class Tag>
concept bool Flow = Single<Tag> && Derived<Tag, flow_tag>;
template <class T> template <class T>
using __sender_category_t = typename T::sender_category; using __sender_category_t = typename T::sender_category;
...@@ -69,8 +55,8 @@ using receiver_category_t = __receiver_category_t<receiver_traits<T>>; ...@@ -69,8 +55,8 @@ using receiver_category_t = __receiver_category_t<receiver_traits<T>>;
////////////////////////////////////////////////////////////////////////////////
// Receiver concepts
template <class S, class Tag = silent_tag> template <class S, class Tag = silent_tag>
concept bool Receiver = Valid<receiver_traits<S>, __receiver_category_t> && concept bool Receiver = Valid<receiver_traits<S>, __receiver_category_t> &&
Derived<receiver_category_t<S>, Tag> && Derived<receiver_category_t<S>, Tag> &&
...@@ -93,11 +79,8 @@ concept bool SingleReceiver = NoneReceiver<S, E> && ...@@ -93,11 +79,8 @@ concept bool SingleReceiver = NoneReceiver<S, E> &&
}; };
////////////////////////////////////////////////////////////////////////////////
// Sender concepts
template <class D, class Tag = silent_tag> template <class D, class Tag = silent_tag>
concept bool Sender = Valid<sender_traits<D>, __sender_category_t> && concept bool Sender = Valid<sender_traits<D>, __sender_category_t> &&
Derived<sender_category_t<D>, Tag> && SemiMovable<D>; Derived<sender_category_t<D>, Tag> && SemiMovable<D>;
...@@ -110,24 +93,22 @@ concept bool SenderTo = Sender<D, Tag> && ...@@ -110,24 +93,22 @@ concept bool SenderTo = Sender<D, Tag> &&
}; };
////////////////////////////////////////////////////////////////////////////////
// TimeSender concepts
template <class D, class Tag = silent_tag> template <class D, class Tag = silent_tag>
concept bool TimeSender = Sender<D, Tag> && requires(D& d) { concept bool TimeSender = Sender<D, Tag> && requires(D& d) {
{ ::pushmi::now(d) } -> Regular { ::pushmi::now(d) } -> Regular
}; };
template <TimeSender D>
using time_point_t = decltype(::pushmi::now(std::declval<D&>()));
template <class D, class S, class Tag = silent_tag> template <class D, class S, class Tag = silent_tag>
concept bool TimeSenderTo = Receiver<S, Tag> && TimeSender<D, Tag> && concept bool TimeSenderTo = Receiver<S, Tag> && TimeSender<D, Tag> &&
requires(D& d, S&& s) { requires(D& d, S&& s, time_point_t<D> tp) {
::pushmi::submit(d, ::pushmi::now(d), (S &&) s); ::pushmi::submit(d, tp, (S &&) s);
}; };
template <TimeSender D>
using time_point_t = decltype(::pushmi::now(std::declval<D&>()));
// // this is a more general form where C (Constraint) could be time or priority // // this is a more general form where C (Constraint) could be time or priority
// // enum or any other ordering constraint value-type. // // enum or any other ordering constraint value-type.
......
...@@ -73,7 +73,7 @@ public: ...@@ -73,7 +73,7 @@ public:
// ask whether T'& is convertible to T. That brings us right back to this // ask whether T'& is convertible to T. That brings us right back to this
// constructor. Constraint recursion! // constructor. Constraint recursion!
static_assert(TimeSenderTo<W, single<Other, E>>); static_assert(TimeSenderTo<W, single<Other, E>>);
if constexpr ((bool)TimeSenderTo<W, single<Other, E>>) { if constexpr((bool)TimeSenderTo<W, single<Other, E>>) {
pobj_ = std::addressof(w); pobj_ = std::addressof(w);
vptr_ = detail::any_time_executor_ref_vtable_v<E, TP, Other, Wrapped>(); vptr_ = detail::any_time_executor_ref_vtable_v<E, TP, Other, Wrapped>();
} }
......
...@@ -279,9 +279,7 @@ class flow_single<> ...@@ -279,9 +279,7 @@ class flow_single<>
: public flow_single<ignoreVF, abortEF, ignoreDF, ignoreStpF, ignoreStrtF> { : public flow_single<ignoreVF, abortEF, ignoreDF, ignoreStpF, ignoreStrtF> {
}; };
using archetype_flow_single = flow_single<>; flow_single()->flow_single<>;
flow_single()->archetype_flow_single;
template <class VF> template <class VF>
requires !Receiver<VF> && !detail::is_v<VF, on_error> && requires !Receiver<VF> && !detail::is_v<VF, on_error> &&
......
...@@ -20,9 +20,8 @@ class none<E> { ...@@ -20,9 +20,8 @@ class none<E> {
return sizeof(Wrapped) <= sizeof(data::buffer_) && return sizeof(Wrapped) <= sizeof(data::buffer_) &&
std::is_nothrow_move_constructible_v<Wrapped>; std::is_nothrow_move_constructible_v<Wrapped>;
} }
enum struct op { destroy, move };
struct vtable { struct vtable {
void (*op_)(op, data&, data*) = +[](op, data&, data*) {}; void (*op_)(data&, data*) = +[](data&, data*) {};
void (*done_)(data&) = +[](data&) {}; void (*done_)(data&) = +[](data&) {};
void (*error_)(data&, E) noexcept = +[](data&, E) noexcept { void (*error_)(data&, E) noexcept = +[](data&, E) noexcept {
std::terminate(); std::terminate();
...@@ -31,13 +30,10 @@ class none<E> { ...@@ -31,13 +30,10 @@ class none<E> {
} const* vptr_ = &vtable::noop_; } const* vptr_ = &vtable::noop_;
template <class Wrapped, bool = insitu<Wrapped>()> template <class Wrapped, bool = insitu<Wrapped>()>
static constexpr vtable const vtable_v = { static constexpr vtable const vtable_v = {
+[](op o, data& src, data* dst) { +[](data& src, data* dst) {
switch (o) { if (dst)
case op::move:
dst->pobj_ = std::exchange(src.pobj_, nullptr); dst->pobj_ = std::exchange(src.pobj_, nullptr);
case op::destroy:
delete static_cast<Wrapped const*>(src.pobj_); delete static_cast<Wrapped const*>(src.pobj_);
}
}, },
+[](data& src) { ::pushmi::set_done(*static_cast<Wrapped*>(src.pobj_)); }, +[](data& src) { ::pushmi::set_done(*static_cast<Wrapped*>(src.pobj_)); },
+[](data& src, E e) noexcept { +[](data& src, E e) noexcept {
...@@ -52,7 +48,7 @@ public: ...@@ -52,7 +48,7 @@ public:
none() = default; none() = default;
none(none&& that) noexcept : none() { none(none&& that) noexcept : none() {
that.vptr_->op_(op::move, that.data_, &data_); that.vptr_->op_(that.data_, &data_);
std::swap(that.vptr_, vptr_); std::swap(that.vptr_, vptr_);
} }
template <class Wrapped> template <class Wrapped>
...@@ -65,7 +61,7 @@ public: ...@@ -65,7 +61,7 @@ public:
vptr_ = &vtable_v<Wrapped>; vptr_ = &vtable_v<Wrapped>;
} }
~none() { ~none() {
vptr_->op_(op::destroy, data_, nullptr); vptr_->op_(data_, nullptr);
} }
none& operator=(none&& that) noexcept { none& operator=(none&& that) noexcept {
this->~none(); this->~none();
...@@ -93,14 +89,11 @@ constexpr typename none<E>::vtable const none<E>::vtable_v; ...@@ -93,14 +89,11 @@ constexpr typename none<E>::vtable const none<E>::vtable_v;
template <class E> template <class E>
template <class Wrapped> template <class Wrapped>
constexpr typename none<E>::vtable const none<E>::vtable_v<Wrapped, true> = { constexpr typename none<E>::vtable const none<E>::vtable_v<Wrapped, true> = {
+[](op o, data& src, data* dst) { +[](data& src, data* dst) {
switch (o) { if (dst)
case op::move:
new (dst->buffer_) new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_))); Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
case op::destroy:
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped(); static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
}, },
+[](data& src) { +[](data& src) {
::pushmi::set_done(*static_cast<Wrapped*>((void*)src.buffer_)); ::pushmi::set_done(*static_cast<Wrapped*>((void*)src.buffer_));
...@@ -108,18 +101,18 @@ constexpr typename none<E>::vtable const none<E>::vtable_v<Wrapped, true> = { ...@@ -108,18 +101,18 @@ constexpr typename none<E>::vtable const none<E>::vtable_v<Wrapped, true> = {
+[](data& src, E e) noexcept {::pushmi::set_error( +[](data& src, E e) noexcept {::pushmi::set_error(
*static_cast<Wrapped*>((void*)src.buffer_), *static_cast<Wrapped*>((void*)src.buffer_),
std::move(e)); std::move(e));
} }
} };
;
template <class EF, class DF> template <class EF, class DF>
requires Invocable<DF&> && !detail::is_v<EF, on_value> && !detail::is_v<EF, single> requires Invocable<DF&>
class none<EF, DF> { class none<EF, DF> {
static_assert(!detail::is_v<EF, on_value> && !detail::is_v<EF, single>);
bool done_ = false; bool done_ = false;
EF ef_; EF ef_{};
DF df_; DF df_{};
public: public:
using receiver_category = none_tag; using receiver_category = none_tag;
// static_assert( // static_assert(
...@@ -137,27 +130,32 @@ class none<EF, DF> { ...@@ -137,27 +130,32 @@ class none<EF, DF> {
template <class E> template <class E>
requires Invocable<EF&, E> requires Invocable<EF&, E>
void error(E e) noexcept { void error(E e) noexcept {
static_assert(NothrowInvocable<EF&, E>, "error function must be noexcept"); static_assert(
if (done_) {return;} noexcept(ef_(std::move(e))),
"error function must be noexcept");
if (!done_) {
done_ = true; done_ = true;
ef_(e); ef_(std::move(e));
}
} }
void done() { void done() {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
df_(); df_();
} }
}
}; };
template <Receiver Data, class DEF, class DDF> template <Receiver<none_tag> Data, class DEF, class DDF>
requires Invocable<DDF&, Data&> && !detail::is_v<DEF, on_value> && !detail::is_v<Data, single> requires Invocable<DDF&, Data&>
class none<Data, DEF, DDF> { class none<Data, DEF, DDF> {
bool done_ = false; bool done_ = false;
Data data_; Data data_{};
DEF ef_; DEF ef_{};
DDF df_; DDF df_{};
static_assert(!detail::is_v<DEF, on_value>);
public: static_assert(!detail::is_v<Data, single>);
public:
using receiver_category = none_tag; using receiver_category = none_tag;
// static_assert( // static_assert(
...@@ -168,71 +166,59 @@ class none<Data, DEF, DDF> { ...@@ -168,71 +166,59 @@ class none<Data, DEF, DDF> {
constexpr none(Data d, DDF df) constexpr none(Data d, DDF df)
: done_(false), data_(std::move(d)), ef_(), df_(std::move(df)) {} : done_(false), data_(std::move(d)), ef_(), df_(std::move(df)) {}
constexpr none(Data d, DEF ef, DDF df = DDF{}) constexpr none(Data d, DEF ef, DDF df = DDF{})
: done_(false), data_(std::move(d)), ef_(std::move(ef)), df_(std::move(df)) {} : done_(false), data_(std::move(d)), ef_(std::move(ef)),
df_(std::move(df)) {}
template <class E> template <class E>
requires Invocable<DEF&, Data&, E> void error(E e) noexcept { requires Invocable<DEF&, Data&, E>
void error(E e) noexcept {
static_assert( static_assert(
NothrowInvocable<DEF&, Data&, E>, "error function must be noexcept"); noexcept(ef_(data_, std::move(e))), "error function must be noexcept");
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
ef_(data_, e); ef_(data_, std::move(e));
}
} }
void done() { void done() {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
df_(data_); df_(data_);
} }
}
}; };
template <> class none<> : public none<abortEF, ignoreDF> {}; template <>
class none<>
using archetype_none = none<>; : public none<abortEF, ignoreDF> {
};
none()->archetype_none; none() -> none<>;
template <class EF> template <class EF>
requires !Receiver<EF> && none(EF) -> none<EF, ignoreDF>;
!detail::is_v<EF, single> &&
!detail::is_v<EF, on_value> &&
!detail::is_v<EF, on_done>
none(EF)->none<EF, ignoreDF>;
template <class DF> template <class DF>
requires !Receiver<DF> && requires Invocable<DF&>
!detail::is_v<DF, on_value> && none(DF)->none<abortEF, DF>;
!detail::is_v<DF, single>
none(on_done<DF>)->none<abortEF, on_done<DF>>;
template <class E, class Wrapped>
requires NoneReceiver<Wrapped, E> &&
!detail::is_v<E, on_value> &&
!detail::is_v<Wrapped, single>
none(Wrapped)->none<E>;
template <class EF, class DF> template <class EF, class DF>
requires Invocable<DF&> none(EF, DF)->none<EF, DF>; requires Invocable<DF&>
none(EF, DF)->none<EF, DF>;
template <Receiver Data> template <Receiver<none_tag> Data>
requires !detail::is_v<Data, on_value> && requires !Receiver<Data, single_tag>
!detail::is_v<Data, single> none(Data) -> none<Data, passDEF, passDDF>;
none(Data)->none<Data, passDEF, passDDF>;
template <Receiver Data, class DEF> template <Receiver<none_tag> Data, class DEF>
requires !detail::is_v<DEF, on_done> && requires !Receiver<Data, single_tag>
!detail::is_v<Data, on_value> && none(Data, DEF) -> none<Data, DEF, passDDF>;
!detail::is_v<Data, single>
none(Data, DEF)->none<Data, DEF, passDDF>;
template <Receiver Data, class DDF> template <Receiver<none_tag> Data, class DDF>
requires !detail::is_v<Data, on_value> && requires Invocable<DDF&, Data&> && !Receiver<Data, single_tag>
!detail::is_v<Data, single> none(Data, DDF) -> none<Data, passDEF, DDF>;
none(Data, on_done<DDF>)->none<Data, passDEF, on_done<DDF>>;
template <Receiver Data, class DEF, class DDF> template <Receiver<none_tag> Data, class DEF, class DDF>
requires Invocable<DDF&, Data&> && requires !Receiver<Data, single_tag>
!detail::is_v<Data, on_value> && none(Data, DEF, DDF) -> none<Data, DEF, DDF>;
!detail::is_v<Data, single>
none(Data, DEF, DDF)->none<Data, DEF, DDF>;
template <class E = std::exception_ptr> template <class E = std::exception_ptr>
using any_none = none<E>; using any_none = none<E>;
...@@ -251,12 +237,12 @@ using any_none = none<E>; ...@@ -251,12 +237,12 @@ using any_none = none<E>;
// return none<E>{std::move(w)}; // return none<E>{std::move(w)};
// } // }
template <SenderTo<std::promise<void>, none_tag> S> template <SenderTo<std::promise<void>, none_tag> Out>
std::future<void> future_from(S sender) { std::future<void> future_from(Out out) {
std::promise<void> p; std::promise<void> p;
auto result = p.get_future(); auto result = p.get_future();
submit(sender, std::move(p)); submit(out, std::move(p));
return result; return result;
} }
} // namespace values } // namespace pushmi
...@@ -14,108 +14,35 @@ namespace pushmi { ...@@ -14,108 +14,35 @@ namespace pushmi {
namespace detail{ namespace detail{
// template <Sender In> template <class Tag>
// struct out_from_fn { struct make_receiver;
// template <>
// }; struct make_receiver<none_tag> : construct_deduced<none> {};
template <>
struct make_receiver<single_tag> : construct_deduced<single> {};
template <Sender In>
struct out_from_fn {
template<Sender In, class... AN> using Make = make_receiver<sender_category_t<In>>;
auto out_from(std::tuple<AN...>&& args) { template <class... Ts>
using SingleReceiver = requires Invocable<Make, Ts...>
decltype(pushmi::sfinae_from_tuple<single>(std::move(args))); auto operator()(std::tuple<Ts...> args) const {
using NoneReceiver = return std::apply(Make(), std::move(args));
decltype(pushmi::sfinae_from_tuple<none>(std::move(args))); }
if constexpr ((bool)TimeSenderTo<In, SingleReceiver, single_tag> || template <class... Ts, class... Fns,
SenderTo<In, SingleReceiver, single_tag>) { class This = std::enable_if_t<sizeof...(Fns) != 0, out_from_fn>>
return pushmi::from_tuple<single>(std::move(args)); requires (SemiMovable<Fns> &&...) &&
} else if constexpr ((bool)TimeSenderTo<In, NoneReceiver, none_tag> || Invocable<Make, std::tuple<Ts...>> &&
SenderTo<In, NoneReceiver, none_tag>) { Invocable<This, std::invoke_result_t<Make, std::tuple<Ts...>>, Fns...>
return pushmi::from_tuple<none>(std::move(args)); auto operator()(std::tuple<Ts...> args, Fns...fns) const {
} return This()(This()(std::move(args)), std::move(fns)...);
} }
template <Receiver<sender_category_t<In>> Out, class...Fns>
template<Sender In, class... AN, class... DVFN, class... DEFN, class... DDFN> requires (SemiMovable<Fns> &&...)
auto out_from( auto operator()(Out out, Fns... fns) const {
std::tuple<AN...>&& args, return Make()(std::move(out), std::move(fns)...);
on_value<DVFN...> vf,
on_error<DEFN...> ef,
on_done<DDFN...> df) {
auto out = out_from<In>(std::move(args));
if constexpr (::pushmi::detail::is_v<decltype(out), single>) {
return single{std::move(out), std::move(vf), std::move(ef), std::move(df)};
} else if constexpr (::pushmi::detail::is_v<decltype(out), none>) {
return none{std::move(out), std::move(ef), std::move(df)};
}
}
template<Sender In, Receiver Out, class... DVFN, class... DEFN, class... DDFN>
auto out_from(
Out&& out,
on_value<DVFN...> vf,
on_error<DEFN...> ef,
on_done<DDFN...> df) {
using SingleReceiver =
decltype(pushmi::sfinae_from_tuple<single>(std::tuple{std::move(out)}));
using NoneReceiver =
decltype(pushmi::sfinae_from_tuple<none>(std::tuple{std::move(out)}));
if constexpr ((bool)TimeSenderTo<In, SingleReceiver, single_tag> ||
SenderTo<In, SingleReceiver, single_tag>) {
return single{std::move(out), std::move(vf), std::move(ef), std::move(df)};
} else if constexpr ((bool)TimeSenderTo<In, NoneReceiver, none_tag> ||
SenderTo<In, NoneReceiver, none_tag>) {
return none{std::move(out), std::move(ef), std::move(df)};
}
}
template<Sender In, Receiver Out, class... DEFN, class... DDFN>
auto out_from(Out&& out, on_error<DEFN...> ef, on_done<DDFN...> df) {
using SingleReceiver =
decltype(pushmi::sfinae_from_tuple<single>(std::tuple{std::move(out)}));
using NoneReceiver =
decltype(pushmi::sfinae_from_tuple<none>(std::tuple{std::move(out)}));
if constexpr ((bool)TimeSenderTo<In, SingleReceiver, single_tag> ||
SenderTo<In, SingleReceiver, single_tag>) {
return single{std::move(out), std::move(ef), std::move(df)};
} else if constexpr ((bool)TimeSenderTo<In, NoneReceiver, none_tag> ||
SenderTo<In, NoneReceiver, none_tag>) {
return none{std::move(out), std::move(ef), std::move(df)};
} }
} };
template<Sender In, Receiver Out, class... DVFN>
auto out_from(Out&& out, on_value<DVFN...> vf) {
using SingleReceiver =
decltype(pushmi::sfinae_from_tuple<single>(std::tuple{std::move(out)}));
using NoneReceiver =
decltype(pushmi::sfinae_from_tuple<none>(std::tuple{std::move(out)}));
if constexpr ((bool)TimeSenderTo<In, SingleReceiver, single_tag> ||
SenderTo<In, SingleReceiver, single_tag>) {
return single{std::move(out), std::move(vf)};
} else if constexpr ((bool)TimeSenderTo<In, NoneReceiver, none_tag> ||
SenderTo<In, NoneReceiver, none_tag>) {
return none{std::move(out)};
}
}
template<Sender In, Receiver Out>
auto out_from(Out&& out) {
using SingleReceiver =
decltype(pushmi::sfinae_from_tuple<single>(std::tuple{std::move(out)}));
using NoneReceiver =
decltype(pushmi::sfinae_from_tuple<none>(std::tuple{std::move(out)}));
if constexpr ((bool)TimeSenderTo<In, SingleReceiver, single_tag> ||
SenderTo<In, SingleReceiver, single_tag>) {
return single{std::move(out)};
} else if constexpr ((bool)TimeSenderTo<In, NoneReceiver, none_tag> ||
SenderTo<In, NoneReceiver, none_tag>) {
return none{std::move(out)};
}
}
template<Sender In, class FN> template<Sender In, class FN>
auto submit_transform_out(FN fn){ auto submit_transform_out(FN fn){
...@@ -151,7 +78,7 @@ auto submit_transform_out(SDSF sdsf, TSDSF tsdsf){ ...@@ -151,7 +78,7 @@ auto submit_transform_out(SDSF sdsf, TSDSF tsdsf){
} }
} }
template<Sender In, class Out, class... FN> template<Sender In, Receiver Out, class... FN>
auto deferred_from(FN&&... fn) { auto deferred_from(FN&&... fn) {
if constexpr ((bool)TimeSenderTo<In, Out, single_tag>) { if constexpr ((bool)TimeSenderTo<In, Out, single_tag>) {
return time_single_deferred{(FN&&) fn...}; return time_single_deferred{(FN&&) fn...};
...@@ -162,7 +89,7 @@ auto deferred_from(FN&&... fn) { ...@@ -162,7 +89,7 @@ auto deferred_from(FN&&... fn) {
} }
} }
template<Sender In, class Out, class... FN> template<Sender In, Receiver Out, class... FN>
auto deferred_from(In in, FN&&... fn) { auto deferred_from(In in, FN&&... fn) {
if constexpr ((bool)TimeSenderTo<In, Out, single_tag>) { if constexpr ((bool)TimeSenderTo<In, Out, single_tag>) {
return time_single_deferred{std::move(in), (FN&&) fn...}; return time_single_deferred{std::move(in), (FN&&) fn...};
...@@ -175,7 +102,7 @@ auto deferred_from(In in, FN&&... fn) { ...@@ -175,7 +102,7 @@ auto deferred_from(In in, FN&&... fn) {
template< template<
Sender In, Sender In,
class Out, Receiver Out,
bool SenderRequires, bool SenderRequires,
bool SingleSenderRequires, bool SingleSenderRequires,
bool TimeSingleSenderRequires> bool TimeSingleSenderRequires>
...@@ -201,7 +128,7 @@ struct set_value_fn { ...@@ -201,7 +128,7 @@ struct set_value_fn {
return [v = (V&&) v]<class Out>(Out out) mutable PUSHMI_VOID_LAMBDA_REQUIRES(Receiver<Out>) { return [v = (V&&) v]<class Out>(Out out) mutable PUSHMI_VOID_LAMBDA_REQUIRES(Receiver<Out>) {
::pushmi::set_value(out, (V&&) v); ::pushmi::set_value(out, (V&&) v);
}; };
} }
}; };
struct set_error_fn { struct set_error_fn {
...@@ -238,7 +165,7 @@ struct set_starting_fn { ...@@ -238,7 +165,7 @@ struct set_starting_fn {
} }
}; };
struct do_submit_fn { struct submit_fn {
template <class Out> template <class Out>
auto operator()(Out out) const { auto operator()(Out out) const {
static_assert(Receiver<Out>, "'Out' must be a model of Receiver"); static_assert(Receiver<Out>, "'Out' must be a model of Receiver");
...@@ -255,7 +182,7 @@ struct do_submit_fn { ...@@ -255,7 +182,7 @@ struct do_submit_fn {
} }
}; };
struct get_now_fn { struct now_fn {
auto operator()() const { auto operator()() const {
return []<class In>(In in) PUSHMI_T_LAMBDA_REQUIRES(decltype(::pushmi::now(in)), TimeSender<In>) { return []<class In>(In in) PUSHMI_T_LAMBDA_REQUIRES(decltype(::pushmi::now(in)), TimeSender<In>) {
return ::pushmi::now(in); return ::pushmi::now(in);
...@@ -270,9 +197,9 @@ inline constexpr detail::set_error_fn set_error{}; ...@@ -270,9 +197,9 @@ inline constexpr detail::set_error_fn set_error{};
inline constexpr detail::set_value_fn set_value{}; inline constexpr detail::set_value_fn set_value{};
inline constexpr detail::set_stopping_fn set_stopping{}; inline constexpr detail::set_stopping_fn set_stopping{};
inline constexpr detail::set_starting_fn set_starting{}; inline constexpr detail::set_starting_fn set_starting{};
inline constexpr detail::do_submit_fn submit{}; inline constexpr detail::submit_fn submit{};
inline constexpr detail::get_now_fn now{}; inline constexpr detail::now_fn now{};
inline constexpr detail::get_now_fn top{}; inline constexpr detail::now_fn top{};
} // namespace extension_operators } // namespace extension_operators
......
...@@ -22,7 +22,7 @@ struct on_fn { ...@@ -22,7 +22,7 @@ struct on_fn {
template <class ExecutorFactory> template <class ExecutorFactory>
auto on_fn::operator()(ExecutorFactory ef) const { auto on_fn::operator()(ExecutorFactory ef) const {
return [ef = std::move(ef)]<class In>(In in) { return [ef = std::move(ef)]<class In>(In in) {
return ::pushmi::detail::deferred_from<In, archetype_single>( return ::pushmi::detail::deferred_from<In, single<>>(
std::move(in), std::move(in),
::pushmi::detail::submit_transform_out<In>( ::pushmi::detail::submit_transform_out<In>(
[ef]<class Out>(In& in, Out out) { [ef]<class Out>(In& in, Out out) {
......
...@@ -18,20 +18,11 @@ namespace operators { ...@@ -18,20 +18,11 @@ namespace operators {
namespace detail { namespace detail {
inline constexpr struct make_receiver_fn {
template <class... AN>
deduced_type_t<none, AN...> operator()(none_tag, AN&& ...an) const {
return none{(AN&&) an...};
}
template <class... AN>
deduced_type_t<single, AN...> operator()(single_tag, AN&& ...an) const {
return single{(AN&&) an...};
}
} make_receiver {};
template <Sender In, class ...AN> template <Sender In, class ...AN>
using receiver_type_t = using receiver_type_t =
std::invoke_result_t<make_receiver_fn, sender_category_t<In>, AN...>; std::invoke_result_t<
pushmi::detail::make_receiver<sender_category_t<In>>,
AN...>;
template <class In, class ... AN> template <class In, class ... AN>
concept bool AutoSenderTo = SenderTo<In, receiver_type_t<In, AN...>>; concept bool AutoSenderTo = SenderTo<In, receiver_type_t<In, AN...>>;
...@@ -49,13 +40,13 @@ private: ...@@ -49,13 +40,13 @@ private:
std::tuple<AN...> args_; std::tuple<AN...> args_;
template <AutoSenderTo<AN...> In> template <AutoSenderTo<AN...> In>
In operator()(In in) { In operator()(In in) {
auto out{::pushmi::detail::out_from<In>(std::move(args_))}; auto out{::pushmi::detail::out_from_fn<In>()(std::move(args_))};
::pushmi::submit(in, std::move(out)); ::pushmi::submit(in, std::move(out));
return in; return in;
} }
template <AutoTimeSenderTo<AN...> In> template <AutoTimeSenderTo<AN...> In>
In operator()(In in) { In operator()(In in) {
auto out{::pushmi::detail::out_from<In>(std::move(args_))}; auto out{::pushmi::detail::out_from_fn<In>()(std::move(args_))};
::pushmi::submit(in, ::pushmi::now(in), std::move(out)); ::pushmi::submit(in, ::pushmi::now(in), std::move(out));
return in; return in;
} }
...@@ -75,7 +66,7 @@ private: ...@@ -75,7 +66,7 @@ private:
std::tuple<AN...> args_; std::tuple<AN...> args_;
template <AutoTimeSenderTo<AN...> In> template <AutoTimeSenderTo<AN...> In>
In operator()(In in) { In operator()(In in) {
auto out{::pushmi::detail::out_from<In>(std::move(args_))}; auto out{::pushmi::detail::out_from_fn<In>()(std::move(args_))};
::pushmi::submit(in, std::move(at_), std::move(out)); ::pushmi::submit(in, std::move(at_), std::move(out));
return in; return in;
} }
...@@ -98,7 +89,7 @@ private: ...@@ -98,7 +89,7 @@ private:
// TODO - only move, move-only types.. // TODO - only move, move-only types..
// if out can be copied, then submit can be called multiple // if out can be copied, then submit can be called multiple
// times.. // times..
auto out{::pushmi::detail::out_from<In>(std::move(args_))}; auto out{::pushmi::detail::out_from_fn<In>()(std::move(args_))};
auto at = ::pushmi::now(in) + std::move(after_); auto at = ::pushmi::now(in) + std::move(after_);
::pushmi::submit(in, std::move(at), std::move(out)); ::pushmi::submit(in, std::move(at), std::move(out));
return in; return in;
...@@ -124,7 +115,7 @@ private: ...@@ -124,7 +115,7 @@ private:
In impl_(In in) { In impl_(In in) {
bool done = false; bool done = false;
std::condition_variable signaled; std::condition_variable signaled;
auto out{::pushmi::detail::out_from<In>( auto out{::pushmi::detail::out_from_fn<In>()(
std::move(args_), std::move(args_),
on_value{[&]<class Out, class V>(Out out, V&& v){ on_value{[&]<class Out, class V>(Out out, V&& v){
if constexpr ((bool)TimeSender<std::remove_cvref_t<V>>) { if constexpr ((bool)TimeSender<std::remove_cvref_t<V>>) {
...@@ -150,7 +141,7 @@ private: ...@@ -150,7 +141,7 @@ private:
signaled.notify_all(); signaled.notify_all();
}} }}
)}; )};
if constexpr ((bool)IsTimeSender) { if constexpr (IsTimeSender) {
::pushmi::submit(in, ::pushmi::now(in), std::move(out)); ::pushmi::submit(in, ::pushmi::now(in), std::move(out));
} else { } else {
::pushmi::submit(in, std::move(out)); ::pushmi::submit(in, std::move(out));
......
...@@ -15,23 +15,26 @@ namespace pushmi { ...@@ -15,23 +15,26 @@ namespace pushmi {
namespace operators { namespace operators {
namespace detail { namespace detail {
template <class SideEffects, class Out> template <Receiver SideEffects, Receiver Out>
struct tap_ { struct tap_ {
SideEffects sideEffects; SideEffects sideEffects;
Out out; Out out;
using receiver_category = single_tag; using side_effects_tag = receiver_category_t<SideEffects>;
using out_tag = receiver_category_t<Out>;
using receiver_category = std::common_type_t<side_effects_tag, out_tag>;
template <class V> template <class V, class UV = std::remove_reference_t<V>>
requires SingleReceiver<SideEffects, const V&>&& SingleReceiver<Out, V> requires SingleReceiver<SideEffects, const UV&> && SingleReceiver<Out, V>
void value(V&& v) { void value(V&& v) {
::pushmi::set_value(sideEffects, const_cast<const V&>(v)); ::pushmi::set_value(sideEffects, std::as_const(v));
::pushmi::set_value(out, (V&&) v); ::pushmi::set_value(out, (V&&) v);
} }
template <class E> template <class E>
requires NoneReceiver<SideEffects, E>&& NoneReceiver<Out, E> requires NoneReceiver<SideEffects, const E&> && NoneReceiver<Out, E>
void error(E e) noexcept { void error(E e) noexcept {
::pushmi::set_error(sideEffects, const_cast<const E&>(e)); ::pushmi::set_error(sideEffects, std::as_const(e));
::pushmi::set_error(out, std::move(e)); ::pushmi::set_error(out, std::move(e));
} }
void done() { void done() {
...@@ -41,29 +44,23 @@ struct tap_ { ...@@ -41,29 +44,23 @@ struct tap_ {
}; };
template <Receiver SideEffects, Receiver Out> template <Receiver SideEffects, Receiver Out>
tap_(SideEffects, Out)->tap_<SideEffects, Out>; tap_(SideEffects, Out) -> tap_<SideEffects, Out>;
struct tap_fn { struct tap_fn {
template <class... AN> template <class... AN>
auto operator()(AN... an) const; auto operator()(AN... an) const;
}; };
template <class... AN> template <class... AN>
auto tap_fn::operator()(AN... an) const { auto tap_fn::operator()(AN... an) const {
auto args = std::tuple{std::move(an)...}; return [args = std::tuple{std::move(an)...}]<class In>(In in) mutable {
return [args = std::move(args)]<class In>(In in) mutable { auto sideEffects{::pushmi::detail::out_from_fn<In>()(std::move(args))};
auto sideEffects{::pushmi::detail::out_from<In>(std::move(args))};
using SideEffects = decltype(sideEffects); using SideEffects = decltype(sideEffects);
static_assert( static_assert(
::pushmi::detail::deferred_requires_from<In, SideEffects, ::pushmi::detail::deferred_requires_from<In, SideEffects,
Receiver<SideEffects> && SenderTo<In, SideEffects, none_tag>,
SenderTo<In, SideEffects>,
Receiver<SideEffects> &&
SenderTo<In, SideEffects, single_tag>, SenderTo<In, SideEffects, single_tag>,
Receiver<SideEffects> &&
TimeSenderTo<In, SideEffects, single_tag> >(), TimeSenderTo<In, SideEffects, single_tag> >(),
"'In' is not deliverable to 'SideEffects'"); "'In' is not deliverable to 'SideEffects'");
...@@ -73,26 +70,17 @@ auto tap_fn::operator()(AN... an) const { ...@@ -73,26 +70,17 @@ auto tap_fn::operator()(AN... an) const {
[sideEffects = std::move(sideEffects)]<class Out>(Out out) { [sideEffects = std::move(sideEffects)]<class Out>(Out out) {
static_assert( static_assert(
::pushmi::detail::deferred_requires_from<In, SideEffects, ::pushmi::detail::deferred_requires_from<In, SideEffects,
Receiver<Out> && SenderTo<In, Out, none_tag>,
SenderTo<In, Out>,
Receiver<Out> &&
SenderTo<In, Out, single_tag>, SenderTo<In, Out, single_tag>,
Receiver<Out> &&
TimeSenderTo<In, Out, single_tag> >(), TimeSenderTo<In, Out, single_tag> >(),
"'In' is not deliverable to 'Out'"); "'In' is not deliverable to 'Out'");
auto gang{::pushmi::detail::out_from<In>(detail::tap_{sideEffects, std::move(out)})}; auto gang{::pushmi::detail::out_from_fn<In>()(
detail::tap_{sideEffects, std::move(out)})};
using Gang = decltype(gang); using Gang = decltype(gang);
static_assert( static_assert(
::pushmi::detail::deferred_requires_from<In, SideEffects, ::pushmi::detail::deferred_requires_from<In, SideEffects,
Receiver<Gang> &&
SenderTo<In, Gang>, SenderTo<In, Gang>,
Receiver<Gang> &&
SenderTo<In, Gang, single_tag>, SenderTo<In, Gang, single_tag>,
Receiver<Gang> &&
TimeSenderTo<In, Gang, single_tag> >(), TimeSenderTo<In, Gang, single_tag> >(),
"'In' is not deliverable to 'Out' & 'SideEffects'"); "'In' is not deliverable to 'Out' & 'SideEffects'");
return gang; return gang;
......
...@@ -17,19 +17,20 @@ namespace operators { ...@@ -17,19 +17,20 @@ namespace operators {
namespace detail { namespace detail {
struct transform_fn { struct transform_fn {
template <class... FN> template <class... FN>
auto operator()(FN... fn) const; auto operator()(FN... fn) const;
}; };
template <class... FN> template <class... FN>
auto transform_fn::operator()(FN... fn) const { auto transform_fn::operator()(FN... fn) const {
auto f = overload{std::move(fn)...}; auto f = overload{std::move(fn)...};
return [f = std::move(f)]<class In>(In in) { return [f = std::move(f)]<class In>(In in) {
// copy 'f' to allow multiple calls to connect to multiple 'in' // copy 'f' to allow multiple calls to connect to multiple 'in'
return ::pushmi::detail::deferred_from<In, archetype_single>( return ::pushmi::detail::deferred_from<In, single<>>(
std::move(in), std::move(in),
::pushmi::detail::submit_transform_out<In>( ::pushmi::detail::submit_transform_out<In>(
[f]<class Out>(Out out) { [f]<class Out>(Out out) {
return ::pushmi::detail::out_from<In>( return ::pushmi::detail::out_from_fn<In>()(
std::move(out), std::move(out),
// copy 'f' to allow multiple calls to submit // copy 'f' to allow multiple calls to submit
on_value{ on_value{
......
...@@ -15,7 +15,7 @@ namespace operators { ...@@ -15,7 +15,7 @@ namespace operators {
namespace detail { namespace detail {
class via_fn { class via_fn {
template <class ExecutorFactory> template <Invocable ExecutorFactory>
auto operator()(ExecutorFactory ef) const; auto operator()(ExecutorFactory ef) const;
}; };
...@@ -30,15 +30,15 @@ struct via_fn_data : public Out { ...@@ -30,15 +30,15 @@ struct via_fn_data : public Out {
template<class Executor, class Out> template<class Executor, class Out>
via_fn_data(Out, Executor) -> via_fn_data<Executor, Out>; via_fn_data(Out, Executor) -> via_fn_data<Executor, Out>;
template <class ExecutorFactory> template <Invocable ExecutorFactory>
auto via_fn::operator()(ExecutorFactory ef) const { auto via_fn::operator()(ExecutorFactory ef) const {
return [ef = std::move(ef)]<class In>(In in) { return [ef = std::move(ef)]<class In>(In in) {
return ::pushmi::detail::deferred_from<In, archetype_single>( return ::pushmi::detail::deferred_from<In, single<>>(
std::move(in), std::move(in),
::pushmi::detail::submit_transform_out<In>( ::pushmi::detail::submit_transform_out<In>(
[ef]<class Out>(Out out) { [ef]<class Out>(Out out) {
auto exec = ef(); auto exec = ef();
return ::pushmi::detail::out_from<In>( return ::pushmi::detail::out_from_fn<In>()(
via_fn_data{std::move(out), std::move(exec)}, via_fn_data{std::move(out), std::move(exec)},
// copy 'f' to allow multiple calls to submit // copy 'f' to allow multiple calls to submit
::pushmi::on_value{[]<class V>(auto& data, V&& v){ ::pushmi::on_value{[]<class V>(auto& data, V&& v){
......
...@@ -4,15 +4,17 @@ ...@@ -4,15 +4,17 @@
// This source code is licensed under the MIT license found in the // This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree. // LICENSE file in the root directory of this source tree.
template <class In, class Operator> #include "traits.h"
auto operator|(In&& in, Operator op) -> decltype(op(std::forward<In>(in))) {
return op(std::forward<In>(in)); template <class In, pushmi::Invocable<In> Op>
decltype(auto) operator|(In&& in, Op op) {
return op((In&&) in);
} }
namespace pushmi { namespace pushmi {
template<class T, class... FN> template<class T, class... FN>
auto pipe(T t, FN... fn) { auto pipe(T t, FN... fn) -> decltype((t | ... | fn)) {
return (t | ... | fn); return (t | ... | fn);
} }
......
...@@ -4,15 +4,16 @@ ...@@ -4,15 +4,16 @@
// This source code is licensed under the MIT license found in the // This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree. // LICENSE file in the root directory of this source tree.
#include <future>
#include "none.h" #include "none.h"
namespace pushmi { namespace pushmi {
namespace detail { namespace detail {
template<class data, class op, class V, class E> template<class data, class V, class E>
struct single_vtable { struct single_vtable {
void (*op_)(op, data&, data*) = +[](op, data&, data*) {}; void (*op_)(data&, data*) = +[](data&, data*) {};
void (*done_)(data&) = +[](data&) {}; void (*done_)(data&) = +[](data&) {};
void (*error_)(data&, E) noexcept = +[](data&, E) noexcept { void (*error_)(data&, E) noexcept = +[](data&, E) noexcept {
std::terminate(); std::terminate();
...@@ -22,19 +23,16 @@ struct single_vtable { ...@@ -22,19 +23,16 @@ struct single_vtable {
static constexpr single_vtable const noop_ = {}; static constexpr single_vtable const noop_ = {};
}; };
template <class data, class op, class V, class E, class Wrapped, bool insitu> template <class data, class V, class E, class Wrapped, bool insitu>
struct single_vtable_v; struct single_vtable_v;
template <class data, class op, class V, class E, class Wrapped> template <class data, class V, class E, class Wrapped>
struct single_vtable_v<data, op, V, E, Wrapped, false> { struct single_vtable_v<data, V, E, Wrapped, false> {
static constexpr single_vtable<data, op, V, E> const vtable_v = { static constexpr single_vtable<data, V, E> const vtable_v = {
+[](op o, data& src, data* dst) { +[](data& src, data* dst) {
switch (o) { if (dst)
case op::move:
dst->pobj_ = std::exchange(src.pobj_, nullptr); dst->pobj_ = std::exchange(src.pobj_, nullptr);
case op::destroy:
delete static_cast<Wrapped const*>(src.pobj_); delete static_cast<Wrapped const*>(src.pobj_);
}
}, },
+[](data& src) { ::pushmi::set_done(*static_cast<Wrapped*>(src.pobj_)); }, +[](data& src) { ::pushmi::set_done(*static_cast<Wrapped*>(src.pobj_)); },
+[](data& src, E e) noexcept { +[](data& src, E e) noexcept {
...@@ -49,17 +47,14 @@ struct single_vtable_v<data, op, V, E, Wrapped, false> { ...@@ -49,17 +47,14 @@ struct single_vtable_v<data, op, V, E, Wrapped, false> {
}; };
}; };
template <class data, class op, class V, class E, class Wrapped> template <class data, class V, class E, class Wrapped>
struct single_vtable_v<data, op, V, E, Wrapped, true> { struct single_vtable_v<data, V, E, Wrapped, true> {
static constexpr single_vtable<data, op, V, E> const vtable_v = { static constexpr single_vtable<data, V, E> const vtable_v = {
+[](op o, data& src, data* dst) { +[](data& src, data* dst) {
switch (o) { if (dst)
case op::move:
new (dst->buffer_) Wrapped( new (dst->buffer_) Wrapped(
std::move(*static_cast<Wrapped*>((void*)src.buffer_))); std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
case op::destroy:
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped(); static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
}, },
+[](data& src) { +[](data& src) {
::pushmi::set_done(*static_cast<Wrapped*>((void*)src.buffer_)); ::pushmi::set_done(*static_cast<Wrapped*>((void*)src.buffer_));
...@@ -80,8 +75,8 @@ struct single_vtable_v<data, op, V, E, Wrapped, true> { ...@@ -80,8 +75,8 @@ struct single_vtable_v<data, op, V, E, Wrapped, true> {
// Class static definitions: // Class static definitions:
template<class data, class op, class V, class E> template<class data, class V, class E>
constexpr single_vtable<data, op, V, E> const single_vtable<data, op, V, E>::noop_; constexpr single_vtable<data, V, E> const single_vtable<data, V, E>::noop_;
} // namespace detail } // namespace detail
...@@ -97,20 +92,20 @@ class single<V, E> { ...@@ -97,20 +92,20 @@ class single<V, E> {
return sizeof(Wrapped) <= sizeof(data::buffer_) && return sizeof(Wrapped) <= sizeof(data::buffer_) &&
std::is_nothrow_move_constructible_v<Wrapped>; std::is_nothrow_move_constructible_v<Wrapped>;
} }
enum struct op { destroy, move }; using vtable = detail::single_vtable<data, V, E>;
using vtable = detail::single_vtable<data, op, V, E>;
vtable const* vptr_ = &vtable::noop_; vtable const* vptr_ = &vtable::noop_;
template <class T, class U = std::decay_t<T>>
using wrapped_t =
std::enable_if_t<!std::is_same_v<U, single>, U>;
public: public:
using receiver_category = single_tag; using receiver_category = single_tag;
single() = default; single() = default;
single(single&& that) noexcept : single() { single(single&& that) noexcept : single() {
that.vptr_->op_(op::move, that.data_, &data_); that.vptr_->op_(that.data_, &data_);
std::swap(that.vptr_, vptr_); std::swap(that.vptr_, vptr_);
} }
template <class T, class U = std::decay_t<T>>
using wrapped_t =
std::enable_if_t<!std::is_same_v<U, single>, U>;
template <class Wrapped> template <class Wrapped>
requires SingleReceiver<wrapped_t<Wrapped>, V, E> requires SingleReceiver<wrapped_t<Wrapped>, V, E>
explicit single(Wrapped obj) : single() { explicit single(Wrapped obj) : single() {
...@@ -124,10 +119,11 @@ public: ...@@ -124,10 +119,11 @@ public:
new (data_.buffer_) Wrapped(std::move(obj)); new (data_.buffer_) Wrapped(std::move(obj));
else else
data_.pobj_ = new Wrapped(std::move(obj)); data_.pobj_ = new Wrapped(std::move(obj));
vptr_ = &detail::single_vtable_v<data, op, V, E, Wrapped, insitu<Wrapped>()>::vtable_v; vptr_ =
&detail::single_vtable_v<data, V, E, Wrapped, insitu<Wrapped>()>::vtable_v;
} }
~single() { ~single() {
vptr_->op_(op::destroy, data_, nullptr); vptr_->op_(data_, nullptr);
} }
single& operator=(single&& that) noexcept { single& operator=(single&& that) noexcept {
this->~single(); this->~single();
...@@ -137,39 +133,41 @@ public: ...@@ -137,39 +133,41 @@ public:
template<class T> template<class T>
requires ConvertibleTo<T&&, V&&> requires ConvertibleTo<T&&, V&&>
void value(T&& t) { void value(T&& t) {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
vptr_->rvalue_(data_, (T&&) t); vptr_->rvalue_(data_, (T&&) t);
} }
}
template<class T> template<class T>
requires ConvertibleTo<T&, V&> requires ConvertibleTo<T&, V&>
void value(T& t) { void value(T& t) {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
vptr_->lvalue_(data_, t); vptr_->lvalue_(data_, t);
} }
}
void error(E e) noexcept { void error(E e) noexcept {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
vptr_->error_(data_, std::move(e)); vptr_->error_(data_, std::move(e));
} }
}
void done() { void done() {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
vptr_->done_(data_); vptr_->done_(data_);
} }
}
}; };
template <class VF, class EF, class DF> template <class VF, class EF, class DF>
requires Invocable<DF&> class single<VF, EF, DF> { requires Invocable<DF&>
class single<VF, EF, DF> {
bool done_ = false; bool done_ = false;
VF vf_; VF vf_{};
EF ef_; EF ef_{};
DF df_; DF df_{};
public:
using receiver_category = single_tag;
static_assert( static_assert(
!detail::is_v<VF, on_error>, !detail::is_v<VF, on_error>,
...@@ -180,6 +178,9 @@ requires Invocable<DF&> class single<VF, EF, DF> { ...@@ -180,6 +178,9 @@ requires Invocable<DF&> class single<VF, EF, DF> {
static_assert(NothrowInvocable<EF, std::exception_ptr>, static_assert(NothrowInvocable<EF, std::exception_ptr>,
"error function must be noexcept and support std::exception_ptr"); "error function must be noexcept and support std::exception_ptr");
public:
using receiver_category = single_tag;
single() = default; single() = default;
constexpr explicit single(VF vf) : single(std::move(vf), EF{}, DF{}) {} constexpr explicit single(VF vf) : single(std::move(vf), EF{}, DF{}) {}
constexpr explicit single(EF ef) : single(VF{}, std::move(ef), DF{}) {} constexpr explicit single(EF ef) : single(VF{}, std::move(ef), DF{}) {}
...@@ -187,37 +188,40 @@ requires Invocable<DF&> class single<VF, EF, DF> { ...@@ -187,37 +188,40 @@ requires Invocable<DF&> class single<VF, EF, DF> {
constexpr single(EF ef, DF df) constexpr single(EF ef, DF df)
: done_(false), vf_(), ef_(std::move(ef)), df_(std::move(df)) {} : done_(false), vf_(), ef_(std::move(ef)), df_(std::move(df)) {}
constexpr single(VF vf, EF ef, DF df = DF{}) constexpr single(VF vf, EF ef, DF df = DF{})
: done_(false), vf_(std::move(vf)), ef_(std::move(ef)), df_(std::move(df)) {} : done_(false), vf_(std::move(vf)), ef_(std::move(ef)), df_(std::move(df))
{}
template <class V> template <class V>
requires Invocable<VF&, V> void value(V&& v) { requires Invocable<VF&, V>
void value(V&& v) {
if (done_) {return;} if (done_) {return;}
done_ = true; done_ = true;
vf_((V&&) v); vf_((V&&) v);
} }
template <class E> template <class E>
requires Invocable<EF&, E> void error(E e) noexcept { requires Invocable<EF&, E>
void error(E e) noexcept {
static_assert(NothrowInvocable<EF&, E>, "error function must be noexcept"); static_assert(NothrowInvocable<EF&, E>, "error function must be noexcept");
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
ef_(std::move(e)); ef_(std::move(e));
} }
}
void done() { void done() {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
df_(); df_();
} }
}
}; };
template <Receiver Data, class DVF, class DEF, class DDF> template <Receiver Data, class DVF, class DEF, class DDF>
requires Invocable<DDF&, Data&> class single<Data, DVF, DEF, DDF> { requires Invocable<DDF&, Data&> class single<Data, DVF, DEF, DDF> {
bool done_ = false; bool done_ = false;
Data data_; Data data_{};
DVF vf_; DVF vf_{};
DEF ef_; DEF ef_{};
DDF df_; DDF df_{};
public:
using receiver_category = single_tag;
static_assert( static_assert(
!detail::is_v<DVF, on_error>, !detail::is_v<DVF, on_error>,
...@@ -228,8 +232,9 @@ requires Invocable<DDF&, Data&> class single<Data, DVF, DEF, DDF> { ...@@ -228,8 +232,9 @@ requires Invocable<DDF&, Data&> class single<Data, DVF, DEF, DDF> {
static_assert(NothrowInvocable<DEF, Data&, std::exception_ptr>, static_assert(NothrowInvocable<DEF, Data&, std::exception_ptr>,
"error function must be noexcept and support std::exception_ptr"); "error function must be noexcept and support std::exception_ptr");
template<class... AN> public:
constexpr explicit single(std::tuple<AN...> t) : single(std::apply(construct<Data>{}, std::move(t)), DEF{}, DDF{}) {} using receiver_category = single_tag;
constexpr explicit single(Data d) constexpr explicit single(Data d)
: single(std::move(d), DVF{}, DEF{}, DDF{}) {} : single(std::move(d), DVF{}, DEF{}, DDF{}) {}
constexpr single(Data d, DDF df) constexpr single(Data d, DDF df)
...@@ -238,25 +243,31 @@ requires Invocable<DDF&, Data&> class single<Data, DVF, DEF, DDF> { ...@@ -238,25 +243,31 @@ requires Invocable<DDF&, Data&> class single<Data, DVF, DEF, DDF> {
: done_(false), data_(std::move(d)), vf_(), ef_(ef), df_(df) {} : done_(false), data_(std::move(d)), vf_(), ef_(ef), df_(df) {}
constexpr single(Data d, DVF vf, DEF ef = DEF{}, DDF df = DDF{}) constexpr single(Data d, DVF vf, DEF ef = DEF{}, DDF df = DDF{})
: done_(false), data_(std::move(d)), vf_(vf), ef_(ef), df_(df) {} : done_(false), data_(std::move(d)), vf_(vf), ef_(ef), df_(df) {}
template <class V> template <class V>
requires Invocable<DVF&, Data&, V> void value(V&& v) { requires Invocable<DVF&, Data&, V>
if (done_) {return;} void value(V&& v) {
if (!done_) {
done_ = true; done_ = true;
vf_(data_, (V&&) v); vf_(data_, (V&&) v);
} }
}
template <class E> template <class E>
requires Invocable<DEF&, Data&, E> void error(E e) noexcept { requires Invocable<DEF&, Data&, E>
void error(E e) noexcept {
static_assert( static_assert(
NothrowInvocable<DEF&, Data&, E>, "error function must be noexcept"); NothrowInvocable<DEF&, Data&, E>, "error function must be noexcept");
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
ef_(data_, std::move(e)); ef_(data_, std::move(e));
} }
}
void done() { void done() {
if (done_) {return;} if (!done_) {
done_ = true; done_ = true;
df_(data_); df_(data_);
} }
}
}; };
template <> template <>
...@@ -264,70 +275,51 @@ class single<> ...@@ -264,70 +275,51 @@ class single<>
: public single<ignoreVF, abortEF, ignoreDF> { : public single<ignoreVF, abortEF, ignoreDF> {
}; };
using archetype_single = single<>; single() -> single<>;
single()->single<>;
template <class... AN>
single(std::tuple<AN...>) ->
single<decltype(single{std::declval<AN&&>()...}), passDVF, passDEF, passDDF>;
template <class VF> template <class VF>
requires !Receiver<VF> && !detail::is_v<VF, on_error> &&
!detail::is_v<VF, on_done>
single(VF) -> single<VF, abortEF, ignoreDF>; single(VF) -> single<VF, abortEF, ignoreDF>;
template <class... EFN> template <class... EFN>
single(on_error<EFN...>) -> single<ignoreVF, on_error<EFN...>, ignoreDF>; single(on_error<EFN...>) -> single<ignoreVF, on_error<EFN...>, ignoreDF>;
template <class DF> template <class DF>
single(on_done<DF>) -> single<ignoreVF, abortEF, on_done<DF>>; requires Invocable<DF&>
single(DF) -> single<ignoreVF, abortEF, DF>;
template <class V, class E, class Wrapped>
requires SingleReceiver<Wrapped, V, E>
single(Wrapped)->single<V, E>;
template <class VF, class EF> template <class VF, class EF>
requires !Receiver<VF> && !detail::is_v<VF, on_error> &&
!detail::is_v<VF, on_done> && !detail::is_v<EF, on_value> &&
!detail::is_v<EF, on_done> && !Invocable<VF&> && !Invocable<EF&>
single(VF, EF) -> single<VF, EF, ignoreDF>; single(VF, EF) -> single<VF, EF, ignoreDF>;
template <class... EFN, class DF> template <class EF, class DF>
single(on_error<EFN...>, on_done<DF>) -> requires Invocable<DF&>
single<ignoreVF, on_error<EFN...>, on_done<DF>>; single(EF, DF) -> single<ignoreVF, EF, DF>;
template <class VF, class EF, class DF> template <class VF, class EF, class DF>
requires !Invocable<VF&> && !Invocable<EF&> && Invocable<DF&> requires Invocable<DF&>
single(VF, EF, DF)->single<VF, EF, DF>; single(VF, EF, DF) -> single<VF, EF, DF>;
template <Receiver<single_tag> Data> template <Receiver<single_tag> Data>
single(Data d) -> single<Data, passDVF, passDEF, passDDF>; single(Data d) -> single<Data, passDVF, passDEF, passDDF>;
template <Receiver<single_tag> Data, class DVF> template <Receiver<single_tag> Data, class DVF>
requires !detail::is_v<DVF, on_error> && single(Data d, DVF vf) -> single<Data, DVF, passDEF, passDDF>;
!detail::is_v<DVF, on_done>
single(Data d, DVF vf)
->single<Data, DVF, passDEF, passDDF>;
template <Receiver<single_tag> Data, class... DEFN> template <Receiver<single_tag> Data, class... DEFN>
single(Data d, on_error<DEFN...>) single(Data d, on_error<DEFN...>) ->
->single<Data, passDVF, on_error<DEFN...>, passDDF>; single<Data, passDVF, on_error<DEFN...>, passDDF>;
template <Receiver<single_tag> Data, class DDF>
requires Invocable<DDF&, Data&>
single(Data d, DDF) -> single<Data, passDVF, passDEF, DDF>;
template <Receiver<single_tag> Data, class DVF, class DEF> template <Receiver<single_tag> Data, class DVF, class DEF>
requires !detail::is_v<DVF, on_error> && !detail::is_v<DVF, on_done> &&
!detail::is_v<DEF, on_done> && !Invocable<DVF&, Data&> && !Invocable<DEF&, Data&>
single(Data d, DVF vf, DEF ef) -> single<Data, DVF, DEF, passDDF>; single(Data d, DVF vf, DEF ef) -> single<Data, DVF, DEF, passDDF>;
template <Receiver<single_tag> Data, class... DEFN, class DDF> template <Receiver<single_tag> Data, class DEF, class DDF>
single(Data d, on_error<DEFN...>, on_done<DDF>) -> requires Invocable<DDF&, Data&>
single<Data, passDVF, on_error<DEFN...>, on_done<DDF>>; single(Data d, DEF, DDF) -> single<Data, passDVF, DEF, DDF>;
template <Receiver<single_tag> Data, class DDF>
single(Data d, on_done<DDF>)->single<Data, passDVF, passDEF, on_done<DDF>>;
template <Receiver<single_tag> Data, class DVF, class DEF, class DDF> template <Receiver<single_tag> Data, class DVF, class DEF, class DDF>
requires !Invocable<DVF&, Data&> && !Invocable<DEF&, Data&> && Invocable<DDF&, Data&>
single(Data d, DVF vf, DEF ef, DDF df) -> single<Data, DVF, DEF, DDF>; single(Data d, DVF vf, DEF ef, DDF df) -> single<Data, DVF, DEF, DDF>;
template <class V, class E = std::exception_ptr> template <class V, class E = std::exception_ptr>
...@@ -339,12 +331,12 @@ using any_single = single<V, E>; ...@@ -339,12 +331,12 @@ using any_single = single<V, E>;
// return single<V, E>{std::move(w)}; // return single<V, E>{std::move(w)};
// } // }
template<class T> template<class T, SenderTo<std::promise<T>, single_tag> Out>
std::future<T> future_from(auto singleSender) { std::future<T> future_from(Out singleSender) {
std::promise<T> p; std::promise<T> p;
auto result = p.get_future(); auto result = p.get_future();
submit(singleSender, single{std::move(p)}); submit(singleSender, std::move(p));
return result; return result;
} }
} // namespace values } // namespace pushmi
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
// This source code is licensed under the MIT license found in the // This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree. // LICENSE file in the root directory of this source tree.
#include <functional>
#include <type_traits> #include <type_traits>
namespace pushmi { namespace pushmi {
...@@ -101,12 +102,11 @@ using requires_ = std::enable_if_t<B, T>; ...@@ -101,12 +102,11 @@ using requires_ = std::enable_if_t<B, T>;
} // namespace pushmi } // namespace pushmi
#if 1 #if 1
#define PUSHMI_VOID_LAMBDA_REQUIRES(...) \
->::pushmi::detail::requires_<(__VA_ARGS__)>
#define PUSHMI_VOID_LAMBDA_REQUIRES(RequiresExp...) \ #define PUSHMI_T_LAMBDA_REQUIRES(T, ...) \
->::pushmi::detail::requires_<(RequiresExp)> ->::pushmi::detail::requires_<(__VA_ARGS__), T>
#define PUSHMI_T_LAMBDA_REQUIRES(T, RequiresExp...) \
->::pushmi::detail::requires_<(RequiresExp), T>
#elif 0 #elif 0
// unsupported syntax.. // unsupported syntax..
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment