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 {
};
template<template <class...> class T>
struct construct_deduced {
struct construct_deduced;
template<>
struct construct_deduced<none> {
template<class... AN>
requires requires (AN&&... an) { T{(AN&&) an...}; }
auto operator()(AN&&... an) const {
return T{std::forward<AN>(an)...};
auto operator()(AN&&... an) const -> decltype(none{(AN&&) an...}) {
return none{(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>
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 {
template <class V>
void operator()(V&&) {}
......
......@@ -16,20 +16,6 @@ struct none_tag : silent_tag {};
struct single_tag : none_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>
using __sender_category_t = typename T::sender_category;
......@@ -69,8 +55,8 @@ using receiver_category_t = __receiver_category_t<receiver_traits<T>>;
////////////////////////////////////////////////////////////////////////////////
// Receiver concepts
template <class S, class Tag = silent_tag>
concept bool Receiver = Valid<receiver_traits<S>, __receiver_category_t> &&
Derived<receiver_category_t<S>, Tag> &&
......@@ -93,11 +79,8 @@ concept bool SingleReceiver = NoneReceiver<S, E> &&
};
////////////////////////////////////////////////////////////////////////////////
// Sender concepts
template <class D, class Tag = silent_tag>
concept bool Sender = Valid<sender_traits<D>, __sender_category_t> &&
Derived<sender_category_t<D>, Tag> && SemiMovable<D>;
......@@ -110,24 +93,22 @@ concept bool SenderTo = Sender<D, Tag> &&
};
////////////////////////////////////////////////////////////////////////////////
// TimeSender concepts
template <class D, class Tag = silent_tag>
concept bool TimeSender = Sender<D, Tag> && requires(D& d) {
{ ::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>
concept bool TimeSenderTo = Receiver<S, Tag> && TimeSender<D, Tag> &&
requires(D& d, S&& s) {
::pushmi::submit(d, ::pushmi::now(d), (S &&) s);
requires(D& d, S&& s, time_point_t<D> tp) {
::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
// // enum or any other ordering constraint value-type.
......
......@@ -73,7 +73,7 @@ public:
// ask whether T'& is convertible to T. That brings us right back to this
// constructor. Constraint recursion!
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);
vptr_ = detail::any_time_executor_ref_vtable_v<E, TP, Other, Wrapped>();
}
......
......@@ -279,9 +279,7 @@ class flow_single<>
: public flow_single<ignoreVF, abortEF, ignoreDF, ignoreStpF, ignoreStrtF> {
};
using archetype_flow_single = flow_single<>;
flow_single()->archetype_flow_single;
flow_single()->flow_single<>;
template <class VF>
requires !Receiver<VF> && !detail::is_v<VF, on_error> &&
......
......@@ -20,9 +20,8 @@ class none<E> {
return sizeof(Wrapped) <= sizeof(data::buffer_) &&
std::is_nothrow_move_constructible_v<Wrapped>;
}
enum struct op { destroy, move };
struct vtable {
void (*op_)(op, data&, data*) = +[](op, data&, data*) {};
void (*op_)(data&, data*) = +[](data&, data*) {};
void (*done_)(data&) = +[](data&) {};
void (*error_)(data&, E) noexcept = +[](data&, E) noexcept {
std::terminate();
......@@ -31,13 +30,10 @@ class none<E> {
} const* vptr_ = &vtable::noop_;
template <class Wrapped, bool = insitu<Wrapped>()>
static constexpr vtable const vtable_v = {
+[](op o, data& src, data* dst) {
switch (o) {
case op::move:
+[](data& src, data* dst) {
if (dst)
dst->pobj_ = std::exchange(src.pobj_, nullptr);
case op::destroy:
delete static_cast<Wrapped const*>(src.pobj_);
}
},
+[](data& src) { ::pushmi::set_done(*static_cast<Wrapped*>(src.pobj_)); },
+[](data& src, E e) noexcept {
......@@ -52,7 +48,7 @@ public:
none() = default;
none(none&& that) noexcept : none() {
that.vptr_->op_(op::move, that.data_, &data_);
that.vptr_->op_(that.data_, &data_);
std::swap(that.vptr_, vptr_);
}
template <class Wrapped>
......@@ -65,7 +61,7 @@ public:
vptr_ = &vtable_v<Wrapped>;
}
~none() {
vptr_->op_(op::destroy, data_, nullptr);
vptr_->op_(data_, nullptr);
}
none& operator=(none&& that) noexcept {
this->~none();
......@@ -93,14 +89,11 @@ constexpr typename none<E>::vtable const none<E>::vtable_v;
template <class E>
template <class Wrapped>
constexpr typename none<E>::vtable const none<E>::vtable_v<Wrapped, true> = {
+[](op o, data& src, data* dst) {
switch (o) {
case op::move:
+[](data& src, data* dst) {
if (dst)
new (dst->buffer_)
Wrapped(std::move(*static_cast<Wrapped*>((void*)src.buffer_)));
case op::destroy:
static_cast<Wrapped const*>((void*)src.buffer_)->~Wrapped();
}
},
+[](data& src) {
::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> = {
+[](data& src, E e) noexcept {::pushmi::set_error(
*static_cast<Wrapped*>((void*)src.buffer_),
std::move(e));
}
}
;
}
};
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> {
static_assert(!detail::is_v<EF, on_value> && !detail::is_v<EF, single>);
bool done_ = false;
EF ef_;
DF df_;
EF ef_{};
DF df_{};
public:
public:
using receiver_category = none_tag;
// static_assert(
......@@ -137,27 +130,32 @@ class none<EF, DF> {
template <class E>
requires Invocable<EF&, E>
void error(E e) noexcept {
static_assert(NothrowInvocable<EF&, E>, "error function must be noexcept");
if (done_) {return;}
static_assert(
noexcept(ef_(std::move(e))),
"error function must be noexcept");
if (!done_) {
done_ = true;
ef_(e);
ef_(std::move(e));
}
}
void done() {
if (done_) {return;}
if (!done_) {
done_ = true;
df_();
}
}
};
template <Receiver Data, class DEF, class DDF>
requires Invocable<DDF&, Data&> && !detail::is_v<DEF, on_value> && !detail::is_v<Data, single>
template <Receiver<none_tag> Data, class DEF, class DDF>
requires Invocable<DDF&, Data&>
class none<Data, DEF, DDF> {
bool done_ = false;
Data data_;
DEF ef_;
DDF df_;
public:
Data data_{};
DEF ef_{};
DDF df_{};
static_assert(!detail::is_v<DEF, on_value>);
static_assert(!detail::is_v<Data, single>);
public:
using receiver_category = none_tag;
// static_assert(
......@@ -168,71 +166,59 @@ class none<Data, DEF, DDF> {
constexpr none(Data d, DDF df)
: done_(false), data_(std::move(d)), ef_(), df_(std::move(df)) {}
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>
requires Invocable<DEF&, Data&, E> void error(E e) noexcept {
requires Invocable<DEF&, Data&, E>
void error(E e) noexcept {
static_assert(
NothrowInvocable<DEF&, Data&, E>, "error function must be noexcept");
if (done_) {return;}
noexcept(ef_(data_, std::move(e))), "error function must be noexcept");
if (!done_) {
done_ = true;
ef_(data_, e);
ef_(data_, std::move(e));
}
}
void done() {
if (done_) {return;}
if (!done_) {
done_ = true;
df_(data_);
}
}
};
template <> class none<> : public none<abortEF, ignoreDF> {};
using archetype_none = none<>;
template <>
class none<>
: public none<abortEF, ignoreDF> {
};
none()->archetype_none;
none() -> none<>;
template <class EF>
requires !Receiver<EF> &&
!detail::is_v<EF, single> &&
!detail::is_v<EF, on_value> &&
!detail::is_v<EF, on_done>
none(EF)->none<EF, ignoreDF>;
none(EF) -> none<EF, ignoreDF>;
template <class DF>
requires !Receiver<DF> &&
!detail::is_v<DF, on_value> &&
!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>;
requires Invocable<DF&>
none(DF)->none<abortEF, 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>
requires !detail::is_v<Data, on_value> &&
!detail::is_v<Data, single>
none(Data)->none<Data, passDEF, passDDF>;
template <Receiver<none_tag> Data>
requires !Receiver<Data, single_tag>
none(Data) -> none<Data, passDEF, passDDF>;
template <Receiver Data, class DEF>
requires !detail::is_v<DEF, on_done> &&
!detail::is_v<Data, on_value> &&
!detail::is_v<Data, single>
none(Data, DEF)->none<Data, DEF, passDDF>;
template <Receiver<none_tag> Data, class DEF>
requires !Receiver<Data, single_tag>
none(Data, DEF) -> none<Data, DEF, passDDF>;
template <Receiver Data, class DDF>
requires !detail::is_v<Data, on_value> &&
!detail::is_v<Data, single>
none(Data, on_done<DDF>)->none<Data, passDEF, on_done<DDF>>;
template <Receiver<none_tag> Data, class DDF>
requires Invocable<DDF&, Data&> && !Receiver<Data, single_tag>
none(Data, DDF) -> none<Data, passDEF, DDF>;
template <Receiver Data, class DEF, class DDF>
requires Invocable<DDF&, Data&> &&
!detail::is_v<Data, on_value> &&
!detail::is_v<Data, single>
none(Data, DEF, DDF)->none<Data, DEF, DDF>;
template <Receiver<none_tag> Data, class DEF, class DDF>
requires !Receiver<Data, single_tag>
none(Data, DEF, DDF) -> none<Data, DEF, DDF>;
template <class E = std::exception_ptr>
using any_none = none<E>;
......@@ -251,12 +237,12 @@ using any_none = none<E>;
// return none<E>{std::move(w)};
// }
template <SenderTo<std::promise<void>, none_tag> S>
std::future<void> future_from(S sender) {
template <SenderTo<std::promise<void>, none_tag> Out>
std::future<void> future_from(Out out) {
std::promise<void> p;
auto result = p.get_future();
submit(sender, std::move(p));
submit(out, std::move(p));
return result;
}
} // namespace values
} // namespace pushmi
......@@ -14,108 +14,35 @@ namespace pushmi {
namespace detail{
// template <Sender In>
// struct out_from_fn {
//
// };
template<Sender In, class... AN>
auto out_from(std::tuple<AN...>&& args) {
using SingleReceiver =
decltype(pushmi::sfinae_from_tuple<single>(std::move(args)));
using NoneReceiver =
decltype(pushmi::sfinae_from_tuple<none>(std::move(args)));
if constexpr ((bool)TimeSenderTo<In, SingleReceiver, single_tag> ||
SenderTo<In, SingleReceiver, single_tag>) {
return pushmi::from_tuple<single>(std::move(args));
} else if constexpr ((bool)TimeSenderTo<In, NoneReceiver, none_tag> ||
SenderTo<In, NoneReceiver, none_tag>) {
return pushmi::from_tuple<none>(std::move(args));
}
}
template<Sender In, class... AN, class... DVFN, class... DEFN, class... DDFN>
auto out_from(
std::tuple<AN...>&& args,
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 <class Tag>
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 {
using Make = make_receiver<sender_category_t<In>>;
template <class... Ts>
requires Invocable<Make, Ts...>
auto operator()(std::tuple<Ts...> args) const {
return std::apply(Make(), std::move(args));
}
template <class... Ts, class... Fns,
class This = std::enable_if_t<sizeof...(Fns) != 0, out_from_fn>>
requires (SemiMovable<Fns> &&...) &&
Invocable<Make, std::tuple<Ts...>> &&
Invocable<This, std::invoke_result_t<Make, std::tuple<Ts...>>, Fns...>
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>
requires (SemiMovable<Fns> &&...)
auto operator()(Out out, Fns... fns) const {
return Make()(std::move(out), std::move(fns)...);
}
}
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>
auto submit_transform_out(FN fn){
......@@ -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) {
if constexpr ((bool)TimeSenderTo<In, Out, single_tag>) {
return time_single_deferred{(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) {
if constexpr ((bool)TimeSenderTo<In, Out, single_tag>) {
return time_single_deferred{std::move(in), (FN&&) fn...};
......@@ -175,7 +102,7 @@ auto deferred_from(In in, FN&&... fn) {
template<
Sender In,
class Out,
Receiver Out,
bool SenderRequires,
bool SingleSenderRequires,
bool TimeSingleSenderRequires>
......@@ -201,7 +128,7 @@ struct set_value_fn {
return [v = (V&&) v]<class Out>(Out out) mutable PUSHMI_VOID_LAMBDA_REQUIRES(Receiver<Out>) {
::pushmi::set_value(out, (V&&) v);
};
}
}
};
struct set_error_fn {
......@@ -238,7 +165,7 @@ struct set_starting_fn {
}
};
struct do_submit_fn {
struct submit_fn {
template <class Out>
auto operator()(Out out) const {
static_assert(Receiver<Out>, "'Out' must be a model of Receiver");
......@@ -255,7 +182,7 @@ struct do_submit_fn {
}
};
struct get_now_fn {
struct now_fn {
auto operator()() const {
return []<class In>(In in) PUSHMI_T_LAMBDA_REQUIRES(decltype(::pushmi::now(in)), TimeSender<In>) {
return ::pushmi::now(in);
......@@ -270,9 +197,9 @@ inline constexpr detail::set_error_fn set_error{};
inline constexpr detail::set_value_fn set_value{};
inline constexpr detail::set_stopping_fn set_stopping{};
inline constexpr detail::set_starting_fn set_starting{};
inline constexpr detail::do_submit_fn submit{};
inline constexpr detail::get_now_fn now{};
inline constexpr detail::get_now_fn top{};
inline constexpr detail::submit_fn submit{};
inline constexpr detail::now_fn now{};
inline constexpr detail::now_fn top{};
} // namespace extension_operators
......
......@@ -22,7 +22,7 @@ struct on_fn {
template <class ExecutorFactory>
auto on_fn::operator()(ExecutorFactory ef) const {
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),
::pushmi::detail::submit_transform_out<In>(
[ef]<class Out>(In& in, Out out) {
......
......@@ -18,20 +18,11 @@ namespace operators {
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>
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>
concept bool AutoSenderTo = SenderTo<In, receiver_type_t<In, AN...>>;
......@@ -49,13 +40,13 @@ private:
std::tuple<AN...> args_;
template <AutoSenderTo<AN...> 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));
return in;
}
template <AutoTimeSenderTo<AN...> 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));
return in;
}
......@@ -75,7 +66,7 @@ private:
std::tuple<AN...> args_;
template <AutoTimeSenderTo<AN...> 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));
return in;
}
......@@ -98,7 +89,7 @@ private:
// TODO - only move, move-only types..
// if out can be copied, then submit can be called multiple
// 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_);
::pushmi::submit(in, std::move(at), std::move(out));
return in;
......@@ -124,7 +115,7 @@ private:
In impl_(In in) {
bool done = false;
std::condition_variable signaled;
auto out{::pushmi::detail::out_from<In>(
auto out{::pushmi::detail::out_from_fn<In>()(
std::move(args_),
on_value{[&]<class Out, class V>(Out out, V&& v){
if constexpr ((bool)TimeSender<std::remove_cvref_t<V>>) {
......@@ -150,7 +141,7 @@ private:
signaled.notify_all();
}}
)};
if constexpr ((bool)IsTimeSender) {
if constexpr (IsTimeSender) {
::pushmi::submit(in, ::pushmi::now(in), std::move(out));
} else {
::pushmi::submit(in, std::move(out));
......
......@@ -15,23 +15,26 @@ namespace pushmi {
namespace operators {
namespace detail {
template <class SideEffects, class Out>
template <Receiver SideEffects, Receiver Out>
struct tap_ {
SideEffects sideEffects;
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>
requires SingleReceiver<SideEffects, const V&>&& SingleReceiver<Out, V>
template <class V, class UV = std::remove_reference_t<V>>
requires SingleReceiver<SideEffects, const UV&> && SingleReceiver<Out, 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);
}
template <class E>
requires NoneReceiver<SideEffects, E>&& NoneReceiver<Out, E>
requires NoneReceiver<SideEffects, const E&> && NoneReceiver<Out, E>
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));
}
void done() {
......@@ -41,29 +44,23 @@ struct tap_ {
};
template <Receiver SideEffects, Receiver Out>
tap_(SideEffects, Out)->tap_<SideEffects, Out>;
tap_(SideEffects, Out) -> tap_<SideEffects, Out>;
struct tap_fn {
template <class... AN>
auto operator()(AN... an) const;
template <class... AN>
auto operator()(AN... an) const;
};
template <class... AN>
auto tap_fn::operator()(AN... an) const {
auto args = std::tuple{std::move(an)...};
return [args = std::move(args)]<class In>(In in) mutable {
auto sideEffects{::pushmi::detail::out_from<In>(std::move(args))};
return [args = std::tuple{std::move(an)...}]<class In>(In in) mutable {
auto sideEffects{::pushmi::detail::out_from_fn<In>()(std::move(args))};
using SideEffects = decltype(sideEffects);
static_assert(
::pushmi::detail::deferred_requires_from<In, SideEffects,
Receiver<SideEffects> &&
SenderTo<In, SideEffects>,
Receiver<SideEffects> &&
SenderTo<In, SideEffects, none_tag>,
SenderTo<In, SideEffects, single_tag>,
Receiver<SideEffects> &&
TimeSenderTo<In, SideEffects, single_tag> >(),
"'In' is not deliverable to 'SideEffects'");
......@@ -73,26 +70,17 @@ auto tap_fn::operator()(AN... an) const {
[sideEffects = std::move(sideEffects)]<class Out>(Out out) {
static_assert(
::pushmi::detail::deferred_requires_from<In, SideEffects,
Receiver<Out> &&
SenderTo<In, Out>,
Receiver<Out> &&
SenderTo<In, Out, none_tag>,
SenderTo<In, Out, single_tag>,
Receiver<Out> &&
TimeSenderTo<In, Out, single_tag> >(),
"'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);
static_assert(
::pushmi::detail::deferred_requires_from<In, SideEffects,
Receiver<Gang> &&
SenderTo<In, Gang>,
Receiver<Gang> &&
SenderTo<In, Gang, single_tag>,
Receiver<Gang> &&
TimeSenderTo<In, Gang, single_tag> >(),
"'In' is not deliverable to 'Out' & 'SideEffects'");
return gang;
......
......@@ -17,19 +17,20 @@ namespace operators {
namespace detail {
struct transform_fn {
template <class... FN>
auto operator()(FN... fn) const;
template <class... FN>
auto operator()(FN... fn) const;
};
template <class... FN>
auto transform_fn::operator()(FN... fn) const {
auto f = overload{std::move(fn)...};
return [f = std::move(f)]<class In>(In 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),
::pushmi::detail::submit_transform_out<In>(
[f]<class Out>(Out out) {
return ::pushmi::detail::out_from<In>(
return ::pushmi::detail::out_from_fn<In>()(
std::move(out),
// copy 'f' to allow multiple calls to submit
on_value{
......
......@@ -15,7 +15,7 @@ namespace operators {
namespace detail {
class via_fn {
template <class ExecutorFactory>
template <Invocable ExecutorFactory>
auto operator()(ExecutorFactory ef) const;
};
......@@ -30,15 +30,15 @@ struct via_fn_data : public Out {
template<class Executor, class Out>
via_fn_data(Out, Executor) -> via_fn_data<Executor, Out>;
template <class ExecutorFactory>
template <Invocable ExecutorFactory>
auto via_fn::operator()(ExecutorFactory ef) const {
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),
::pushmi::detail::submit_transform_out<In>(
[ef]<class Out>(Out out) {
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)},
// copy 'f' to allow multiple calls to submit
::pushmi::on_value{[]<class V>(auto& data, V&& v){
......
......@@ -4,15 +4,17 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
template <class In, class Operator>
auto operator|(In&& in, Operator op) -> decltype(op(std::forward<In>(in))) {
return op(std::forward<In>(in));
#include "traits.h"
template <class In, pushmi::Invocable<In> Op>
decltype(auto) operator|(In&& in, Op op) {
return op((In&&) in);
}
namespace pushmi {
template<class T, class... FN>
auto pipe(T t, FN... fn) {
auto pipe(T t, FN... fn) -> decltype((t | ... | fn)) {
return (t | ... | fn);
}
......
......@@ -4,6 +4,7 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
#include <functional>
#include <type_traits>
namespace pushmi {
......@@ -101,12 +102,11 @@ using requires_ = std::enable_if_t<B, T>;
} // namespace pushmi
#if 1
#define PUSHMI_VOID_LAMBDA_REQUIRES(...) \
->::pushmi::detail::requires_<(__VA_ARGS__)>
#define PUSHMI_VOID_LAMBDA_REQUIRES(RequiresExp...) \
->::pushmi::detail::requires_<(RequiresExp)>
#define PUSHMI_T_LAMBDA_REQUIRES(T, RequiresExp...) \
->::pushmi::detail::requires_<(RequiresExp), T>
#define PUSHMI_T_LAMBDA_REQUIRES(T, ...) \
->::pushmi::detail::requires_<(__VA_ARGS__), T>
#elif 0
// 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