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); delete static_cast<Wrapped const*>(src.pobj_);
case op::destroy:
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_))); static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
case op::destroy:
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))),
done_ = true; "error function must be noexcept");
ef_(e); if (!done_) {
done_ = true;
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>
...@@ -198,10 +125,10 @@ namespace detail{ ...@@ -198,10 +125,10 @@ namespace detail{
struct set_value_fn { struct set_value_fn {
template<class V> template<class V>
auto operator()(V&& v) const { auto operator()(V&& v) const {
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,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..
......
...@@ -32,10 +32,10 @@ SCENARIO( "empty can be used with tap and submit", "[empty][deferred]" ) { ...@@ -32,10 +32,10 @@ SCENARIO( "empty can be used with tap and submit", "[empty][deferred]" ) {
int signals = 0; int signals = 0;
e | e |
op::tap( op::tap(
[&](auto e) noexcept { signals += 1000; }, [&](auto e) noexcept { signals += 1000; },
[&](){ signals += 10; }) | [&](){ signals += 10; }) |
op::submit( op::submit(
[&](auto e) noexcept { signals += 1000; }, [&](auto e) noexcept { signals += 1000; },
[&](){ signals += 10; }); [&](){ signals += 10; });
THEN( "the done signal is recorded twice" ) { THEN( "the done signal is recorded twice" ) {
...@@ -63,11 +63,11 @@ SCENARIO( "empty can be used with tap and submit", "[empty][deferred]" ) { ...@@ -63,11 +63,11 @@ SCENARIO( "empty can be used with tap and submit", "[empty][deferred]" ) {
e | e |
op::tap( op::tap(
[&](auto v){ signals += 100; }, [&](auto v){ signals += 100; },
[&](auto e) noexcept { signals += 1000; }, [&](auto e) noexcept { signals += 1000; },
[&](){ signals += 10; }) | [&](){ signals += 10; }) |
op::submit( op::submit(
[&](auto v){ signals += 100; }, [&](auto v){ signals += 100; },
[&](auto e) noexcept { signals += 1000; }, [&](auto e) noexcept { signals += 1000; },
[&](){ signals += 10; }); [&](){ signals += 10; });
THEN( "the done signal is recorded twice" ) { THEN( "the done signal is recorded twice" ) {
...@@ -96,7 +96,7 @@ SCENARIO( "just() can be used with transform and submit", "[just][deferred]" ) { ...@@ -96,7 +96,7 @@ SCENARIO( "just() can be used with transform and submit", "[just][deferred]" ) {
[&](int v){ signals += 10000; return v * 2; }) | [&](int v){ signals += 10000; return v * 2; }) |
op::submit( op::submit(
[&](auto v){ value = v; signals += 100; }, [&](auto v){ value = v; signals += 100; },
[&](auto e) noexcept { signals += 1000; }, [&](auto e) noexcept { signals += 1000; },
[&](){ signals += 10; }); [&](){ signals += 10; });
THEN( "the transform signal is recorded twice, the value signal once and the result is correct" ) { THEN( "the transform signal is recorded twice, the value signal once and the result is correct" ) {
......
...@@ -56,7 +56,7 @@ SCENARIO( "trampoline executor", "[trampoline][deferred]" ) { ...@@ -56,7 +56,7 @@ SCENARIO( "trampoline executor", "[trampoline][deferred]" ) {
op::submit( op::submit(
[&](auto at){ signaled = at; [&](auto at){ signaled = at;
signals += 100; }, signals += 100; },
[&](auto e) noexcept { signals += 1000; }, [&](auto e) noexcept { signals += 1000; },
[&](){ signals += 10; }); [&](){ signals += 10; });
THEN( "the value signal is recorded once and the signal did not drift much" ) { THEN( "the value signal is recorded once and the signal did not drift much" ) {
......
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