732 lines
25 KiB
C++
732 lines
25 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
|
|
* (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
*
|
|
* https://llvm.org/LICENSE.txt
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <unifex/async_trace.hpp>
|
|
#include <unifex/manual_lifetime.hpp>
|
|
#include <unifex/manual_lifetime_union.hpp>
|
|
#include <unifex/receiver_concepts.hpp>
|
|
#include <unifex/scope_guard.hpp>
|
|
#include <unifex/sender_concepts.hpp>
|
|
#include <unifex/type_traits.hpp>
|
|
#include <unifex/type_list.hpp>
|
|
#include <unifex/std_concepts.hpp>
|
|
#include <unifex/bind_back.hpp>
|
|
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <unifex/detail/prologue.hpp>
|
|
|
|
namespace unifex
|
|
{
|
|
namespace _final
|
|
{
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver>
|
|
struct _op {
|
|
class type;
|
|
};
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver>
|
|
using operation =
|
|
typename _op<SourceSender, CompletionSender, remove_cvref_t<Receiver>>::type;
|
|
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver,
|
|
typename... Values>
|
|
struct _value_receiver {
|
|
class type;
|
|
};
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver,
|
|
typename... Values>
|
|
using value_receiver =
|
|
typename _value_receiver<
|
|
SourceSender,
|
|
CompletionSender,
|
|
Receiver,
|
|
std::decay_t<Values>...>::type;
|
|
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver,
|
|
typename... Values>
|
|
class _value_receiver<SourceSender, CompletionSender, Receiver, Values...>::type final {
|
|
using value_receiver = type;
|
|
using operation_type = operation<SourceSender, CompletionSender, Receiver>;
|
|
|
|
public:
|
|
explicit type(operation_type* op) noexcept : op_(op) {}
|
|
|
|
type(type&& other) noexcept
|
|
: op_(std::exchange(other.op_, nullptr)) {}
|
|
|
|
void set_value() && noexcept {
|
|
auto* const op = op_;
|
|
|
|
using completion_value_op_t = connect_result_t<CompletionSender, value_receiver>;
|
|
unifex::deactivate_union_member<completion_value_op_t>(op->completionValueOp_);
|
|
|
|
UNIFEX_TRY {
|
|
// Move the stored values onto the stack so that we can
|
|
// destroy the ones stored in the operation-state. This
|
|
// prevents the need to add a big switch to the operation
|
|
// state destructor to determine which value-tuple type
|
|
// destructor needs to be run.
|
|
auto values = [op]() -> std::tuple<Values...> {
|
|
scope_guard g{[op]() noexcept {
|
|
unifex::deactivate_union_member<std::tuple<Values...>>(op->value_);
|
|
}};
|
|
return static_cast<std::tuple<Values...>&&>(
|
|
op->value_.template get<std::tuple<Values...>>());
|
|
}
|
|
();
|
|
|
|
std::apply(
|
|
[&](Values&&... values) {
|
|
unifex::set_value(
|
|
static_cast<Receiver&&>(op->receiver_),
|
|
static_cast<Values&&>(values)...);
|
|
},
|
|
static_cast<std::tuple<Values...>&&>(values));
|
|
|
|
} UNIFEX_CATCH (...) {
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_), std::current_exception());
|
|
}
|
|
}
|
|
|
|
template <typename Error>
|
|
void set_error(Error&& error) && noexcept {
|
|
auto* const op = op_;
|
|
|
|
using completion_value_op_t = connect_result_t<CompletionSender, value_receiver>;
|
|
unifex::deactivate_union_member<completion_value_op_t>(op->completionValueOp_);
|
|
|
|
// Discard the stored value.
|
|
unifex::deactivate_union_member<std::tuple<Values...>>(op->value_);
|
|
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_),
|
|
static_cast<Error&&>(error));
|
|
}
|
|
|
|
void set_done() && noexcept {
|
|
auto* const op = op_;
|
|
|
|
using completion_value_op_t = connect_result_t<CompletionSender, value_receiver>;
|
|
unifex::deactivate_union_member<completion_value_op_t>(op->completionValueOp_);
|
|
|
|
// Discard the stored value.
|
|
unifex::deactivate_union_member<std::tuple<Values...>>(op->value_);
|
|
|
|
unifex::set_done(static_cast<Receiver&&>(op->receiver_));
|
|
}
|
|
|
|
template(typename CPO, typename R)
|
|
(requires is_receiver_query_cpo_v<CPO> AND
|
|
same_as<R, value_receiver> AND
|
|
is_callable_v<CPO, const Receiver&>)
|
|
friend auto tag_invoke(
|
|
CPO cpo,
|
|
const R& r) noexcept(is_nothrow_callable_v<
|
|
CPO,
|
|
const Receiver&>)
|
|
-> callable_result_t<CPO, const Receiver&> {
|
|
return static_cast<CPO&&>(cpo)(r.get_receiver());
|
|
}
|
|
|
|
template <typename Func>
|
|
friend void tag_invoke(
|
|
tag_t<visit_continuations>,
|
|
const value_receiver& r,
|
|
Func&& func) {
|
|
std::invoke(func, r.get_receiver());
|
|
}
|
|
|
|
private:
|
|
const Receiver& get_receiver() const noexcept {
|
|
return op_->receiver_;
|
|
}
|
|
|
|
operation_type* op_;
|
|
};
|
|
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver,
|
|
typename Error>
|
|
struct _error_receiver {
|
|
class type;
|
|
};
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver,
|
|
typename Error>
|
|
using error_receiver =
|
|
typename _error_receiver<
|
|
SourceSender,
|
|
CompletionSender,
|
|
Receiver,
|
|
std::decay_t<Error>>::type;
|
|
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver,
|
|
typename Error>
|
|
class _error_receiver<SourceSender, CompletionSender, Receiver, Error>::type final {
|
|
using error_receiver = type;
|
|
using operation_type = operation<SourceSender, CompletionSender, Receiver>;
|
|
|
|
public:
|
|
explicit type(operation_type* op) noexcept : op_(op) {}
|
|
|
|
type(type&& other) noexcept
|
|
: op_(std::exchange(other.op_, nullptr)) {}
|
|
|
|
void set_value() && noexcept {
|
|
auto* const op = op_;
|
|
|
|
using completion_error_op_t = connect_result_t<CompletionSender, error_receiver>;
|
|
unifex::deactivate_union_member<completion_error_op_t>(op->completionErrorOp_);
|
|
|
|
Error errorCopy = static_cast<Error&&>(op->error_.template get<Error>());
|
|
unifex::deactivate_union_member<Error>(op->error_);
|
|
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_),
|
|
static_cast<Error&&>(errorCopy));
|
|
}
|
|
|
|
template(typename OtherError)
|
|
(requires receiver<Receiver, OtherError>)
|
|
void set_error(OtherError otherError) && noexcept {
|
|
auto* const op = op_;
|
|
|
|
using completion_error_op_t = connect_result_t<CompletionSender, error_receiver>;
|
|
unifex::deactivate_union_member<completion_error_op_t>(op->completionErrorOp_);
|
|
|
|
// Discard existing stored error from source-sender.
|
|
unifex::deactivate_union_member<Error>(op->error_);
|
|
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_),
|
|
static_cast<OtherError&&>(otherError));
|
|
}
|
|
|
|
void set_done() && noexcept {
|
|
auto* const op = op_;
|
|
|
|
using completion_error_op_t = connect_result_t<CompletionSender, error_receiver>;
|
|
unifex::deactivate_union_member<completion_error_op_t>(op->completionErrorOp_);
|
|
|
|
// Discard existing stored error from source-sender.
|
|
unifex::deactivate_union_member<Error>(op->error_);
|
|
|
|
unifex::set_done(static_cast<Receiver&&>(op->receiver_));
|
|
}
|
|
|
|
template(typename CPO, typename R)
|
|
(requires is_receiver_query_cpo_v<CPO> AND
|
|
same_as<R, error_receiver> AND
|
|
is_callable_v<CPO, const Receiver&>)
|
|
friend auto tag_invoke(
|
|
CPO cpo,
|
|
const R& r) noexcept(is_nothrow_callable_v<
|
|
CPO,
|
|
const Receiver&>)
|
|
-> callable_result_t<CPO, const Receiver&> {
|
|
return static_cast<CPO&&>(cpo)(r.get_receiver());
|
|
}
|
|
|
|
template <typename Func>
|
|
friend void tag_invoke(
|
|
tag_t<visit_continuations>,
|
|
const error_receiver& r,
|
|
Func&& func) {
|
|
std::invoke(func, r.get_receiver());
|
|
}
|
|
|
|
private:
|
|
const Receiver& get_receiver() const noexcept {
|
|
return op_->receiver_;
|
|
}
|
|
|
|
operation_type* op_;
|
|
};
|
|
|
|
template <typename SourceSender, typename CompletionSender, typename Receiver>
|
|
struct _done_receiver {
|
|
class type;
|
|
};
|
|
template <typename SourceSender, typename CompletionSender, typename Receiver>
|
|
using done_receiver =
|
|
typename _done_receiver<SourceSender, CompletionSender, Receiver>::type;
|
|
|
|
template <typename SourceSender, typename CompletionSender, typename Receiver>
|
|
class _done_receiver<SourceSender, CompletionSender, Receiver>::type final {
|
|
using done_receiver = type;
|
|
using operation_type = operation<SourceSender, CompletionSender, Receiver>;
|
|
|
|
public:
|
|
explicit type(operation_type* op) noexcept : op_(op) {}
|
|
|
|
type(type&& other) noexcept
|
|
: op_(std::exchange(other.op_, nullptr)) {}
|
|
|
|
void set_value() && noexcept {
|
|
auto* const op = op_;
|
|
unifex::deactivate_union_member(op->completionDoneOp_);
|
|
unifex::set_done(static_cast<Receiver&&>(op->receiver_));
|
|
}
|
|
|
|
template(typename Error)
|
|
(requires receiver<Receiver, Error>)
|
|
void set_error(Error&& error) && noexcept {
|
|
auto* const op = op_;
|
|
unifex::deactivate_union_member(op->completionDoneOp_);
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_),
|
|
static_cast<Error&&>(error));
|
|
}
|
|
|
|
void set_done() && noexcept {
|
|
auto* const op = op_;
|
|
unifex::deactivate_union_member(op->completionDoneOp_);
|
|
unifex::set_done(static_cast<Receiver&&>(op->receiver_));
|
|
}
|
|
|
|
template(typename CPO, typename R)
|
|
(requires is_receiver_query_cpo_v<CPO> AND
|
|
same_as<R, done_receiver> AND
|
|
is_callable_v<CPO, const Receiver&>)
|
|
friend auto tag_invoke(
|
|
CPO cpo,
|
|
const R& r) noexcept(is_nothrow_callable_v<
|
|
CPO,
|
|
const Receiver&>)
|
|
-> callable_result_t<CPO, const Receiver&> {
|
|
return static_cast<CPO&&>(cpo)(r.get_receiver());
|
|
}
|
|
|
|
template <typename Func>
|
|
friend void tag_invoke(
|
|
tag_t<visit_continuations>,
|
|
const done_receiver& r,
|
|
Func&& func) {
|
|
std::invoke(func, r.get_receiver());
|
|
}
|
|
|
|
private:
|
|
const Receiver& get_receiver() const noexcept {
|
|
return op_->receiver_;
|
|
}
|
|
|
|
operation_type* op_;
|
|
};
|
|
|
|
template <typename SourceSender, typename CompletionSender, typename Receiver>
|
|
struct _receiver {
|
|
class type;
|
|
};
|
|
template <typename SourceSender, typename CompletionSender, typename Receiver>
|
|
using receiver_t = typename _receiver<SourceSender, CompletionSender, Receiver>::type;
|
|
|
|
template <typename SourceSender, typename CompletionSender, typename Receiver>
|
|
class _receiver<SourceSender, CompletionSender, Receiver>::type final {
|
|
using operation_type = operation<SourceSender, CompletionSender, Receiver>;
|
|
|
|
public:
|
|
explicit type(operation_type* op) noexcept : op_(op) {}
|
|
|
|
type(type&& other) noexcept
|
|
: op_(std::exchange(other.op_, nullptr)) {}
|
|
|
|
template(typename... Values)
|
|
(requires receiver_of<Receiver, Values...>)
|
|
void set_value(Values&&... values) && noexcept {
|
|
auto* const op = op_;
|
|
|
|
UNIFEX_TRY {
|
|
unifex::activate_union_member<std::tuple<std::decay_t<Values>...>>(
|
|
op->value_, static_cast<Values&&>(values)...);
|
|
} UNIFEX_CATCH (...) {
|
|
std::move(*this).set_error(std::current_exception());
|
|
return;
|
|
}
|
|
|
|
unifex::deactivate_union_member(op->sourceOp_);
|
|
|
|
UNIFEX_TRY {
|
|
using value_receiver = value_receiver<
|
|
SourceSender,
|
|
CompletionSender,
|
|
Receiver,
|
|
Values...>;
|
|
using completion_value_op_t =
|
|
connect_result_t<CompletionSender, value_receiver>;
|
|
auto& completionOp =
|
|
unifex::activate_union_member_with<completion_value_op_t>(
|
|
op->completionValueOp_,
|
|
[&] {
|
|
return unifex::connect(
|
|
static_cast<CompletionSender&&>(op->completionSender_),
|
|
value_receiver{op});
|
|
});
|
|
unifex::start(completionOp);
|
|
} UNIFEX_CATCH (...) {
|
|
using decayed_tuple_t = std::tuple<std::decay_t<Values>...>;
|
|
unifex::deactivate_union_member<decayed_tuple_t>(op->value_);
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_), std::current_exception());
|
|
}
|
|
}
|
|
|
|
template <typename Error>
|
|
void set_error(Error&& error) && noexcept {
|
|
static_assert(
|
|
std::is_nothrow_constructible_v<std::decay_t<Error>, Error>);
|
|
|
|
auto* const op = op_;
|
|
unifex::activate_union_member<std::decay_t<Error>>(
|
|
op->error_, static_cast<Error&&>(error));
|
|
|
|
unifex::deactivate_union_member(op->sourceOp_);
|
|
|
|
UNIFEX_TRY {
|
|
using error_receiver_t = error_receiver<
|
|
SourceSender,
|
|
CompletionSender,
|
|
Receiver,
|
|
Error>;
|
|
using completion_error_op_t =
|
|
connect_result_t<CompletionSender, error_receiver_t>;
|
|
auto& completionOp =
|
|
unifex::activate_union_member_with<completion_error_op_t>(
|
|
op->completionErrorOp_,
|
|
[&] {
|
|
return unifex::connect(
|
|
static_cast<CompletionSender&&>(op->completionSender_),
|
|
error_receiver_t{op});
|
|
});
|
|
unifex::start(completionOp);
|
|
} UNIFEX_CATCH (...) {
|
|
unifex::deactivate_union_member<std::decay_t<Error>>(op->error_);
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_), std::current_exception());
|
|
}
|
|
}
|
|
|
|
void set_done() && noexcept {
|
|
auto* const op = op_;
|
|
|
|
unifex::deactivate_union_member(op->sourceOp_);
|
|
|
|
UNIFEX_TRY {
|
|
using done_receiver =
|
|
done_receiver<SourceSender, CompletionSender, Receiver>;
|
|
auto& completionOp = unifex::activate_union_member_with(
|
|
op->completionDoneOp_,
|
|
[&] {
|
|
return unifex::connect(
|
|
static_cast<CompletionSender&&>(op->completionSender_),
|
|
done_receiver{op});
|
|
});
|
|
unifex::start(completionOp);
|
|
} UNIFEX_CATCH (...) {
|
|
unifex::set_error(
|
|
static_cast<Receiver&&>(op->receiver_), std::current_exception());
|
|
}
|
|
}
|
|
|
|
template(typename CPO, typename R)
|
|
(requires is_receiver_query_cpo_v<CPO> AND
|
|
same_as<R, type> AND
|
|
is_callable_v<CPO, const Receiver&>)
|
|
friend auto
|
|
tag_invoke(CPO cpo, const R& r) noexcept(
|
|
is_nothrow_callable_v<CPO, const Receiver&>)
|
|
-> callable_result_t<CPO, const Receiver&> {
|
|
return static_cast<CPO&&>(cpo)(r.get_receiver());
|
|
}
|
|
|
|
template <typename Func>
|
|
friend void tag_invoke(
|
|
tag_t<visit_continuations>, const type& r, Func&& func) {
|
|
std::invoke(func, r.get_receiver());
|
|
}
|
|
|
|
private:
|
|
const Receiver& get_receiver() const noexcept {
|
|
return op_->receiver_;
|
|
}
|
|
|
|
operation_type* op_;
|
|
};
|
|
|
|
template <
|
|
typename SourceSender,
|
|
typename CompletionSender,
|
|
typename Receiver>
|
|
class _op<SourceSender, CompletionSender, Receiver>::type {
|
|
friend receiver_t<SourceSender, CompletionSender, Receiver>;
|
|
friend done_receiver<SourceSender, CompletionSender, Receiver>;
|
|
|
|
template <
|
|
typename SourceSender2,
|
|
typename CompletionSender2,
|
|
typename Receiver2,
|
|
typename... Values>
|
|
friend struct _value_receiver;
|
|
|
|
template <
|
|
typename SourceSender2,
|
|
typename CompletionSender2,
|
|
typename Receiver2,
|
|
typename Error>
|
|
friend struct _error_receiver;
|
|
|
|
template <typename... Values>
|
|
using value_operation = connect_result_t<
|
|
CompletionSender,
|
|
value_receiver<SourceSender, CompletionSender, Receiver, Values...>>;
|
|
|
|
template <typename Error>
|
|
using error_operation = connect_result_t<
|
|
CompletionSender,
|
|
error_receiver<SourceSender, CompletionSender, Receiver, Error>>;
|
|
|
|
template <typename... Errors>
|
|
using error_operation_union =
|
|
manual_lifetime_union<
|
|
error_operation<std::exception_ptr>,
|
|
error_operation<Errors>...>;
|
|
|
|
using done_operation = connect_result_t<
|
|
CompletionSender,
|
|
done_receiver<SourceSender, CompletionSender, Receiver>>;
|
|
|
|
template <typename... Errors>
|
|
using error_result_union =
|
|
manual_lifetime_union<std::exception_ptr, Errors...>;
|
|
|
|
public:
|
|
template <typename CompletionSender2, typename Receiver2>
|
|
explicit type(
|
|
SourceSender&& sourceSender,
|
|
CompletionSender2&& completionSender,
|
|
Receiver2&& r)
|
|
: completionSender_(static_cast<CompletionSender2&&>(completionSender))
|
|
, receiver_(static_cast<Receiver2&&>(r))
|
|
, sourceOp_{} {
|
|
sourceOp_.construct_with([&] {
|
|
return unifex::connect(
|
|
static_cast<SourceSender&&>(sourceSender),
|
|
receiver_t<SourceSender, CompletionSender, Receiver>{this});
|
|
});
|
|
}
|
|
|
|
~type() {
|
|
if (!started_) {
|
|
unifex::deactivate_union_member(sourceOp_);
|
|
}
|
|
}
|
|
|
|
void start() & noexcept {
|
|
UNIFEX_ASSERT(!started_);
|
|
started_ = true;
|
|
unifex::start(sourceOp_.get());
|
|
}
|
|
|
|
private:
|
|
UNIFEX_NO_UNIQUE_ADDRESS remove_cvref_t<CompletionSender> completionSender_;
|
|
UNIFEX_NO_UNIQUE_ADDRESS Receiver receiver_;
|
|
bool started_ = false;
|
|
|
|
// Result storage.
|
|
union {
|
|
// Storage for error-types that might be produced by SourceSender.
|
|
UNIFEX_NO_UNIQUE_ADDRESS
|
|
sender_error_types_t<remove_cvref_t<SourceSender>, error_result_union>
|
|
error_;
|
|
|
|
// Storage for value-types that might be produced by SourceSender.
|
|
UNIFEX_NO_UNIQUE_ADDRESS
|
|
sender_value_types_t<
|
|
remove_cvref_t<SourceSender>,
|
|
manual_lifetime_union,
|
|
decayed_tuple<std::tuple>::template apply>
|
|
value_;
|
|
};
|
|
|
|
using source_operation_t =
|
|
manual_lifetime<connect_result_t<
|
|
SourceSender,
|
|
receiver_t<SourceSender, CompletionSender, Receiver>>>;
|
|
|
|
using completion_value_union_t =
|
|
sender_value_types_t<
|
|
remove_cvref_t<SourceSender>,
|
|
manual_lifetime_union,
|
|
value_operation>;
|
|
|
|
using completion_error_union_t =
|
|
sender_error_types_t<remove_cvref_t<SourceSender>, error_operation_union>;
|
|
|
|
// Operation storage.
|
|
union {
|
|
// Storage for the source operation state.
|
|
source_operation_t sourceOp_;
|
|
|
|
// Storage for the completion operation for the case where
|
|
// the source operation completed with a value.
|
|
completion_value_union_t completionValueOp_;
|
|
|
|
// Storage for the completion operation for the case where the
|
|
// source operation completed with an error.
|
|
completion_error_union_t completionErrorOp_;
|
|
|
|
// Storage for the completion operation for the case where the
|
|
// source operation completed with 'done'.
|
|
manual_lifetime<done_operation> completionDoneOp_;
|
|
};
|
|
};
|
|
|
|
template <typename SourceSender, typename CompletionSender>
|
|
struct _sender {
|
|
class type;
|
|
};
|
|
template <typename SourceSender, typename CompletionSender>
|
|
using sender = typename _sender<
|
|
remove_cvref_t<SourceSender>,
|
|
remove_cvref_t<CompletionSender>>::type;
|
|
|
|
template <typename SourceSender, typename CompletionSender>
|
|
class _sender<SourceSender, CompletionSender>::type {
|
|
using sender = type;
|
|
public:
|
|
template <
|
|
template <typename...> class Variant,
|
|
template <typename...> class Tuple>
|
|
using value_types = typename sender_traits<SourceSender>::
|
|
template value_types<Variant, decayed_tuple<Tuple>::template apply>;
|
|
|
|
// This can produce any of the error_types of SourceSender, or of
|
|
// CompletionSender or an exception_ptr corresponding to an exception thrown
|
|
// by the copy/move of the value result.
|
|
// TODO: In theory we could eliminate exception_ptr in the case that the
|
|
// connect() operation and move/copy of values
|
|
template <template <typename...> class Variant>
|
|
using error_types =
|
|
typename concat_type_lists_unique_t<
|
|
sender_error_types_t<SourceSender, decayed_tuple<type_list>::template apply>,
|
|
sender_error_types_t<CompletionSender, decayed_tuple<type_list>::template apply>,
|
|
type_list<std::exception_ptr>>::template apply<Variant>;
|
|
|
|
static constexpr bool sends_done =
|
|
sender_traits<SourceSender>::sends_done ||
|
|
sender_traits<CompletionSender>::sends_done;
|
|
|
|
template <typename SourceSender2, typename CompletionSender2>
|
|
explicit type(
|
|
SourceSender2&& source, CompletionSender2&& completion)
|
|
noexcept(std::is_nothrow_constructible_v<SourceSender, SourceSender2> &&
|
|
std::is_nothrow_constructible_v<CompletionSender, CompletionSender2>)
|
|
: source_(static_cast<SourceSender2&&>(source))
|
|
, completion_(static_cast<CompletionSender2&&>(completion)) {}
|
|
|
|
private:
|
|
|
|
// TODO: Also constrain these methods to check that the CompletionSender
|
|
// is connectable to any of the instantiations of done/value/error_receiver
|
|
// that could be created for each of the results that SourceSender might
|
|
// complete with. For now we just check done_receiver as an approximation.
|
|
|
|
template(typename CPO, typename S, typename Receiver)
|
|
(requires
|
|
same_as<CPO, tag_t<connect>> AND
|
|
same_as<remove_cvref_t<S>, sender> AND
|
|
receiver<Receiver> AND
|
|
sender_to<
|
|
member_t<S, SourceSender>,
|
|
receiver_t<
|
|
member_t<S, SourceSender>,
|
|
CompletionSender,
|
|
remove_cvref_t<Receiver>>> AND
|
|
sender_to<
|
|
CompletionSender,
|
|
done_receiver<
|
|
member_t<S, SourceSender>,
|
|
CompletionSender,
|
|
remove_cvref_t<Receiver>>>)
|
|
friend auto tag_invoke(CPO, S&& s, Receiver&& r)
|
|
-> operation<member_t<S, SourceSender>, CompletionSender, Receiver> {
|
|
return operation<member_t<S, SourceSender>, CompletionSender, Receiver>{
|
|
static_cast<S&&>(s).source_,
|
|
static_cast<S&&>(s).completion_,
|
|
static_cast<Receiver&&>(r)};
|
|
}
|
|
|
|
SourceSender source_;
|
|
CompletionSender completion_;
|
|
};
|
|
} // namespace _final
|
|
|
|
namespace _final_cpo
|
|
{
|
|
inline const struct _fn {
|
|
template <typename SourceSender, typename CompletionSender>
|
|
auto operator()(SourceSender&& source, CompletionSender&& completion) const
|
|
noexcept(std::is_nothrow_constructible_v<
|
|
_final::sender<SourceSender, CompletionSender>,
|
|
SourceSender,
|
|
CompletionSender>) -> _final::sender<SourceSender, CompletionSender> {
|
|
return _final::sender<SourceSender, CompletionSender>{
|
|
static_cast<SourceSender&&>(source),
|
|
static_cast<CompletionSender&&>(completion)};
|
|
}
|
|
template <typename CompletionSender>
|
|
constexpr auto operator()(CompletionSender&& completion) const
|
|
noexcept(is_nothrow_callable_v<
|
|
tag_t<bind_back>, _fn, CompletionSender>)
|
|
-> bind_back_result_t<_fn, CompletionSender> {
|
|
return bind_back(*this, (CompletionSender&&)completion);
|
|
}
|
|
} finally{};
|
|
} // namespace _final_cpo
|
|
|
|
using _final_cpo::finally;
|
|
|
|
} // namespace unifex
|
|
|
|
#include <unifex/detail/epilogue.hpp>
|