namespace std::execution {
struct split_t { unspecified };
inline constexpr split_t split{};
}
概要
split
は、任意の入力Senderを複数回接続(connect)可能とするSenderアダプタである。
split
はパイプ可能Senderアダプタオブジェクトであり、パイプライン記法をサポートする。
効果
説明用の型split-env
を、インスタンスenv
に対して式get_stop_token(env)
が適格かつ型inplace_stop_token
をもつ型とする。
説明用の式sndr
に対して、型Sndr
をdecltype((sndr))
とする。sender_in<Sndr, split-env> == false
のとき、呼び出し式split(sndr)
は不適格となる。
そうでなければ、呼び出し式split(sndr)
はsndr
が1回だけ評価されることを除いて、下記と等価。
transform_sender(get-domain-early(sndr), make-sender(split, {}, sndr))
Senderアルゴリズムタグ split
説明用のSenderアルゴリズムタグ型split-impl-tag
を空の型とする。説明用の式sndr
に対して、式split.transform_sender(sndr)
は下記と等価。
auto&& [tag, _, child] = sndr;
auto* sh_state = new shared-state{std::forward_like<decltype((sndr))>(child)};
return make-sender(split-impl-tag(), shared-wrapper{sh_state, tag});
説明用の型shared-wrapper
は、sh_state
が指すshared-state
オブジェクトの参照カウントを管理するクラスである。
shared-wrapper
はcopyable
のモデルである。- ムーブ操作 : 移動済みオブジェクトをヌルとする。
- コピー操作 :
sh_state->inc-ref()
を呼び出して参照カウントをインクリメントする。 - 代入操作 : Copy-And-Swap操作を行う。
- デストラクタ :
sh_state
がヌルのときは何もしない。そうでないとき、sh_state->dec-ref()
を評価して参照カウントをデクリメントする。
Senderアルゴリズムタグ split-impl-tag
Senderアルゴリズム動作説明用のクラステンプレートimpls-for
に対して、下記の特殊化が定義される。
namespace std::execution {
template<>
struct impls-for<split-impl-tag> : default-impls {
static constexpr auto get-state = see below;
static constexpr auto start = see below;
};
}
impls-for<split-impl-tag>::get-state
メンバは、下記ラムダ式と等価な関数呼び出し可能なオブジェクトで初期化される。
[]<class Sndr>(Sndr&& sndr, auto& rcvr) noexcept {
return local-state{std::forward<Sndr>(sndr), rcvr};
}
impls-for<split-impl-tag>::start
メンバは、下記の関数呼び出し演算子をもつオブジェクトで初期化される。
template<class Sndr, class Rcvr>
void operator()(local-state<Sndr, Rcvr>& state, Rcvr& rcvr) const noexcept;
効果 : state.sh_state->completed == true
のとき、state.notify()
を評価してリターンする。そうでなければ、下記を順番に行う。
-
以下を評価する。
state.on_stop.emplace( get_stop_token(get_env(rcvr)), on-stop-request{state.sh_state->stop_src});
-
下記をアトミックに行う。
state.sh_state->completed
の値c
を読み取りc == false
のとき、state.sh_state->waiting_state
にaddressof(state)
を挿入する
c == true
ならば、state.notify()
を呼び出してリターンする。- そうではなく、
addressof(state)
がstate.sh_state->waiting_state
に最初に追加されるアイテムならば、state.sh_state->start-op()
を評価する。
説明専用エンティティ
クラステンプレートlocal-state
namespace std::execution {
struct local-state-base { // exposition only
virtual ~local-state-base() = default;
virtual void notify() noexcept = 0; // exposition only
};
template<class Sndr, class Rcvr>
struct local-state : local-state-base { // exposition only
using on-stop-callback = // exposition only
stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;
local-state(Sndr&& sndr, Rcvr& rcvr) noexcept;
~local-state();
void notify() noexcept override;
private:
optional<on-stop-callback> on_stop; // exposition only
shared-state<Sndr>* sh_state; // exposition only
Rcvr* rcvr; // exposition only
};
}
local-state(Sndr&& sndr, Rcvr& rcvr) noexcept;
-
効果 : 下記と等価。
auto& [_, data, _] = sndr; this->sh_state = data.sh_state.get(); this->sh_state->inc-ref(); this->rcvr = addressof(rcvr);
~local-state();
-
効果 : 下記と等価。
sh_state->dec-ref();
void notify() noexcept override;
-
効果 : 下記と等価。
クラステンプレートsplit-receiver
namespace std::execution {
template<class Sndr>
struct split-receiver { // exposition only
using receiver_concept = receiver_t;
template<class Tag, class... Args>
void complete(Tag, Args&&... args) noexcept { // exposition only
using tuple_t = decayed-tuple<Tag, Args...>;
try {
sh_state->result.template emplace<tuple_t>(Tag(), std::forward<Args>(args)...);
} catch (...) {
using tuple_t = tuple<set_error_t, exception_ptr>;
sh_state->result.template emplace<tuple_t>(set_error, current_exception());
}
sh_state->notify();
}
template<class... Args>
void set_value(Args&&... args) && noexcept {
complete(execution::set_value, std::forward<Args>(args)...);
}
template<class Error>
void set_error(Error&& err) && noexcept {
complete(execution::set_error, std::forward<Error>(err));
}
void set_stopped() && noexcept {
complete(execution::set_stopped);
}
struct env { // exposition only
shared-state<Sndr>* sh-state; // exposition only
inplace_stop_token query(get_stop_token_t) const noexcept {
return sh-state->stop_src.get_token();
}
};
env get_env() const noexcept {
return env{sh_state};
}
shared-state<Sndr>* sh_state; // exposition only
};
}
クラステンプレートshared-state
namespace std::execution {
template<class Sndr>
struct shared-state {
using variant-type = see below; // exposition only
using state-list-type = see below; // exposition only
explicit shared-state(Sndr&& sndr);
void start-op() noexcept; // exposition only
void notify() noexcept; // exposition only
void inc-ref() noexcept; // exposition only
void dec-ref() noexcept; // exposition only
inplace_stop_source stop_src{}; // exposition only
variant-type result{}; // exposition only
state-list-type waiting_states; // exposition only
atomic<bool> completed{false}; // exposition only
atomic<size_t> ref_count{1}; // exposition only
connect_result_t<Sndr, split-receiver<Sndr>> op_state; // exposition only
};
}
-
説明用のパック
Sigs
をcompletion_signatures_of_t<Sndr>
によるcompletion_signatures
特殊化のテンプレートパラメータと定義する。説明用の型Tag
とパックArgs
に対して、説明用のエイリアステンプレートas-tuple<Tag(Args...)>
をdecayed-tuple<Tag, Args...>
と定義する。型variant-type
は下記定義において重複削除した型となる。variant<tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>
-
型
state-list-type
を、local-state-base
オブジェクトへのポインタのリストを格納し、アトミックに要素挿入できる型とする。
explicit shared-state(Sndr&& sndr);
- 効果 :
op_state
をconnect(std::forward<Sndr>(sndr), split-receiver{this})
の結果で初期化する。 - 事後条件 :
waiting_states
が空、かつcompleted == false
void start-op() noexcept;
- 効果 :
inc-ref()
を評価する。stop_src.stop_requested() == ture
のときnotify()
を評価する。そうでなければ、start(op_state)
を評価する。
void notify() noexcept;
- 効果 : 下記をアトミックに行い、ローカル変数
prior_states
の各ポインタp
に対してp->notify()
を評価し、最後にdec-ref()
を評価する。completed
にtrue
を設定し、waiting_states
を空のリストと交換し、古い値をローカル変数prior_states
に格納する。
void inc-ref() noexcept;
- 効果 :
ref_count
をインクリメントする。
void dec-ref() noexcept;
- 効果 :
ref_count
をデクリメントする。ref_count
の新たな値が0
のとき、delete this
を呼び出す。 - 同期操作 :
dec-ref()
の評価がref_count
を値0
にデクリメントしないとき、ref_count
を値0
へデクリメントするdec-ref()
の評価に対して同期する。
カスタマイゼーションポイント
Senderアルゴリズム構築時に、Sendersndr
に関連付けられた実行ドメインに対してexecution::transform_sender
経由でSender変換が行われる。
デフォルト実行ドメインではsplit.transform_sender(sndr)
が呼ばれ、前述仕様通りのSenderへと変換される。
例
#include <print>
#include <execution>
namespace ex = std::execution;
int main()
{
{ // 関数呼び出し
ex::sender auto snd0 = ex::just(21);
ex::sender auto snd1 = ex::then(snd0, [](int n) {
std::println("then");
return 2 * n;
});
ex::sender auto sndr = ex::split(snd1);
auto [val1] = std::this_thread::sync_wait(sndr).value();
std::println("{}", val1);
auto [val2] = std::this_thread::sync_wait(sndr).value();
std::println("{}", val2);
}
{ // パイプライン記法
ex::sender auto sndr =
ex::just(21)
| ex::then([](int n) {
std::println("then");
return 2 * n;
})
| ex::split();
auto [val1] = std::this_thread::sync_wait(sndr).value();
std::println("{}", val1);
auto [val2] = std::this_thread::sync_wait(sndr).value();
std::println("{}", val2);
}
}
出力
then
42
42
then
42
42
バージョン
言語
- C++26
処理系
- Clang: ??
- GCC: ??
- ICC: ??
- Visual C++: ??