namespace std::execution {
struct on_t { unspecified };
inline constexpr on_t on{};
}
概要
on
は、2種類の呼び出し形式サポートするSenderアダプタである。
on(sch, sndr)
: Sendersndr
をSchedulersch
に関連付けられた実行リソースに属する実行エージェント上で開始し、完了後にon
Senderが開始された実行リソースへと実行制御を戻す。on(sndr, sch, closure)
: Sendersndr
の完了後に、Schedulersch
に関連付けられた実行リソースに属する実行エージェントへ実行を移し、sndr
の完了結果をもってSenderアダプタクロージャclosure
を実行し、Sendersndr
が完了された実行リソースへと実行制御を戻す。
on
はパイプ可能Senderアダプタオブジェクトであり、パイプライン記法をサポートする。
効果
呼び出し式 on(sch, sndr)
説明用の式sch
とsndr
に対して、下記いずれかがtrue
となるとき呼び出し式on(sch, sndr)
は不適格となる。
decltype((sch))
がscheduler
を満たさない、もしくはdecltype((sndr))
がsender
を満たさず、かつパイプ可能Senderアダプタクロージャオブジェクトではない、もしくはdecltype((sndr))
がsender
を満たし、かつパイプ可能Senderアダプタクロージャオブジェクトである。
そうでなければ、呼び出し式on(sch, sndr)
はsch
が1回だけ評価されることを除いて、下記と等価。
transform_sender(
query-with-default(get_domain, sch, default_domain()),
make-sender(on, sch, sndr))
呼び出し式 on(sndr, sch, closure)
説明用の式sndr
, sch
, closure
に対して、下記いずれかがtrue
となるとき呼び出し式on(sndr, sch, closure)
は不適格となる。
decltype((sch))
がscheduler
を満たさない、もしくはdecltype((sndr))
がsender
を満たさない、もしくはclosure
がパイプ可能Senderアダプタクロージャオブジェクトではない。
そうでなければ、呼び出し式on(sndr, sch, closure)
はsndr
が1回だけ評価されることを除いて、下記と等価。
transform_sender(
get-domain-early(sndr),
make-sender(on, product-type{sch, closure}, sndr))
Senderアルゴリズムタグ on
説明用の式out_sndr
とenv
に対して、型OutSndr
をdecltype((out_sndr))
とし、型Env
をdecltype((env))
とする。sender-for<OutSndr, on_t> == false
のとき、式on.transform_env(out_sndr, env)
および式on.transform_sender(out_sndr, env)
はいずれも不適格となる。
そうでなければ、下記の通り。
-
式
on.transform_env(out_sndr, env)
は下記と等価。auto&& [_, data, _] = out_sndr; if constexpr (scheduler<decltype(data)>) { return JOIN-ENV(SCHED-ENV(std::forward_like<OutSndr>(data)), FWD-ENV(std::forward<Env>(env))); } else { return std::forward<Env>(env); }
-
式
on.transform_sender(out_sndr, env)
は下記と等価。auto&& [_, data, child] = out_sndr; if constexpr (scheduler<decltype(data)>) { auto orig_sch = query-with-default(get_scheduler, env, not-a-scheduler()); if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) { return not-a-sender{}; } else { return continues_on( starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)), std::move(orig_sch)); } } else { auto& [sch, closure] = data; auto orig_sch = query-with-default( get_completion_scheduler<set_value_t>, get_env(child), query-with-default(get_scheduler, env, not-a-scheduler())); if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) { return not-a-sender{}; } else { return write-env( continues_on( std::forward_like<OutSndr>(closure)( continues_on( write-env(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)), sch)), orig_sch), SCHED-ENV(sch)); } }
説明専用のクラスnot-a-scheduler
を未規定な空のクラス型、not-a-sender
を下記の通り定義する。
struct not-a-sender {
using sender_concept = sender_t;
auto get_completion_signatures(auto&&) const {
return see below;
}
};
メンバ関数get_completion_signatures
はcompletion_signatures
クラステンプレートの特殊化とは異なる型のオブジェクトを返す。
処理系(標準ライブラリ実装者)は、この型を用いてユーザにエラー理由を通知することが推奨される。
カスタマイゼーションポイント
Senderアルゴリズム構築時に、関連付けられた実行ドメインに対してexecution::transform_sender
経由でSender変換が行われる。
デフォルト実行ドメインでは無変換。
- 呼び出し式
on(sch, sndr)
: Schedulersch
に関連付けられた実行ドメイン - 呼び出し式
on(sndr, sch, closure)
: Sendersndr
に関連付けられた実行ドメイン
Receiverとの接続(connect)時に、関連付けられた実行ドメインに対してexecution::transform_sender
経由でSender変換が行われる。
デフォルト実行ドメインではon.transform_sender(out_sndr, env)
が呼ばれ、前述仕様通りのSenderへと変換される。
呼び出し式 on(sch, sndr)
説明用の式out_sndr
をon(sch, sndr)
の戻り値Senderとし、型OutSndr
をdecltype((out_sndr))
とする。式out_rcvr
をsender_in<OutSndr, Env> == true
となる環境Env
に関連付けられたReceiverとする。out_sndr
とout_rcvr
との接続(connect)結果Operation Stateへの左辺値参照をop
としたとき、start(op)
呼び出しは下記を満たすべき。
- 現在のScheduler
get_scheduler(get_env(rcvr))
を記憶する。 - Scheduler
sch
に関連付けられた実行リソースに属する実行エージェント上でsndr
を開始する。 sndr
の完了後に、手順1で記憶したSchedulerに関連付けられた実行リソースに実行制御を戻す。sndr
の非同期操作の結果をout_rcvr
へ転送する。
いずれかのスケジューリングが失敗した場合、未規定の実行エージェント上でout_rcvr
のエラー完了が行われるべき。
呼び出し式 on(sndr, sch, closure)
説明用の式out_sndr
をon(sndr, sch, closure)
の戻り値Senderとし、型OutSndr
をdecltype((out_sndr))
とする。式out_rcvr
をsender_in<OutSndr, Env> == true
となる環境Env
に関連付けられたReceiverとする。out_sndr
とout_rcvr
との接続(connect)結果Operation Stateへの左辺値参照をop
としたとき、start(op)
呼び出しは下記を満たすべき。
- 下記のうち最初に適格となるものを、現在のSchedulerとして記憶する。
get_completion_scheduler<set_value_t>(get_env(rcvr))
get_scheduler(get_env(rcvr))
sndr
を現在の実行エージェント上で開始する。sndr
の完了後に、Schedulersch
に関連付けられた実行リソースの実行エージェントに実行制御を移す。sndr
の非同期操作の結果を、Senderclosure(S)
と接続して開始するかのように転送する。ここでS
はsndr
の非同期操作の結果と同期的に完了するSenderとする。- 前手順で開始した非同期操作が完了したら、手順1で記憶したSchedulerに関連付けられた実行リソースに実行制御を戻し、非同期操作の結果を
out_rcvr
へ転送する。
いずれかのスケジューリングが失敗した場合、未規定の実行エージェント上でout_rcvr
のエラー完了が行われるべき。
例
例1: on(sch, sndr)
#include <thread>
#include <print>
#include <execution>
namespace ex = std::execution;
int main()
{
std::println("main#{}", std::this_thread::get_id());
ex::run_loop loop;
std::jthread worker{[&]{
std::println("start worker#{}", std::this_thread::get_id());
loop.run();
}};
ex::scheduler auto sch = loop.get_scheduler();
ex::sender auto snd0 =
ex::then(ex::just(), []() {
std::println("on worker#{}", std::this_thread::get_id());
return 6;
});
ex::sender auto snd1 = ex::on(sch, snd0);
ex::sender auto snd2 =
ex::then(snd1, [](int n) {
std::println("on main#{}", std::this_thread::get_id());
return n * 7;
});
auto [val] = std::this_thread::sync_wait(std::move(snd2)).value();
std::println("val={}", val);
loop.finish();
}
出力例
main#133519926773568
start worker#133519921645120
on worker#133519921645120
on main#133519926773568
val=42
例2: on(sndr, sch, closure)
#include <thread>
#include <print>
#include <execution>
namespace ex = std::execution;
int main()
{
std::println("main#{}", std::this_thread::get_id());
ex::run_loop loop;
std::jthread worker{[&]{
std::println("start worker#{}", std::this_thread::get_id());
loop.run();
}};
ex::scheduler auto sch = loop.get_scheduler();
ex::sender auto sndr =
ex::just()
| ex::then([]() {
std::println("on main#{}", std::this_thread::get_id());
return 2;
})
| ex::on(sch, ex::then([](int n) {
std::println("on worker#{}", std::this_thread::get_id());
return n * 3;
}))
| ex::then([](int n) {
std::println("on main#{}", std::this_thread::get_id());
return n * 7;
});
auto [val] = std::this_thread::sync_wait(std::move(sndr)).value();
std::println("val={}", val);
loop.finish();
}
出力例
main#133519926773568
start worker#133519921645120
on main#133519926773568
on worker#133519921645120
on main#133519926773568
val=42
バージョン
言語
- C++26
処理系
- Clang: ??
- GCC: ??
- ICC: ??
- Visual C++: ??