Commit ec7519ab authored by Kirk Shoop's avatar Kirk Shoop Committed by Facebook Github Bot

improve pushmi compile-time debugging

Summary:
use PUSHMI_TEMPLATE_DEBUG in a few more places
add some additional code to the submit customization point to allow the compiler to emit error info when none of the fallbacks worked.

Reviewed By: yfeldblum

Differential Revision: D15016463

fbshipit-source-id: 1c43c21093d54d662c1d084040608db0e0c029be
parent c43e3f85
...@@ -168,7 +168,7 @@ inline void set_value(std::promise<void>& p) // ...@@ -168,7 +168,7 @@ inline void set_value(std::promise<void>& p) //
// //
struct set_done_fn { struct set_done_fn {
PUSHMI_TEMPLATE(class S) PUSHMI_TEMPLATE_DEBUG(class S)
(requires // (requires //
requires( requires(
set_done(std::declval<S&>()), set_done(std::declval<S&>()),
...@@ -186,7 +186,7 @@ struct set_done_fn { ...@@ -186,7 +186,7 @@ struct set_done_fn {
} }
}; };
struct set_error_fn { struct set_error_fn {
PUSHMI_TEMPLATE(class S, class E) PUSHMI_TEMPLATE_DEBUG(class S, class E)
(requires // (requires //
requires(set_error(std::declval<S&>(), std::declval<E>()))) // requires(set_error(std::declval<S&>(), std::declval<E>()))) //
void operator()(S& s, E&& e) const // void operator()(S& s, E&& e) const //
...@@ -195,7 +195,7 @@ struct set_error_fn { ...@@ -195,7 +195,7 @@ struct set_error_fn {
} }
}; };
struct set_value_fn { struct set_value_fn {
PUSHMI_TEMPLATE(class S, class... VN) PUSHMI_TEMPLATE_DEBUG(class S, class... VN)
(requires // (requires //
requires( requires(
set_value(std::declval<S&>(), std::declval<VN>()...), set_value(std::declval<S&>(), std::declval<VN>()...),
...@@ -214,7 +214,7 @@ struct set_value_fn { ...@@ -214,7 +214,7 @@ struct set_value_fn {
}; };
struct set_starting_fn { struct set_starting_fn {
PUSHMI_TEMPLATE(class S, class Up) PUSHMI_TEMPLATE_DEBUG(class S, class Up)
(requires // (requires //
requires( requires(
set_starting(std::declval<S&>(), std::declval<Up>()), set_starting(std::declval<S&>(), std::declval<Up>()),
......
...@@ -50,6 +50,7 @@ class any_flow_sender ...@@ -50,6 +50,7 @@ class any_flow_sender
vtable const* vptr_ = &noop_; vtable const* vptr_ = &noop_;
template <class Wrapped> template <class Wrapped>
any_flow_sender(Wrapped obj, std::false_type) : any_flow_sender() { any_flow_sender(Wrapped obj, std::false_type) : any_flow_sender() {
static_assert(FlowSenderTo<wrapped_t<Wrapped>&, any_flow_receiver<PE, PV, E, VN...>>, "Wrapped object does not support w.submit(any_flow_receiver) - perhaps std::move(w).submit(any_flow_receiver) is supported");
struct s { struct s {
static void op(data& src, data* dst) { static void op(data& src, data* dst) {
if (dst) if (dst)
...@@ -66,8 +67,8 @@ class any_flow_sender ...@@ -66,8 +67,8 @@ class any_flow_sender
vptr_ = &vtbl; vptr_ = &vtbl;
} }
template <class Wrapped> template <class Wrapped>
any_flow_sender(Wrapped obj, std::true_type) noexcept any_flow_sender(Wrapped obj, std::true_type) noexcept : any_flow_sender() {
: any_flow_sender() { static_assert(FlowSenderTo<wrapped_t<Wrapped>&, any_flow_receiver<PE, PV, E, VN...>>, "Wrapped object does not support w.submit(any_flow_receiver) - perhaps std::move(w).submit(any_flow_receiver) is supported");
struct s { struct s {
static void op(data& src, data* dst) { static void op(data& src, data* dst) {
if (dst) if (dst)
...@@ -106,8 +107,8 @@ class any_flow_sender ...@@ -106,8 +107,8 @@ class any_flow_sender
new ((void*)this) any_flow_sender(std::move(that)); new ((void*)this) any_flow_sender(std::move(that));
return *this; return *this;
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) // (requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...> && Constructible<any_flow_receiver<PE, PV, E, VN...>, Out>) //
void submit(Out&& out) { void submit(Out&& out) {
vptr_->submit_(data_, any_flow_receiver<PE, PV, E, VN...>{(Out &&) out}); vptr_->submit_(data_, any_flow_receiver<PE, PV, E, VN...>{(Out &&) out});
} }
...@@ -129,13 +130,13 @@ class flow_sender<SF> { ...@@ -129,13 +130,13 @@ class flow_sender<SF> {
constexpr explicit flow_sender(SF sf) constexpr explicit flow_sender(SF sf)
: sf_(std::move(sf)) {} : sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires FlowReceiver<Out> && Invocable<SF&, Out>) (requires FlowReceiver<Out> && Invocable<SF&, Out>)
void submit(Out out) & { void submit(Out out) & {
sf_(std::move(out)); sf_(std::move(out));
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires FlowReceiver<Out> && Invocable<SF&&, Out>) (requires FlowReceiver<Out> && Invocable<SF&&, Out>)
void submit(Out out) && { void submit(Out out) && {
std::move(sf_)(std::move(out)); std::move(sf_)(std::move(out));
...@@ -162,12 +163,12 @@ class flow_sender<Data, DSF> { ...@@ -162,12 +163,12 @@ class flow_sender<Data, DSF> {
constexpr flow_sender(Data data, DSF sf) constexpr flow_sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {} : data_(std::move(data)), sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires FlowReceiver<Out> && Invocable<DSF&, Data&, Out>) (requires FlowReceiver<Out> && Invocable<DSF&, Data&, Out>)
void submit(Out out) & { void submit(Out out) & {
sf_(data_, std::move(out)); sf_(data_, std::move(out));
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires FlowReceiver<Out> && Invocable<DSF&&, Data&&, Out>) (requires FlowReceiver<Out> && Invocable<DSF&&, Data&&, Out>)
void submit(Out out) && { void submit(Out out) && {
std::move(sf_)(std::move(data_), std::move(out)); std::move(sf_)(std::move(data_), std::move(out));
......
...@@ -53,6 +53,7 @@ class any_flow_single_sender ...@@ -53,6 +53,7 @@ class any_flow_single_sender
template <class Wrapped> template <class Wrapped>
any_flow_single_sender(Wrapped obj, std::false_type) any_flow_single_sender(Wrapped obj, std::false_type)
: any_flow_single_sender() { : any_flow_single_sender() {
static_assert(FlowSenderTo<wrapped_t<Wrapped>&, any_flow_receiver<PE, std::ptrdiff_t, E, VN...>>, "Wrapped object does not support w.submit(any_flow_receiver) - perhaps std::move(w).submit(any_flow_receiver) is supported");
struct s { struct s {
static void op(data& src, data* dst) { static void op(data& src, data* dst) {
if (dst) if (dst)
...@@ -73,6 +74,7 @@ class any_flow_single_sender ...@@ -73,6 +74,7 @@ class any_flow_single_sender
template <class Wrapped> template <class Wrapped>
any_flow_single_sender(Wrapped obj, std::true_type) noexcept any_flow_single_sender(Wrapped obj, std::true_type) noexcept
: any_flow_single_sender() { : any_flow_single_sender() {
static_assert(FlowSenderTo<wrapped_t<Wrapped>&, any_flow_receiver<PE, std::ptrdiff_t, E, VN...>>, "Wrapped object does not support w.submit(any_flow_receiver) - perhaps std::move(w).submit(any_flow_receiver) is supported");
struct s { struct s {
static void op(data& src, data* dst) { static void op(data& src, data* dst) {
if (dst) if (dst)
...@@ -114,8 +116,8 @@ class any_flow_single_sender ...@@ -114,8 +116,8 @@ class any_flow_single_sender
new ((void*)this) any_flow_single_sender(std::move(that)); new ((void*)this) any_flow_single_sender(std::move(that));
return *this; return *this;
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) // (requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...> && Constructible<any_flow_receiver<PE, std::ptrdiff_t, E, VN...>, Out>) //
void submit(Out&& out) { void submit(Out&& out) {
vptr_->submit_( vptr_->submit_(
data_, data_,
...@@ -139,13 +141,13 @@ class flow_single_sender<SF> { ...@@ -139,13 +141,13 @@ class flow_single_sender<SF> {
constexpr explicit flow_single_sender(SF sf) constexpr explicit flow_single_sender(SF sf)
: sf_(std::move(sf)) {} : sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<SF&, Out>) (requires Receiver<Out> && Invocable<SF&, Out>)
void submit(Out out) & { void submit(Out out) & {
sf_(std::move(out)); sf_(std::move(out));
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<SF&&, Out>) (requires Receiver<Out> && Invocable<SF&&, Out>)
void submit(Out out) && { void submit(Out out) && {
std::move(sf_)(std::move(out)); std::move(sf_)(std::move(out));
...@@ -176,13 +178,13 @@ class flow_single_sender<Data, DSF> { ...@@ -176,13 +178,13 @@ class flow_single_sender<Data, DSF> {
constexpr flow_single_sender(Data data, DSF sf) constexpr flow_single_sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {} : data_(std::move(data)), sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<DSF&, Data&, Out>) (requires Receiver<Out> && Invocable<DSF&, Data&, Out>)
void submit(Out out) & { void submit(Out out) & {
sf_(data_, std::move(out)); sf_(data_, std::move(out));
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<DSF&&, Data&&, Out>) (requires Receiver<Out> && Invocable<DSF&&, Data&&, Out>)
void submit(Out out) && { void submit(Out out) && {
std::move(sf_)(std::move(data_), std::move(out)); std::move(sf_)(std::move(data_), std::move(out));
......
...@@ -32,8 +32,9 @@ namespace folly { ...@@ -32,8 +32,9 @@ namespace folly {
namespace pushmi { namespace pushmi {
namespace _submit_adl { namespace _submit_adl {
struct submit_adl_disabled;
template <class S, class R> template <class S, class R>
void submit(S&&, R&&) = delete; submit_adl_disabled submit(S&&, R&&);
template <class T> template <class T>
constexpr typename T::value_type const _v = T::value; constexpr typename T::value_type const _v = T::value;
...@@ -77,35 +78,57 @@ namespace _submit_adl { ...@@ -77,35 +78,57 @@ namespace _submit_adl {
#endif #endif
struct _impl_ { struct _impl_ {
PUSHMI_TEMPLATE_DEBUG(class S, class R) // these pull the line numbers for the different invocations
// out of the IF_CONSTEXPR macros
PUSHMI_TEMPLATE(class S, class R)
(requires Sender<S> && Receiver<R>) (requires Sender<S> && Receiver<R>)
auto operator()(S&& from, R&& to) const auto adl_submit(S&& from, R&& to) const {
{ return submit((S&&) from, (R&&) to);
}
PUSHMI_TEMPLATE(class S, class R)
(requires Sender<S> && Receiver<R>)
auto member_submit(S&& from, R&& to) const {
return ((S&&) from).submit((R&&) to);
}
PUSHMI_TEMPLATE(class S, class R)
(requires Sender<S> && Receiver<R>)
void debug_submit(S&& from, R&& to) const {
// try to get a useful compile error
PUSHMI_IF_CONSTEXPR((
Same<decltype(submit((S&&) from, (R&&) to)),
submit_adl_disabled>) (
static_assert(std::is_void<decltype(member_submit(id((S&&) from), (R&&) to))>::value, "s.submit(r) failed and adl was disabled");
) else (
static_assert(std::is_void<decltype(adl_submit(id((S&&) from), (R&&) to))>::value, "submit(s, r) via ADL failed");
))
}
PUSHMI_TEMPLATE(class S, class R)
(requires Sender<S> && Receiver<R>)
auto operator()(S&& from, R&& to) const {
// Prefer a .submit() member if it exists: // Prefer a .submit() member if it exists:
PUSHMI_IF_CONSTEXPR_RETURN((HasMemberSubmit_<S, R>) ( PUSHMI_IF_CONSTEXPR_RETURN((HasMemberSubmit_<S, R>) (
id((S&&) from).submit((R&&) to); member_submit(id((S&&) from), (R&&) to);
return std::true_type{}; return std::true_type{};
) else ( ) else (
// Otherwise, dispatch to a submit() free function if it exists: // Otherwise, dispatch to a submit() free function if it exists:
PUSHMI_IF_CONSTEXPR_RETURN((HasNonMemberSubmit_<S, R>) ( PUSHMI_IF_CONSTEXPR_RETURN((HasNonMemberSubmit_<S, R>) (
submit(id((S&&) from), (R&&) to); adl_submit(id((S&&) from), (R&&) to);
return std::true_type{}; return std::true_type{};
) else ( ) else (
#if FOLLY_HAS_COROUTINES #if FOLLY_HAS_COROUTINES
// Otherwise, if we support coroutines and S looks like an // Otherwise, if we support coroutines and S looks like an
// awaitable, dispatch to the // awaitable, dispatch to the coroutine
PUSHMI_IF_CONSTEXPR_RETURN((IsSubmittableAwaitable_<S, R>) ( PUSHMI_IF_CONSTEXPR_RETURN((IsSubmittableAwaitable_<S, R>) (
_submit_awaitable_(id((S&&) from), (R&&) to); _submit_awaitable_(id((S&&) from), (R&&) to);
return std::true_type{}; return std::true_type{};
) else ( ) else (
debug_submit(id((S&&) from), (R&&) to);
return std::false_type{}; return std::false_type{};
)) ))
#else #else
debug_submit(id((S&&) from), (R&&) to);
// does not work for free functions
id((S&&) from).submit((R&&) to);
return std::false_type{}; return std::false_type{};
#endif #endif
)) ))
)) ))
......
...@@ -50,6 +50,7 @@ class any_sender ...@@ -50,6 +50,7 @@ class any_sender
vtable const* vptr_ = &noop_; vtable const* vptr_ = &noop_;
template <class Wrapped> template <class Wrapped>
any_sender(Wrapped obj, std::false_type) : any_sender() { any_sender(Wrapped obj, std::false_type) : any_sender() {
static_assert(SenderTo<wrapped_t<Wrapped>&, any_receiver<E, VN...>>, "Wrapped object does not support w.submit(any_receiver) - perhaps std::move(w).submit(any_receiver) is supported");
struct s { struct s {
static void op(data& src, data* dst) { static void op(data& src, data* dst) {
if (dst) if (dst)
...@@ -67,6 +68,7 @@ class any_sender ...@@ -67,6 +68,7 @@ class any_sender
} }
template <class Wrapped> template <class Wrapped>
any_sender(Wrapped obj, std::true_type) noexcept : any_sender() { any_sender(Wrapped obj, std::true_type) noexcept : any_sender() {
static_assert(SenderTo<wrapped_t<Wrapped>&, any_receiver<E, VN...>>, "Wrapped object does not support w.submit(any_receiver) - perhaps std::move(w).submit(any_receiver) is supported");
struct s { struct s {
static void op(data& src, data* dst) { static void op(data& src, data* dst) {
if (dst) if (dst)
...@@ -95,7 +97,7 @@ class any_sender ...@@ -95,7 +97,7 @@ class any_sender
} }
PUSHMI_TEMPLATE(class Wrapped) PUSHMI_TEMPLATE(class Wrapped)
(requires SenderTo<wrapped_t<Wrapped>, any_receiver<E, VN...>>) // (requires Sender<wrapped_t<Wrapped>>) //
explicit any_sender(Wrapped obj) noexcept(insitu<Wrapped>()) explicit any_sender(Wrapped obj) noexcept(insitu<Wrapped>())
: any_sender{std::move(obj), bool_<insitu<Wrapped>()>{}} {} : any_sender{std::move(obj), bool_<insitu<Wrapped>()>{}} {}
~any_sender() { ~any_sender() {
...@@ -106,8 +108,8 @@ class any_sender ...@@ -106,8 +108,8 @@ class any_sender
new ((void*)this) any_sender(std::move(that)); new ((void*)this) any_sender(std::move(that));
return *this; return *this;
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) // (requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...> && Constructible<any_receiver<E, VN...>, Out>) //
void submit(Out&& out) { void submit(Out&& out) {
vptr_->submit_(data_, any_receiver<E, VN...>{(Out &&) out}); vptr_->submit_(data_, any_receiver<E, VN...>{(Out &&) out});
} }
...@@ -126,14 +128,14 @@ class sender<SF> : public sender_tag { ...@@ -126,14 +128,14 @@ class sender<SF> : public sender_tag {
constexpr sender() = default; constexpr sender() = default;
constexpr explicit sender(SF sf) : sf_(std::move(sf)) {} constexpr explicit sender(SF sf) : sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires PUSHMI_EXP( (requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<SF&, Out>)) // lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<SF&, Out>)) //
void submit(Out out) & { void submit(Out out) & {
sf_(std::move(out)); sf_(std::move(out));
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires PUSHMI_EXP( (requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<SF&&, Out>)) // lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<SF&&, Out>)) //
void submit(Out out) && { void submit(Out out) && {
...@@ -159,13 +161,13 @@ class sender<Data, DSF> { ...@@ -159,13 +161,13 @@ class sender<Data, DSF> {
constexpr sender(Data data, DSF sf) constexpr sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {} : data_(std::move(data)), sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires PUSHMI_EXP( (requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&, Data&, Out>)) // lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&, Data&, Out>)) //
void submit(Out out) & { void submit(Out out) & {
sf_(data_, std::move(out)); sf_(data_, std::move(out));
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires PUSHMI_EXP( (requires PUSHMI_EXP(
lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&&, Data&&, Out>)) // lazy::Receiver<Out> PUSHMI_AND lazy::Invocable<DSF&&, Data&&, Out>)) //
void submit(Out out) && { void submit(Out out) && {
......
...@@ -101,8 +101,7 @@ class any_single_sender ...@@ -101,8 +101,7 @@ class any_single_sender
static const vtable vtbl{s::op, s::submit}; static const vtable vtbl{s::op, s::submit};
new (&data_.buffer_) Wrapped(std::move(obj)); new (&data_.buffer_) Wrapped(std::move(obj));
vptr_ = &vtbl; vptr_ = &vtbl;
} } template <class Wrapped>
template <class Wrapped>
any_single_sender(Wrapped obj, std::false_type, std::true_type) noexcept any_single_sender(Wrapped obj, std::false_type, std::true_type) noexcept
: any_single_sender() { : any_single_sender() {
struct s { struct s {
...@@ -144,8 +143,8 @@ class any_single_sender ...@@ -144,8 +143,8 @@ class any_single_sender
new ((void*)this) any_single_sender(std::move(that)); new ((void*)this) any_single_sender(std::move(that));
return *this; return *this;
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...>) // (requires ReceiveError<Out, E>&& ReceiveValue<Out, VN...> && Constructible<any_receiver<E, VN...>, Out>) //
void submit(Out&& out) { void submit(Out&& out) {
vptr_->submit_(data_, any_receiver<E, VN...>{(Out &&) out}); vptr_->submit_(data_, any_receiver<E, VN...>{(Out &&) out});
} }
...@@ -166,13 +165,13 @@ class single_sender<SF> { ...@@ -166,13 +165,13 @@ class single_sender<SF> {
constexpr single_sender() = default; constexpr single_sender() = default;
constexpr explicit single_sender(SF sf) : sf_(std::move(sf)) {} constexpr explicit single_sender(SF sf) : sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<SF&, Out>) // (requires Receiver<Out> && Invocable<SF&, Out>) //
void submit(Out&& out) & { void submit(Out&& out) & {
sf_((Out&&)out); sf_((Out&&)out);
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<SF&&, Out>) // (requires Receiver<Out> && Invocable<SF&&, Out>) //
void submit(Out&& out) && { void submit(Out&& out) && {
std::move(sf_)((Out&&)out); std::move(sf_)((Out&&)out);
...@@ -198,12 +197,12 @@ class single_sender<Data, DSF> { ...@@ -198,12 +197,12 @@ class single_sender<Data, DSF> {
constexpr single_sender(Data data, DSF sf) constexpr single_sender(Data data, DSF sf)
: data_(std::move(data)), sf_(std::move(sf)) {} : data_(std::move(data)), sf_(std::move(sf)) {}
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<DSF&, Data&, Out>) // (requires Receiver<Out> && Invocable<DSF&, Data&, Out>) //
void submit(Out&& out) & { void submit(Out&& out) & {
sf_(data_, (Out&&)out); sf_(data_, (Out&&)out);
} }
PUSHMI_TEMPLATE(class Out) PUSHMI_TEMPLATE_DEBUG(class Out)
(requires Receiver<Out> && Invocable<DSF&&, Data&&, Out>) // (requires Receiver<Out> && Invocable<DSF&&, Data&&, Out>) //
void submit(Out&& out) && { void submit(Out&& out) && {
std::move(sf_)(std::move(data_), (Out&&)out); std::move(sf_)(std::move(data_), (Out&&)out);
......
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