Reference
Concepts
The stdexec API is structured around a small set of foundational concepts. Most sender adaptors and consumers express their requirements in terms of these concepts, so understanding them — and which one to reach for in which situation — pays off across the rest of the reference.
The concepts fall into three layers:
Sender side: sender, sender_in, sender_to. A sender is the basic unit of composition — a value that describes (but does not yet execute) an async computation.
Receiver / operation-state side: receiver, receiver_of, operation_state. These describe the consumer half of a sender/receiver pair — the destination of completion signals and the running operation that delivers them.
Context side: scheduler, scope_token, scope_association. These describe how work is dispatched onto execution resources and how its lifetime is tracked.
Sender concepts
sender
-
template<class _Sender>
concept sender - #include <__sender_concepts.hpp>
The fundamental concept of the sender model: a type that describes (but does not yet execute) an asynchronous operation.
A
senderis the basic unit of composition in stdexec. It is a value type that describes an async computation; the work it describes does not start until the sender is connected to a receiver (viastdexec::connect) and the resulting operation state is started (viastdexec::start).Concretely, a type
Ssatisfiessenderif:Shas been opted into the concept — either by exposing asender_concepttype alias derived fromstdexec::sender_tag, or by specializingstdexec::enable_sender<S>totrue, or by being an awaitable in stdexec’s coroutine promise type.Sprovides an environment viastdexec::get_env(every sender has an environment, possibly empty).S'sdecayed type is move-constructible and constructible from anS(this is what allows senders to be stored and forwarded by value).
Note that
senderby itself does not require the sender’s completion signatures to be computable. That is the additional constraint ofsender_in(which carries an environment). Generic sender-adaptor code that needs to know “what does this sender
complete with?” uses
sender_in<S, Env>, notsender(alone).See [exec.snd.concepts] in the C++26 working draft.
See also
stdexec::sender_in — sender plus a specific environment, with computable signatures
See also
stdexec::sender_to — sender plus a specific receiver, with compatible signatures
See also
stdexec::sender_tag — the tag type that opts a class into this concept
See also
stdexec::enable_sender — alternative opt-in path
-
struct sender_tag
Tag type used to opt a class into the
stdexec::senderconcept.A user-defined type satisfies
stdexec::senderby exposing a publicsender_concepttype alias whose type derives fromsender_tag:struct my_sender { using sender_concept = stdexec::sender_tag; // ... usual sender machinery: completion signatures, connect ... };
See also
See also
See also
Subclassed by experimental::execution::sequence_sender_tag
-
template<class _Sender>
bool const stdexec::enable_sender = __detail::__enable_sender<_Sender> A variable template that opts a class into the
stdexec::senderconcept by an alternative path.Specialize
enable_sender<MySender>totrueto declare thatMySenderis a sender, without having to define asender_concepttype alias on the class itself. This is useful when the class cannot be modified (e.g. third-party types) or when the class is a coroutine awaitable type.struct legacy_sender { }; // cannot be modified template <> inline constexpr bool stdexec::enable_sender<legacy_sender> = true;
By default,
enable_sender<S>istruewhenShas asender_conceptalias deriving fromsender_tag, or whenSis awaitable in stdexec’s coroutine promise type.
sender_in
-
template<class _Sender, class ..._Env>
concept sender_in - #include <__sender_concepts.hpp>
A
senderwhose completion signatures can be computed in a given environment.sender_inis the form of the sender concept that generic adaptor code actually uses. Wheresenderjust asks “is this a sender at
all?”,
sender_in<S, Env>asks “is @c S a sender whose completion
signatures we can compute when connected to a receiver with
environment @c Env?” — that information is what every adaptor needs to type-check itself.
Concretely,
sender_in<S, Env>requires:Ssatisfiessender.get_completion_signatures<S, Env>()is a constant expression whose value is a validcompletion_signaturesspecialization.
The
Envparameter is optional (the variadic accepts zero or one environment). When no environment is supplied, the sender must have a dependent-environment-free set of completion signatures — i.e. its signatures must not vary by environment.See [exec.snd.concepts] in the C++26 working draft.
See also
stdexec::sender — the base concept
See also
stdexec::sender_to — adds a specific receiver
See also
stdexec::get_completion_signatures — the customization point this concept depends on
sender_to
-
template<class _Sender, class _Receiver>
concept sender_to - #include <__sender_concepts.hpp>
A
senderthat can be connected to a specificreceiver.sender_to<S, R>is the strongest form of the sender concept: it requires thatSis a sender whose completion signatures can be computed inR'senvironment, thatRis a receiver that accepts all of those signatures, and thatconnect(S, R)is well-formed.This is the constraint a sender consumer or scheduler implementation uses just before actually calling
connect— it’s the strongest way to say “yes, this pair is wired up correctly.”See [exec.snd.concepts] in the C++26 working draft.
See also
stdexec::sender_in — without the receiver-compatibility check
See also
stdexec::receiver_of — the receiver-side mirror of this concept
See also
stdexec::connect — the operation
sender_tovalidates
Receiver and operation-state concepts
receiver
-
template<class _Receiver>
concept receiver - #include <__receivers.hpp>
The fundamental concept of the receiver model: a callback-shaped object that consumes the result of an asynchronous operation.
A receiver is the destination half of a sender/receiver pair. It is the object on which a started operation eventually invokes one of
set_value,set_error, orset_stoppedto deliver its completion. Receivers are typically synthesized by sender consumers and adaptors — most user code never writes a receiver by hand; it writes senders and composes them.Concretely, a type
Rsatisfiesreceiverif:Ris opted into the concept via areceiver_concepttype alias deriving fromstdexec::receiver_tag.Rprovides an environment viastdexec::get_env(so child operations can query for the stop token, allocator, scheduler, etc.).R'sdecayed type is nothrow move-constructible — receivers are moved into operation states by sender adaptors, and that move must not throw.R'sdecayed type is constructible from anR.
Note that this concept alone does not require
Rto accept any particular completion signals — for that, seereceiver_of, which takes acompletion_signaturespack and validates that the receiver has matchingset_value/set_error/set_stoppedmembers.See [exec.recv.concepts] in the C++26 working draft.
See also
stdexec::receiver_of — receiver plus specific completion signatures
See also
stdexec::receiver_tag — the tag type that opts a class into this concept
See also
See also
See also
-
struct receiver_tag
Tag type used to opt a class into the
stdexec::receiverconcept.A user-defined type satisfies
stdexec::receiverby exposing a publicreceiver_concepttype alias whose type derives fromreceiver_tag:struct my_receiver { using receiver_concept = stdexec::receiver_tag; void set_value(int v) noexcept { ... } void set_error(std::exception_ptr e) noexcept { ... } void set_stopped() noexcept { ... } };
See also
See also
See also
Subclassed by stdexec::__receiver_adaptor< _Derived, _Base >
receiver_of
-
template<class _Receiver, class _Completions>
concept receiver_of - #include <__receivers.hpp>
A
receiverthat accepts a specific set of completion signatures.receiver_of<R, Sigs>says: “R is a receiver, and for every
@c set_xxx_t(Args…) signature in @c Sigs (a
@c stdexec::completion_signatures pack), R has a matching member that
is callable with @c Args… “. This is the constraint that ensures a sender’s completion signals can actually be delivered to the receiver — sender adaptors typically express their compatibility requirements in terms of
receiver_of, not barereceiver.When this concept fails, stdexec produces a focused error message naming the specific completion signal the receiver doesn’t accept (e.g. “the receiver does not accept set_value_t(int)”) — this is the main reason to use
receiver_ofover manually checking each member’s callability.See [exec.recv.concepts] in the C++26 working draft.
See also
stdexec::receiver — without the signature check
See also
stdexec::sender_to — the sender-side mirror of this concept
See also
stdexec::completion_signatures — the signature pack this concept consumes
operation_state
-
template<class _Op>
concept operation_state - #include <__operation_states.hpp>
An in-progress, immovable, startable representation of an asynchronous operation — the result of connecting a sender to a receiver.
An operation state is what you get from
stdexec::connect:it is the concrete, type-erased-by-construction record of one specific sender being driven into one specific receiver. Callingstdexec::starton it begins the work; the operation runs until it eventually invokes one ofset_value/set_error/set_stoppedon the receiver it was constructed with.Three properties define the concept
operation_state:The type is destructible — operations clean up cleanly when their storage goes away (typically after they have completed).
The type is an object type (not a reference, not a function) — operation states are stored, not passed by handle.
stdexec::start(op)is well-formed for any lvalueopof the type.
What the concept does not require (but the rules of the sender model do):
Immovability after construction. Once an operation state has been constructed, it must not be moved or copied — child operations inside it typically hold pointers into its storage, which would dangle on move. The natural way to satisfy this is to delete the move and copy constructors, which is what most operation states do. The concept itself doesn’t check this; the rule is part of the sender-model contract.
Lifetime guarantee until completion. Once
starthas been called, the operation state must remain alive until the receiver has been completed. This is the caller’s responsibility (the caller ofstart, typically a sender adaptor or a consumer).
Like receivers, operation states are usually an implementation detail of sender adaptors and consumers — most user code never names a specific operation-state type.
See [exec.opstate] in the C++26 working draft.
See also
stdexec::connect — the customization point that produces operation states
See also
stdexec::start — the customization point this concept depends on
See also
stdexec::operation_state_tag — the tag type that opts a class into this concept
-
struct operation_state_tag
Tag type used to opt a class into the
stdexec::operation_stateconcept.A user-defined operation-state type satisfies
stdexec::operation_stateby exposing a publicoperation_state_concepttype alias whose type derives fromoperation_state_tag:struct my_opstate { using operation_state_concept = stdexec::operation_state_tag; void start() noexcept { ... } };
See also
See also
See also
Context concepts
scheduler
-
template<class _Scheduler>
concept scheduler - #include <__schedulers.hpp>
A lightweight handle to an execution context — the abstraction over things that can run sender pipelines.
A scheduler is a small, value-like handle to a (potentially heavy and immovable) execution resource: a thread pool, a GPU stream, an event loop, an inline run-loop, etc. The only operation a scheduler must support is
stdexec::schedule, which obtains a sender that, when connected and started, eventually value-completes on that resource.Concretely, a type
Ssatisfiesschedulerif:schedule(s)is well-formed and returns asender. This is the defining operation; everything else is value-semantics plumbing.S'sdecayed type is equality-comparable (two schedulers compare equal iff they refer to the same execution resource — used for optimization decisions such as elision of redundantcontinues_onhops).S'sdecayed type is copy-constructible and nothrow move-constructible — schedulers are cheap to pass around.
See [exec.sched] in the C++26 working draft.
See also
stdexec::schedule — the customization point that defines this concept
See also
stdexec::starts_on — adaptor that runs a sender on a scheduler
See also
stdexec::continues_on — adaptor that transfers to a scheduler mid-pipeline
See also
stdexec::schedule_result_t — the sender type returned by
schedule(s)
scope_token
-
template<class _Token>
concept scope_token - #include <__scope_concepts.hpp>
A copyable handle to an async scope — the owner of lifetime for spawned operations.
An async scope is a logical container for fire-and-forget operations launched via
stdexec::spawnorstdexec::spawn_future. It tracks every operation associated with it so that, at shutdown, it can block until every spawned operation has completed (typically via a.join() member that returns a sender).A
scope_tokenis a small, copyable handle to such a scope. User code typically obtains a token from anexec::async_scopeviascope.get_token()and passes it intospawnorspawn_future.Concretely, a type
Tsatisfiesscope_tokenif it is copyable and provides two members:token.try_associate()— attempts to register a new operation with the scope, returning ascope_associationwhose boolean conversion indicates success. Fails (returns “false”) when the scope has already begun shutting down.token.wrap(sndr)— wraps a sender so that, when started viaspawn, its lifetime is tied to the scope.
See [exec.scope.concepts] in the C++26 working draft.
See also
stdexec::scope_association — the return type of
try_associateSee also
stdexec::spawn — fire-and-forget into a scope
See also
stdexec::spawn_future — spawn into a scope and observe via a sender
scope_association
-
template<class _Assoc>
concept scope_association - #include <__scope_concepts.hpp>
A movable handle representing successful (or failed) registration of an operation with an async scope.
Implementations of
stdexec::scope_tokenreturn ascope_associationfromtry_associate(). It is a small, movable value that:Contextually converts to
boolto indicate whether the association succeeded (the scope is still open and the operation was registered) or failed (the scope is shutting down and the operation must not start).Holds onto whatever state the scope needs to track the operation so that the operation can be deregistered at completion.
Can produce a fresh, equivalent association via
try_associate()— used to model “re-associate with the
same scope”.
Concretely,
scope_associationrequires the type to be movable, nothrow move-constructible and assignable, default-initializable, and to support both theboolconversion and thetry_associate()member.User code typically does not interact with
scope_associationdirectly — it is the return type ofscope_token::try_associateand is used internally bystdexec::spawnandstdexec::spawn_future.See [exec.scope.concepts] in the C++26 working draft.
See also
See also
See also
Core Customization Points
The customization points listed here are the defining operations of the sender model. They are what every sender, receiver, and operation state type must support (each in its own way) to participate in the protocol. Most user code never calls these directly — sender adaptors and consumers do — but anyone writing a new sender, receiver, or scheduler will implement one or more of these.
The CPOs fall into three layers:
Sender-side: connect, get_completion_signatures, get_env. These describe how a sender exposes its computation and attributes to the framework.
Operation-state-side: start. The trigger that turns a connected sender into a running operation.
Receiver-side: set_value, set_error, set_stopped, get_env. These describe how an operation state delivers a completion to its receiver and queries the receiver’s environment.
See the Developer’s Guide for a narrative walkthrough of how these fit together when writing a new sender adaptor.
Sender-side
connect
-
struct connect_t
Customization point object that connects a sender to a receiver, producing an operation state.
connectis the central seam of the sender model. Callingstdexec::connect(sndr, rcvr)does not run the sender; it produces an operation state — an opaque, immovable, startable object that, when subsequently passed tostdexec::start, will eventually deliver exactly one completion signal (set_value,set_error, orset_stopped) to the receiver.Every sender adaptor and consumer ultimately reaches for
connect. Most user code does not call it directly —sync_wait,spawn, and the various adaptors do — but it is the operation a sender author must support, either by exposing a.connect(receiver)member, by being a coroutine awaitable (so the fallback awaitable adapter applies), or — historically — viatag_invoke(now deprecated).See [exec.connect] in the C++26 working draft for the normative specification.
Lookup.
Before dispatching,
connecttransforms the sender via the active domain’stransform_sender(passing inget_env(rcvr)as the environment). This is how domain-based customization — e.g. the GPU scheduler taking over athenchain — is implemented.connectthen dispatches by trying, in order:A static member:
S::__static_connect(sndr, rcvr)(an stdexec-internal extension point).A non-static member:
sndr.connect(rcvr). This is the standard way sender authors customizeconnectin C++26.The awaitable fallback: if
sndris awaitable in stdexec’s receiver-promise type, an adapter operation state is synthesized. This is what makes coroutines work as senders.tag_invoke(connect, sndr, rcvr)— deprecated, retained for backwards compatibility.
Customization.
The standard pattern for sender authors is the
.connect()member:struct my_sender { using sender_concept = stdexec::sender_tag; using completion_signatures = stdexec::completion_signatures< stdexec::set_value_t(int)>; template <stdexec::receiver_of<completion_signatures> R> auto connect(R rcvr) && -> my_opstate<R> { return my_opstate<R>{std::move(rcvr), ...}; } };
The returned object must satisfy
stdexec::operation_state. In particular, it must be immovable once constructed (typically by deleting the move and copy constructors).Concept checks.
connect(s, r)is only well-formed when bothsender_in<S, env_of_t<R>>andreceiver_of<R, completion_signatures_of_t<S, env_of_t<R>>>hold. The diagnostics for failures here are intentionally focused — stdexec emits messages that name the specific completion signal or environment query the receiver doesn’t accept.See also
stdexec::start — what you call on the returned operation state
See also
stdexec::operation_state — the concept the result satisfies
See also
stdexec::sender_to — the concept this CPO drives
See also
stdexec::set_value — one of the completions the operation eventually delivers
See also
stdexec::transform_sender — the domain-customization step run before dispatch
Public Functions
-
template<class _Sender, class _Receiver, class _DeclFn = __connect::__connect_declfn_t<_Sender, _Receiver>>
inline constexpr auto operator()(_Sender &&__sndr, _Receiver &&__rcvr) const noexcept(__nothrow_callable<_DeclFn>) -> __call_result_t<_DeclFn> Connect
__sndrto__rcvr, returning an operation state.- Template Parameters:
_Sender – A type modeling
stdexec::sender_inforenv_of_t<_Receiver>._Receiver – A type modeling
stdexec::receiver_offor__sndr'scompletion signatures.
- Parameters:
__sndr – The sender describing the asynchronous work. Perfect-forwarded into the operation state.
__rcvr – The receiver that will eventually receive a completion. Perfect-forwarded into the operation state.
- Returns:
An object satisfying
stdexec::operation_state. Pass it tostdexec::startto begin the work.
get_completion_signatures
Unlike the other entries in this section, get_completion_signatures
is a function template (not a CPO instance), so it has no underlying
struct. The two forms — environment-free and environment-dependent —
share documentation:
Operation-state-side
start
-
struct start_t
Customization point object that begins the execution of an operation state.
startis the trigger that turns a connected sender into a running asynchronous operation. The operation state returned bystdexec::connectdoes nothing until it is passed tostart; from that moment on, it is running and will eventually deliver exactly one completion signal to the receiver it was connected with.See [exec.opstate.start] in the C++26 working draft.
Lifetime contract.
The operation state passed to
startmust:Be an lvalue —
start(op)withopan rvalue is ill-formed. The reason: the caller is responsible for keeping the operation state alive until completion, which only makes sense for objects with stable identity.Remain alive until the receiver has been completed. The operation state itself typically holds child operation states (for sender adaptors) and references to the receiver’s storage — destroying it early would invalidate those.
Once
startreturns, the operation is running but may or may not have already completed (an inline-completing operation may have completed synchronously beforestartreturned; an async operation may complete arbitrarily later).Customization.
An operation state opts in by exposing a
noexcept,void-returning.start()member:struct my_opstate { using operation_state_concept = stdexec::operation_state_tag; // Immovable — once constructed, must stay put: my_opstate(my_opstate&&) = delete; void start() noexcept { // Begin async work; eventually call set_value/set_error/set_stopped // on the receiver stored in this op-state. } };
start()must benoexcept— there’s nowhere for it to throw to, since the caller is typically the runtime, not user code that can handle exceptions. It must also returnvoid. The dispatch site enforces both with static asserts.tag_invoke-basedcustomization is supported via a deprecated overload, retained for backwards compatibility.See also
stdexec::connect — the CPO that produces operation states
See also
stdexec::operation_state — the concept this CPO drives
See also
stdexec::set_value — one of the completions
starteventually triggersPublic Functions
-
template<class _Op>
inline constexpr void operator()(_Op &__op) const noexcept Begin execution of
__op.Dispatches to
__op.start(). Statically asserts bothnoexceptandvoid-returning.- Template Parameters:
_Op – A type satisfying
stdexec::operation_state.- Parameters:
__op – An lvalue reference to the operation state. Passing an rvalue is ill-formed.
Receiver-side
set_value
-
struct set_value_t : public stdexec::__detail::__completion_tag<__disposition::__value>
Customization point object for the value completion signal of the sender/receiver protocol.
set_value(rcvr, vs...)is the call an operation state makes on its receiver to deliver a successful asynchronous result. It is one of three terminal completion signals (alongside set_error_t and set_stopped_t) that exactly one of will be called on a receiver once a connected operation has started.User code rarely calls
set_valuedirectly — it is invoked from inside operation-state implementations. Sender authors writing new adaptors do call it, and receiver authors provide the matching member that this CPO dispatches to.Customization.
A receiver opts into receiving value completions by defining a
noexcept,void-returningmember:struct my_receiver { using receiver_concept = stdexec::receiver_tag; void set_value(int v) noexcept { ... } // overload set per value type };
At the call site,
stdexec::set_value(rcvr, vs...)dispatches torcvr.set_value(vs...), statically asserting both that the member isnoexceptand that it returnsvoid.See [exec.recv] in the C++26 working draft.
See also
stdexec::set_error — the error-completion CPO
See also
stdexec::set_stopped — the stopped-completion CPO
See also
stdexec::receiver — the receiver concept this CPO drives
See also
stdexec::receiver_of — receiver plus specific completion signatures
Public Functions
-
template<class _Receiver, class ..._As>
inline constexpr void operator()(_Receiver &&__rcvr, _As&&... __as) const noexcept Deliver a value completion to
__rcvr.Dispatches to
__rcvr.set_value(__as...). The static asserts inside enforce that the member isnoexceptand that it returnsvoid— the two non-negotiable properties of every completion signal.- Template Parameters:
_Receiver – A type whose decayed form satisfies
stdexec::receiverand has a matching.set_value(_As...)member._As – The value-datum argument types.
-
template<class _Receiver, class ..._As>
-
set_value_t const stdexec::set_value
The customization point object for delivering a value completion.
set_valueis an instance of set_value_t. See set_value_t for the full description and customization rules.
set_error
-
struct set_error_t : public stdexec::__detail::__completion_tag<__disposition::__error>
Customization point object for the error completion signal of the sender/receiver protocol.
set_error(rcvr, e)is the call an operation state makes on its receiver to deliver a failure. Unlike a thrown exception, the error is a typed datum — receivers may distinguish, say,std::exception_ptrfromstd::error_codefrom a domain-specific error enum by overloading on the argument type.Customization.
A receiver opts into receiving error completions of a given type
Eby defining anoexcept,void-returningmember:struct my_receiver { using receiver_concept = stdexec::receiver_tag; void set_error(std::exception_ptr e) noexcept { ... } void set_error(std::error_code e) noexcept { ... } // multiple OK };
Like
set_value, the dispatch site enforcesnoexceptandvoidreturn via static asserts.Receivers MUST accept exactly one error completion at runtime. That is: at most one of
set_value,set_error,set_stoppedis ever called on a given receiver, exactly once.See [exec.recv] in the C++26 working draft.
See also
See also
See also
Public Functions
-
template<class _Receiver, class _Error>
inline constexpr void operator()(_Receiver &&__rcvr, _Error &&__err) const noexcept Deliver an error completion to
__rcvr.Dispatches to
__rcvr.set_error(__err). Statically asserts bothnoexceptandvoid-returning.- Template Parameters:
_Receiver – A type with a matching
.set_error(_Error)member._Error – The error datum type.
-
template<class _Receiver, class _Error>
-
set_error_t const stdexec::set_error
The customization point object for delivering an error completion.
set_erroris an instance of set_error_t. See set_error_t for the full description and customization rules.
set_stopped
-
struct set_stopped_t : public stdexec::__detail::__completion_tag<__disposition::__stopped>
Customization point object for the stopped completion signal of the sender/receiver protocol.
set_stopped(rcvr)is the call an operation state makes on its receiver to report that the operation was cancelled. It carries no datum— the stopped channel is informational only (“we are
ending early because cancellation was requested or because no result
is needed any more”).
Cancellation is cooperative: receivers can request stop via the stop token in their environment (see
stdexec::get_stop_token); senders that observe such a request may complete withset_stoppedinstead ofset_valueorset_error.Customization.
A receiver opts into receiving stopped completions by defining a
noexcept,void-returningnullary member:struct my_receiver { using receiver_concept = stdexec::receiver_tag; void set_stopped() noexcept { ... } };
See [exec.recv] in the C++26 working draft.
See also
See also
See also
stdexec::get_stop_token — the receiver-environment query for the stop token
Public Functions
-
template<class _Receiver>
inline constexpr void operator()(_Receiver &&__rcvr) const noexcept Deliver a stopped completion to
__rcvr.Dispatches to
__rcvr.set_stopped(). Statically asserts bothnoexceptandvoid-returning.- Template Parameters:
_Receiver – A type with a matching nullary
.set_stopped()member.
-
template<class _Receiver>
-
set_stopped_t const stdexec::set_stopped
The customization point object for delivering a stopped completion.
set_stoppedis an instance of set_stopped_t. See set_stopped_t for the full description and customization rules.
get_env
-
struct get_env_t
Customization point object that obtains the environment of a sender or receiver.
Every sender and every receiver has an associated environment — an unordered, type-keyed bag of properties such as the stop token, the allocator, the preferred scheduler, the start scheduler, and any domain-specific properties a sender adaptor wants to expose. The environment is what makes the sender model contextual: a sender adapts its behavior based on the environment of the receiver it is connected to.
get_env(provider)returns the environment ofprovider, whereprovideris either a receiver (yielding its environment, which the sender will introspect via queries) or a sender (yielding its attributes, which the framework consults to determine things like the sender’s completion scheduler).See [exec.queries.get_env] in the C++26 working draft.
Customization.
Most receivers and senders simply expose a
noexcept, const-callable.get_env()member returning their environment:struct my_receiver { using receiver_concept = stdexec::receiver_tag; auto get_env() const noexcept { return stdexec::env{stdexec::prop{stdexec::get_stop_token, my_stop_token_}}; } };
Many receivers don’t have any properties to expose — for those, the
get_envmember can simply return an emptystdexec::env<>(or theget_envCPO will return one automatically via its__ignoreoverload).tag_invoke-basedcustomization is supported via a deprecated overload, retained for backwards compatibility.Environment queries.
Once you have an environment, you query it by calling the appropriate query CPO on it:
get_stop_token(env),get_allocator(env),get_scheduler(env), etc. Each query is a separate CPO; the environment dispatches based on the query’s type. Inside a sender pipeline you almost always reach forstdexec::read_env(or its helpers likeget_stop_token()with no argument) rather than callingget_envdirectly.See also
stdexec::env — the environment container type
See also
stdexec::read_env — the sender factory that exposes env values to pipelines
See also
stdexec::get_stop_token — example of an environment query CPO
See also
stdexec::get_allocator
See also
stdexec::get_scheduler
Public Functions
-
template<class _EnvProvider>
inline constexpr auto operator()(_EnvProvider const &__env_provider) const noexcept -> __detail::__get_env_member_result_t<_EnvProvider const&> Obtain the environment of
__env_provider.Dispatches to
__env_provider.get_env(), statically asserting that the member isnoexcept.- Template Parameters:
_EnvProvider – A type whose const-lvalue has a
.get_env() constmember.- Parameters:
__env_provider – The receiver or sender whose environment to retrieve.
- Returns:
The environment object (typed as defined by the provider).
-
template<class _EnvProvider>
Sender Factories
A sender factory is an algorithm that produces a sender from non-sender inputs (values, an error, a scheduler, an environment query). Factories sit at the head of a sender pipeline.
just — produce a sender from values
Produces a sender that synchronously completes with the given values on the value channel. The canonical way to inject literal values into a sender pipeline. See just — inject literal values for an approachable introduction with worked examples.
-
struct just_t
A sender factory that produces a sender which completes synchronously with the given values on the value channel.
justis the simplest sender factory: it captures zero or more values and produces a sender that, when connected and started, immediately delivers those values viaset_valueto the connected receiver — all within the receiver’sstart()call, without any context transition. It is the canonical way to inject literal values into a sender pipeline.auto s0 = stdexec::just(); // value-completes with no datums auto s1 = stdexec::just(42); // value-completes with one int auto s2 = stdexec::just(1, 2, 3); // value-completes with three ints auto s3 = stdexec::just(std::string{"x"}, 7); // mixed types are fine
See [exec.just] in the C++26 working draft for the normative specification.
Completion signatures.
Given
just(ts...)with argument-pack typesTs…, the resulting sender has the single completion signature:set_value_t(std::decay_t<Ts>...)
Each argument is decay-copied into the resulting sender. The error and stopped channels are empty —
justnever completes withset_errororset_stopped.Exception behavior.
The factory call itself is
noexceptwhen every decay-copy isnoexcept. The produced sender’sstart()isnoexceptby construction.Cancellation.
justdoes not consult the receiver’s stop token; it always synchronously completes withset_value.Example.
#include <stdexec/execution.hpp> #include <cassert> int main() { using namespace stdexec; auto sndr = just(21) | then([](int x) { return x * 2; }); auto [v] = sync_wait(std::move(sndr)).value(); assert(v == 42); }
See also
stdexec::just_error — synchronously complete with an error
See also
stdexec::just_stopped — synchronously complete with stopped
See also
stdexec::read_env — synchronously complete with a value read from the environment
Public Functions
-
template<__movable_value... _Ts>
inline constexpr auto operator()(_Ts&&... __ts) const noexcept(__nothrow_decay_copyable<_Ts...>) Construct a sender that synchronously value-completes with the decay-copies of
__ts….- Template Parameters:
_Ts – Zero or more types each satisfying the internal
__movable_valueconcept.- Parameters:
__ts – The values to deliver. Each is decay-copied into the resulting sender.
- Returns:
A sender with the single completion signature
set_value_t(std::decay_t<_Ts>...).
-
template<__movable_value... _Ts>
just_error — produce a sender from an error
Produces a sender that synchronously completes with the given error on the error channel. Mostly useful for testing error-handling adaptors.
-
struct just_error_t
A sender factory that produces a sender which completes synchronously with the given error on the error channel.
just_erroris the error-channel analogue ofjust:it captures a single error datum and produces a sender that, when connected and started, immediately delivers that error viaset_errorto the connected receiver. It is the canonical way to inject a literal error into a sender pipeline — useful for testing error-handling adaptors such asupon_errorandlet_error.auto s1 = stdexec::just_error(std::error_code{ENOENT, std::system_category()}); auto s2 = stdexec::just_error(std::make_exception_ptr(std::runtime_error{"boom"}));
See [exec.just] in the C++26 working draft for the normative specification (
just_erroris specified alongsidejustandjust_stopped).Completion signatures.
Given
just_error(e)withE=decltype((e)), the resulting sender has the single completion signature:set_error_t(std::decay_t<E>)
The error is decay-copied into the resulting sender. The value and stopped channels are empty.
Cancellation.
just_errordoes not consult the receiver’s stop token; it always synchronously completes withset_error.See also
stdexec::just — synchronously complete with values
See also
stdexec::just_stopped — synchronously complete with stopped
See also
stdexec::upon_error — handle the error channel
See also
stdexec::let_error — handle the error channel with a sender-returning function
Public Functions
-
template<__movable_value _Error>
inline constexpr auto operator()(_Error &&__err) const noexcept(__nothrow_decay_copyable<_Error>) Construct a sender that synchronously error-completes with the decay-copy of
__err.- Template Parameters:
_Error – A type satisfying the internal
__movable_valueconcept.- Parameters:
__err – The error datum to deliver. Decay-copied into the sender.
- Returns:
A sender with the single completion signature
set_error_t(std::decay_t<_Error>).
-
template<__movable_value _Error>
-
just_error_t const stdexec::just_error
The customization point object for the
just_errorsender factory.just_erroris an instance of just_error_t. See just_error_t for the full description, completion signatures, and a usage example.
just_stopped — produce a stopped sender
Produces a sender that synchronously completes on the stopped channel. Mostly useful for testing cancellation-handling adaptors.
-
struct just_stopped_t
A sender factory that produces a sender which completes synchronously on the stopped channel.
just_stoppedis the stopped-channel analogue ofjust:it produces a sender that, when connected and started, immediately invokesset_stoppedon the connected receiver. It carries no datum (the stopped channel has none). It is the canonical way to inject a literal cancellation into a sender pipeline — useful for testing cancellation handling adaptors such asupon_stoppedandlet_stopped.auto s = stdexec::just_stopped();
See [exec.just] in the C++26 working draft for the normative specification (
just_stoppedis specified alongsidejustandjust_error).Completion signatures.
The resulting sender has the single completion signature:
set_stopped_t()
The value and error channels are empty.
Cancellation.
just_stoppeddoes not consult the receiver’s stop token (the cancellation it delivers is unconditional, not a response to a request).See also
stdexec::just — synchronously complete with values
See also
stdexec::just_error — synchronously complete with an error
See also
stdexec::upon_stopped — handle the stopped channel
See also
stdexec::let_stopped — handle the stopped channel with a sender-returning function
Public Functions
-
template<class _Tag = just_stopped_t>
inline constexpr auto operator()() const noexcept Construct a sender that synchronously stops-completes.
- Returns:
A sender with the single completion signature
set_stopped_t().
-
template<class _Tag = just_stopped_t>
-
just_stopped_t const stdexec::just_stopped
The customization point object for the
just_stoppedsender factory.just_stoppedis an instance of just_stopped_t. See just_stopped_t for the full description, completion signatures, and a usage example.
read_env — produce a sender from an environment query
Produces a sender whose value completion is the result of querying the
connected receiver’s environment with a given query CPO. It is the
primitive behind the standard get_stop_token(), get_allocator(),
get_scheduler() helpers. See read_env — read a value from the receiver’s environment for an
approachable introduction.
-
__read_env_t const stdexec::read_env
A sender factory that produces a sender whose value completion is the result of querying the receiver’s environment.
read_envreaches into the receiver to read a value associated with a query CPO — things like the receiver’s stop token, its associated allocator, or its preferred scheduler. The resulting sender, when connected and started, evaluatesq(get_env(rcvr))and delivers the result viaset_valueto the connected receiver.It is the primitive used by the standard environment-query helpers such as
get_stop_token(),get_allocator(),get_scheduler(), andget_delegation_scheduler()— each of those is simplyread_envapplied to the corresponding query CPO.The call form takes a query CPO (not a value):
auto sndr = stdexec::read_env(stdexec::get_stop_token);
See [exec.read.env] in the C++26 working draft for the normative specification.
Completion signatures.
Given
read_env(q)and an environment typeEnv(taken from the connected receiver), the resulting sender has completion signatures:set_value_t(decltype(q(declval<Env>()))) // always present set_error_t(std::exception_ptr) // present iff q(env) may throw
The query result type is taken from the actual environment at connect time, so the same
read_envsender may have different concrete completion signatures depending on which receiver it is connected to.If the environment does not provide a value for
q(i.e.q(env)is ill-formed or returnsvoid), the program is ill-formed at the point where the sender is connected, with a diagnostic that names the offending query.Exception behavior.
If invoking
qon the receiver’s environment throws, the exception is delivered throughset_error_t(std::exception_ptr). Ifqisnoexcept(typical for query CPOs), nostd::exception_ptrerror completion is added.Cancellation.
read_envdoes not consult the receiver’s stop token; it completes synchronously in itsstart.Example.
#include <stdexec/execution.hpp> using namespace stdexec; // Lift the current stop token into the pipeline so a downstream // algorithm can inspect it: auto sndr = read_env(get_stop_token) | then([](auto tok) { return tok.stop_requested(); });
See also
stdexec::just — synchronously complete with literal values
See also
stdexec::get_stop_token — equivalent to
read_env(get_stop_token)See also
stdexec::get_scheduler — equivalent to
read_env(get_scheduler)
schedule — produce a sender from a scheduler
Produces a sender that, when connected and started, value-completes from the context of the given scheduler. It is the bridge between the scheduler and sender abstractions, and the way to begin a pipeline that must run on a specific execution context. See schedule — start a pipeline on a scheduler for an approachable introduction.
-
struct schedule_t
A sender factory that obtains a sender from a scheduler.
scheduleis the bridge between a scheduler (a lightweight handle to an execution context) and the sender world: it produces a sender that, when connected and started, eventually completes withset_valueto the connected receiver from the context of the scheduler. It is the canonical way to begin a sender pipeline that needs to run on a specific execution context (a thread pool, a GPU stream, an event loop, …).auto sched = stdexec::get_parallel_scheduler(); auto sndr = stdexec::schedule(sched);
The sender returned by
schedule(sched)is informally called a “schedule-sender” or “schedule sender”. The expressionschedule(sch)is expression-equivalent tosch.schedule(), which is the customization point that scheduler authors implement.schedulealso drives thestdexec::schedulerconcept: a typeSsatisfiesschedulerif (and roughly only if)schedule(s)is a valid expression returning a sender, andSis equality-comparable and nothrow-move-constructible.See [exec.schedule] in the C++26 working draft for the normative specification.
Completion signatures.
The exact signatures are determined by the scheduler’s implementation, but every conforming schedule-sender includes:
set_value_t() // delivered on the scheduler's context set_stopped_t() // typical: stop-token observed during scheduling set_error_t(...) // implementation-defined; some schedulers can fail
set_valuecarries no datums. The point ofscheduleis the context transition, not the value — downstream adaptors (then,let_value, etc.) chained onto the schedule-sender therefore run on the scheduler’s context.Cancellation.
A schedule-sender that has not yet started executing on the scheduler’s context typically observes the receiver’s stop token and may complete with
set_stoppedinstead. Once it has begun running on the scheduler’s context, the value completion is delivered.Example.
#include <stdexec/execution.hpp> int main() { using namespace stdexec; auto sched = get_parallel_scheduler(); auto sndr = schedule(sched) // hop onto sched | then([] { return 42; }); // ... and compute on it auto [v] = sync_wait(std::move(sndr)).value(); (void)v; }
See also
stdexec::starts_on — start a sender on a given scheduler
See also
stdexec::continues_on — transfer execution to a scheduler mid-pipeline
See also
stdexec::on — execute a sender on a scheduler then return
Public Functions
-
template<class _Scheduler>
inline auto operator()(_Scheduler &&__sched) const noexcept(noexcept(static_cast<_Scheduler&&>(__sched).schedule())) -> decltype(static_cast<_Scheduler&&>(__sched).schedule()) Obtain a schedule-sender by calling
__sched.schedule().- Template Parameters:
_Scheduler – A type whose lvalue has a member function
.schedule() returning a sender.- Parameters:
__sched – The scheduler to obtain a sender from.
- Returns:
The sender produced by
__sched.schedule()— a sender that, when connected and started, value-completes (with no datums) on__sched'sexecution context.- Pre:
decltype(__sched.schedule())must satisfy thestdexec::senderconcept (statically checked).
-
template<class _Scheduler>
inline auto operator()(_Scheduler &&__sched) const noexcept(__nothrow_tag_invocable<schedule_t, _Scheduler>) -> __tag_invoke_result_t<schedule_t, _Scheduler> Deprecated overload: obtain a schedule-sender via
tag_invoke.- Deprecated:
The
tag_invoke-basedcustomization ofscheduleis deprecated in favor of thesched.schedule()member-function form. New scheduler types should provide a member.schedule() instead of atag_invokeoverload forschedule_t.
-
template<class _Scheduler>
-
schedule_t const stdexec::schedule
The customization point object for the
schedulesender factory.scheduleis an instance of schedule_t. See schedule_t for the full description, completion signatures, scheduler-concept relationship, and a usage example.
just_from (experimental) — like just but value-producing via a function
-
constexpr just_from_t experimental::execution::just_from = {}
-
constexpr just_error_from_t experimental::execution::just_error_from = {}
-
constexpr just_stopped_from_t experimental::execution::just_stopped_from = {}
Sender Adaptors
TODO: More sender adaptor algorithms
then — apply a function to the value channel
Transforms a predecessor sender’s value completion by invoking a callable
with the values it produces. See then — transform a value for an approachable
introduction with worked examples; the complete reference (including
completion-signature transformation rules, exception behavior, and the
operator() overloads) follows.
-
struct then_t
A pipeable sender adaptor that transforms a predecessor sender’s value completion by invoking a callable on the values it produces.
thenmaps the value channel of a sender through a function while forwarding the error and stopped channels unchanged. It is the asynchronous analogue of applyingstd::invoketo the result of a synchronous computation, and is the most common sender adaptor in practice; most sender pipelines contain at least onethen.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::then(sndr, f); // direct invocation auto s2 = sndr | stdexec::then(f); // pipe syntax
The two forms are expression-equivalent. See [exec.then] in the C++26 working draft for the normative specification.
Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // one or more value completions set_error_t(Es)... // zero or more error completions set_stopped_t() // optional stopped completion
the sender produced by
then(sndr, f)has completion signaturesset_value_t(R) // R = decltype(std::invoke(f, Vs...)) // (or set_value_t() when R is void) set_error_t(Es)... // forwarded unchanged from sndr set_error_t(std::exception_ptr) // added when invoking f may throw set_stopped_t() // forwarded unchanged from sndr
If
sndrhas multiple value completions,fmust be invocable with every one of them; otherwise the program is ill-formed and the diagnostic surfaces at the point where the resulting sender is connected to a receiver. The resulting value-completion arity is always one: each distinct return typeRfrom invokingfcontributes aset_value_t(R)overload.Exception behavior.
If invoking
fthrows, the exception is delivered throughset_error_t(std::exception_ptr)on the resulting sender. Whenfisnoexceptfor every value-argument pack ofsndr, no additionalstd::exception_ptrerror completion is added.Cancellation.
thendoes not interact with the receiver’s stop token. Whensndrcompletes withset_stopped,fis not invoked and the stopped completion is forwarded to the downstream receiver.Example.
#include <stdexec/execution.hpp> #include <cassert> int main() { using namespace stdexec; auto sndr = just(21) | then([](int x) { return x * 2; }) | then([](int x) { return x + 1; }); auto [v] = sync_wait(std::move(sndr)).value(); assert(v == 43); }
See also
stdexec::upon_error — adapt the error channel
See also
stdexec::upon_stopped — adapt the stopped channel
See also
stdexec::let_value — adapt the value channel with a sender-returning function
Public Functions
-
template<sender _Sender, __movable_value _Fun>
inline constexpr auto operator()(_Sender &&__sndr, _Fun __fun) const -> __well_formed_sender auto Construct a sender that adapts
__sndrby invoking__funwith each value-completion argument pack it produces.- Template Parameters:
_Sender – A type satisfying the
stdexec::senderconcept._Fun – A decayed, move-constructible callable type (satisfying the internal
__movable_valueconcept).
- Parameters:
__sndr – The predecessor sender whose value-completion is to be adapted. Perfect-forwarded into the resulting sender, so an rvalue is moved and an lvalue copied as needed.
__fun – The function (or callable) to invoke with each value-completion of
__sndr. Stored by value (decayed) in the resulting sender.
- Returns:
A sender that, when connected to a receiver and started, drives
__sndrand routes each of its value-completions through__fun. The error and stopped channels are forwarded unchanged.- Pre:
__funmust be invocable with every value-completion argument pack of__sndr(with appropriate value categories). Otherwise the program is ill-formed at the point where the resulting sender is connected to a receiver.
-
template<__movable_value _Fun>
inline constexpr auto operator()(_Fun __fun) const Construct a sender-adaptor closure that, when applied to a sender, produces
then(sndr, __fun).This overload enables the pipe syntax:
sndr | then(__fun)is equivalent tothen(sndr, __fun).- Template Parameters:
_Fun – A decayed, move-constructible callable type (satisfying the internal
__movable_valueconcept).- Parameters:
__fun – The callable to invoke on the predecessor’s value completions when the closure is later applied to a sender.
- Returns:
A sender-adaptor closure object that captures
__funby value. When piped against a sendersndr, it yields the senderthen(sndr, std::move(__fun)).
-
template<sender _Sender, __movable_value _Fun>
upon_error — handle the error channel
Handles a predecessor sender’s error completion by invoking a callable on the error datum and delivering the result as a value completion — the canonical way to recover from an error and continue the pipeline. See upon_error — recover from an error for an approachable introduction with worked examples.
-
struct upon_error_t
A pipeable sender adaptor that handles a predecessor sender’s error completion by invoking a callable on the error datum.
upon_errormaps the error channel of a sender through a function while forwarding the value and stopped channels unchanged. The function’s return value becomes a value completion on the resulting sender — soupon_erroris the canonical way to recover from an error: turn it into a substitute value and continue the pipeline as if nothing had gone wrong.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::upon_error(sndr, f); // direct invocation auto s2 = sndr | stdexec::upon_error(f); // pipe syntax
The two forms are expression-equivalent. See [exec.then] in the C++26 working draft for the normative specification (
upon_erroris specified alongsidethenandupon_stopped).Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // forwarded unchanged set_error_t(Es)... // one or more error completions set_stopped_t() // forwarded unchanged (if present)
the sender produced by
upon_error(sndr, f)has completion signaturesset_value_t(Vs...) // forwarded unchanged from sndr set_value_t(R) // R = decltype(std::invoke(f, E)) for each E // (or set_value_t() when R is void) set_error_t(std::exception_ptr) // added when invoking f may throw set_stopped_t() // forwarded unchanged from sndr
For each distinct error type
Ethatsndrmay complete with,fmust be invocable withE(with appropriate value category). Otherwise the program is ill-formed at the point where the resulting sender is connected to a receiver. All originalset_error_tcompletions are replaced by the union of value completions produced byf— only errors thrown byfitself remain on the error channel.Exception behavior.
If invoking
fthrows, the exception is delivered throughset_error_t(std::exception_ptr)on the resulting sender. Whenfisnoexceptfor every error type ofsndr, nostd::exception_ptrerror completion is added.Cancellation.
upon_errordoes not interact with the receiver’s stop token. Whensndrcompletes withset_stopped,fis not invoked and the stopped completion is forwarded to the downstream receiver.Example.
#include <stdexec/execution.hpp> #include <cassert> int main() { using namespace stdexec; auto sndr = just_error(std::error_code{ENOENT, std::system_category()}) | upon_error([](std::error_code) { return -1; }); auto [v] = sync_wait(std::move(sndr)).value(); assert(v == -1); }
See also
stdexec::then — adapt the value channel
See also
stdexec::upon_stopped — adapt the stopped channel
See also
stdexec::let_error — adapt the error channel with a sender-returning function
Public Functions
-
template<sender _Sender, __movable_value _Fun>
inline constexpr auto operator()(_Sender &&__sndr, _Fun __fun) const -> __well_formed_sender auto Construct a sender that handles each error completion of
__sndrby invoking__funon the error datum.- Template Parameters:
_Sender – A type satisfying the
stdexec::senderconcept._Fun – A decayed, move-constructible callable type (satisfying the internal
__movable_valueconcept).
- Parameters:
__sndr – The predecessor sender whose error-completions are to be adapted. Perfect-forwarded into the resulting sender.
__fun – The function (or callable) to invoke with each error-completion datum of
__sndr. Stored by value (decayed) in the resulting sender.
- Returns:
A sender that, when connected to a receiver and started, drives
__sndrand routes each of its error-completions through__fun, delivering the result on the value channel. The value and stopped channels of__sndrare forwarded unchanged.- Pre:
__funmust be invocable with every error type of__sndr(with appropriate value categories). Otherwise the program is ill-formed at the point where the resulting sender is connected to a receiver.
-
template<__movable_value _Fun>
inline constexpr auto operator()(_Fun __fun) const noexcept(__nothrow_move_constructible<_Fun>) Construct a sender-adaptor closure that, when applied to a sender, produces
upon_error(sndr, __fun).This overload enables the pipe syntax:
sndr | upon_error(__fun)is equivalent toupon_error(sndr, __fun).- Template Parameters:
_Fun – A decayed, move-constructible callable type.
- Parameters:
__fun – The callable to invoke on the predecessor’s error completions when the closure is later applied to a sender.
- Returns:
A sender-adaptor closure object that captures
__funby value. When piped against a sendersndr, it yields the senderupon_error(sndr, std::move(__fun)).
-
template<sender _Sender, __movable_value _Fun>
-
upon_error_t const stdexec::upon_error
The customization point object for the
upon_errorsender adaptor.upon_erroris an instance of upon_error_t. See upon_error_t for the full description, completion-signature transformation rules, exception and cancellation behavior, and a usage example.
upon_stopped — handle the stopped channel
Handles a predecessor sender’s stopped completion by invoking a nullary callable and delivering its return value as a value completion — the canonical way to recover from cancellation. See upon_stopped — recover from cancellation for an approachable introduction with worked examples.
-
struct upon_stopped_t
A pipeable sender adaptor that handles a predecessor sender’s stopped completion by invoking a nullary callable.
upon_stoppedmaps the stopped channel of a sender into a value completion by invoking a callable with no arguments and forwarding its return value downstream. The value and error channels are forwarded unchanged. This is the canonical way to recover from cancellation: turn a stopped completion into a substitute value and continue.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::upon_stopped(sndr, f); // direct invocation auto s2 = sndr | stdexec::upon_stopped(f); // pipe syntax
The two forms are expression-equivalent. See [exec.then] in the C++26 working draft for the normative specification (
upon_stoppedis specified alongsidethenandupon_error).Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // forwarded unchanged set_error_t(Es)... // forwarded unchanged set_stopped_t() // (must be present)
the sender produced by
upon_stopped(sndr, f)has completion signaturesset_value_t(Vs...) // forwarded unchanged from sndr set_value_t(R) // R = decltype(std::invoke(f)) // (or set_value_t() when R is void) set_error_t(Es)... // forwarded unchanged from sndr set_error_t(std::exception_ptr) // added when invoking f may throw // (no set_stopped_t in the output)
fmust be invocable with no arguments — this requirement is enforced by therequiresclause on the operator overloads. The originalset_stopped_tcompletion is consumed: the resulting sender will never complete viaset_stopped(unlessfitself returns a sender that does, whichupon_stoppeddoes not — seelet_stoppedfor that).Exception behavior.
If invoking
fthrows, the exception is delivered throughset_error_t(std::exception_ptr)on the resulting sender. Whenfisnoexcept, nostd::exception_ptrerror completion is added.Cancellation.
upon_stoppeddoes not interact with the receiver’s stop token. It reacts to the predecessor’sset_stoppedby invokingf; it does not itself initiate cancellation.Example.
#include <stdexec/execution.hpp> #include <cassert> int main() { using namespace stdexec; auto sndr = just_stopped() | upon_stopped([] { return 42; }); auto [v] = sync_wait(std::move(sndr)).value(); assert(v == 42); }
See also
stdexec::then — adapt the value channel
See also
stdexec::upon_error — adapt the error channel
See also
stdexec::let_stopped — adapt the stopped channel with a sender-returning function
Public Functions
-
template<sender _Sender, __movable_value _Fun>
inline auto operator()(_Sender &&__sndr, _Fun __fun) const -> __well_formed_sender auto Construct a sender that handles a stopped completion of
__sndrby invoking__funand delivering its return value.- Template Parameters:
_Sender – A type satisfying the
stdexec::senderconcept._Fun – A decayed, move-constructible, nullary callable type.
- Parameters:
__sndr – The predecessor sender whose stopped completion is to be adapted. Perfect-forwarded into the resulting sender.
__fun – The function (or callable) to invoke when
__sndrcompletes viaset_stopped. Stored by value (decayed) in the resulting sender.
- Returns:
A sender that, when connected to a receiver and started, drives
__sndrand reacts to itsset_stoppedcompletion by invoking__funand forwarding the result viaset_value. The value and error channels of__sndrare forwarded unchanged.- Pre:
__funmust be invocable with no arguments (therequiresclause enforces this). Otherwise the call is not viable.
-
template<__movable_value _Fun>
inline auto operator()(_Fun __fun) const noexcept(__nothrow_move_constructible<_Fun>) Construct a sender-adaptor closure that, when applied to a sender, produces
upon_stopped(sndr, __fun).This overload enables the pipe syntax:
sndr | upon_stopped(__fun)is equivalent toupon_stopped(sndr, __fun).- Template Parameters:
_Fun – A decayed, move-constructible, nullary callable type.
- Parameters:
__fun – The callable to invoke on the predecessor’s stopped completion when the closure is later applied to a sender.
- Returns:
A sender-adaptor closure object that captures
__funby value. When piped against a sendersndr, it yields the senderupon_stopped(sndr, std::move(__fun)).
-
template<sender _Sender, __movable_value _Fun>
-
upon_stopped_t const stdexec::upon_stopped
The customization point object for the
upon_stoppedsender adaptor.upon_stoppedis an instance of upon_stopped_t. See upon_stopped_t for the full description, completion-signature transformation rules, exception and cancellation behavior, and a usage example.
let_value — chain a sender-returning function on the value channel
Chains a sender-returning function onto a predecessor’s value completion. The returned sender is connected and started, and its completions become the completions of the overall pipeline. This is the way to launch another asynchronous operation based on a predecessor’s values. See let_value — chain another async operation for an approachable introduction with worked examples.
-
struct let_value_t : public stdexec::__let::__let_t<let_value_t>
A pipeable sender adaptor that chains a sender-returning function onto a predecessor’s value completion.
let_valueis the way to launch another asynchronous operation based on the values produced by a predecessor sender. Where then takes a function returning a value,let_valuetakes a function returning a sender, which is then connected and started, becoming the tail of the pipeline.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::let_value(sndr, f); // direct invocation auto s2 = sndr | stdexec::let_value(f); // pipe syntax
The signature of the operator overloads (inherited from a detail base) is:
template <sender Sender, movable-value Fun> auto operator()(Sender&& sndr, Fun fun) const -> sender auto; // direct template <class Fun> auto operator()(Fun fun) const; // closure
The two forms are expression-equivalent. See [exec.let] in the C++26 working draft for the normative specification.
Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // one or more value completions set_error_t(Es)... // forwarded unchanged set_stopped_t() // forwarded unchanged (if present)
the sender produced by
let_value(sndr, f)has completion signatures equal to the union of:the completion signatures of every sender returned by an invocation
std::invoke(f, vs...)for each value-completion argument pack(vs…) ofsndr, plusthe
set_error_tcompletions ofsndr(forwarded unchanged),the
set_stopped_tcompletion ofsndr(forwarded unchanged), andset_error_t(std::exception_ptr)if invokingfor connecting its returned sender may throw.
fmust be invocable with every value-completion argument pack ofsndr, and every such invocation must return a type satisfying thesenderconcept. Otherwise the program is ill-formed at the point where the resulting sender is connected to a receiver.Use then vs.
let_value:choosethenwhen the callable returns a value; chooselet_valuewhen the callable returns a sender (i.e. when the next step is itself asynchronous). Passing a sender-returning function tothenwould forward the sender as a value — almost never what you want.Exception behavior.
If invoking
fthrows, or if connecting the sender returned byfthrows, the exception is delivered throughset_error_t(std::exception_ptr)on the resulting sender. When both steps arenoexcept, nostd::exception_ptrerror completion is added.Cancellation.
let_valuedoes not introduce stop-token interaction of its own. Ifsndrcompletes withset_stopped,fis not invoked and the stopped completion is forwarded.Example.
#include <stdexec/execution.hpp> #include <cassert> int main() { using namespace stdexec; auto fetch_async = [](int id) { return just(id * 10); // pretend this is a non-trivial async op }; auto sndr = just(7) | let_value(fetch_async); auto [v] = sync_wait(std::move(sndr)).value(); assert(v == 70); }
See also
stdexec::then — adapt the value channel with a value-returning function
See also
stdexec::let_error — adapt the error channel with a sender-returning function
See also
stdexec::let_stopped — adapt the stopped channel with a sender-returning function
-
let_value_t const stdexec::let_value
The customization point object for the
let_valuesender adaptor.let_valueis an instance of let_value_t. See let_value_t for the full description, completion-signature transformation rules, exception and cancellation behavior, and a usage example.
let_error — chain a sender-returning function on the error channel
Chains a sender-returning function onto a predecessor’s error completion — the way to launch another asynchronous operation to recover from an error.
-
struct let_error_t : public stdexec::__let::__let_t<let_error_t>
A pipeable sender adaptor that chains a sender-returning function onto a predecessor’s error completion.
let_erroris to upon_error what let_value is to then: it lets the recovery step be another asynchronous operation. When the predecessor completes withset_error_t(e),fis invoked witheand is expected to return a sender; that sender is connected and started, and its completions become the completions of the overall pipeline.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::let_error(sndr, f); // direct invocation auto s2 = sndr | stdexec::let_error(f); // pipe syntax
See [exec.let] in the C++26 working draft for the normative specification.
Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // forwarded unchanged set_error_t(Es)... // one or more error completions set_stopped_t() // forwarded unchanged (if present)
the sender produced by
let_error(sndr, f)has completion signatures equal to the union of:the
set_value_tcompletions ofsndr(forwarded unchanged),the completion signatures of every sender returned by an invocation
std::invoke(f, e)for each error typeeofsndr,the
set_stopped_tcompletion ofsndr(forwarded unchanged), andset_error_t(std::exception_ptr)if invokingfor connecting its returned sender may throw.
All original
set_error_tcompletions are consumed: only errors produced by the senders thatfreturns (or thrown byfitself) survive on the error channel.Use upon_error vs.
let_error:chooseupon_errorwhen the recovery is synchronous (returns a value); chooselet_errorwhen the recovery is itself asynchronous (returns a sender).Exception behavior.
If invoking
fthrows, or if connecting the sender returned byfthrows, the exception is delivered throughset_error_t(std::exception_ptr)on the resulting sender.Cancellation.
let_errordoes not introduce stop-token interaction of its own. Aset_stoppedcompletion ofsndris forwarded without invokingf.Example.
#include <stdexec/execution.hpp> #include <cassert> #include <system_error> int main() { using namespace stdexec; auto retry_async = [](std::error_code) { return just(7); }; auto sndr = just_error(std::error_code{ENOENT, std::system_category()}) | let_error(retry_async); auto [v] = sync_wait(std::move(sndr)).value(); assert(v == 7); }
See also
stdexec::upon_error — adapt the error channel with a value-returning function
See also
stdexec::let_value — adapt the value channel with a sender-returning function
See also
stdexec::let_stopped — adapt the stopped channel with a sender-returning function
-
let_error_t const stdexec::let_error
The customization point object for the
let_errorsender adaptor.let_erroris an instance of let_error_t. See let_error_t for the full description and example.
let_stopped — chain a sender-returning function on the stopped channel
Chains a sender-returning nullary function onto a predecessor’s stopped completion — the way to launch another asynchronous operation to recover from cancellation.
-
struct let_stopped_t : public stdexec::__let::__let_t<let_stopped_t>
A pipeable sender adaptor that chains a sender-returning nullary function onto a predecessor’s stopped completion.
let_stoppedis to upon_stopped what let_value is to then: it lets the recovery from cancellation be another asynchronous operation. When the predecessor completes withset_stopped,fis invoked with no arguments and is expected to return a sender; that sender is connected and started, and its completions become the completions of the overall pipeline.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::let_stopped(sndr, f); // direct invocation auto s2 = sndr | stdexec::let_stopped(f); // pipe syntax
See [exec.let] in the C++26 working draft for the normative specification.
Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // forwarded unchanged set_error_t(Es)... // forwarded unchanged set_stopped_t() // (must be present)
the sender produced by
let_stopped(sndr, f)has completion signatures equal to the union of:the
set_value_tcompletions ofsndr(forwarded unchanged),the
set_error_tcompletions ofsndr(forwarded unchanged),the completion signatures of the sender returned by
std::invoke(f), andset_error_t(std::exception_ptr)if invokingfor connecting its returned sender may throw.
The original
set_stopped_tcompletion is consumed: it appears on the resulting sender only if the sender returned byfitself completes viaset_stopped.Use upon_stopped vs.
let_stopped:chooseupon_stoppedwhen the recovery is synchronous (returns a value); chooselet_stoppedwhen the recovery is itself asynchronous (returns a sender).Exception behavior.
If invoking
fthrows, or if connecting the sender returned byfthrows, the exception is delivered throughset_error_t(std::exception_ptr)on the resulting sender.Cancellation.
let_stoppedreacts to the predecessor’sset_stopped; it does not initiate cancellation. The sender returned byfsees the receiver’s stop token as normal.Example.
#include <stdexec/execution.hpp> #include <cassert> int main() { using namespace stdexec; auto fallback_async = [] { return just(42); }; auto sndr = just_stopped() | let_stopped(fallback_async); auto [v] = sync_wait(std::move(sndr)).value(); assert(v == 42); }
See also
stdexec::upon_stopped — adapt the stopped channel with a value-returning function
See also
stdexec::let_value — adapt the value channel with a sender-returning function
See also
stdexec::let_error — adapt the error channel with a sender-returning function
-
let_stopped_t const stdexec::let_stopped
The customization point object for the
let_stoppedsender adaptor.let_stoppedis an instance of let_stopped_t. See let_stopped_t for the full description and example.
Scheduling adaptors
These adaptors move work between execution contexts. starts_on begins a
sender on a new scheduler; continues_on transfers execution to a new
scheduler after a sender completes; on runs work on a different
scheduler and then returns to where it started. See
starts_on vs. continues_on vs. on: which one? for a side-by-side comparison.
starts_on — run a sender on a scheduler
Produces a sender that runs an input sender starting on a given scheduler’s execution resource. The completion is delivered on that same resource (no round-trip back).
-
struct starts_on_t
A sender adaptor that runs a sender starting on the execution resource associated with a given scheduler.
starts_ontakes a schedulerschedand a sendersndrand produces a sender that, when connected and started, first hops ontosched'sexecution resource and then runssndrthere. The completions of the produced sender are delivered to the connected receiver fromsched'sresource — there is no “round trip” back to whatever scheduler started the operation (compare on_t).Unlike most sender adaptors in stdexec,
starts_onhas no pipe form: it is always called asstarts_on(sched, sndr), neversndr | starts_on(sched). This reflects the spec:starts_ontakes the scheduler first, mirroring the order of operations (schedule, then run).auto s = stdexec::starts_on(some_sched, sndr);
See [exec.starts.on] in the C++26 working draft for the normative specification.
Equivalence.
Semantically,
starts_on(sch, sndr)is equivalent toschedule(sch) | let_value([sndr = std::forward<Sndr>(sndr)] { return std::move(sndr); })
stdexec’s implementation is structured differently for efficiency on GPU contexts (it avoids making
sndrdependent on the schedule-completion), but the observable semantics match.Completion signatures.
The resulting sender’s completion signatures are essentially those of
sndr, augmented with any error completion that the scheduling step itself can produce:// (signatures of sndr) ... set_error_t(/* scheduler error type, if scheduling may fail */) set_stopped_t() // if not already present
If scheduling onto
schfails, an error completion is delivered to the receiver on an unspecified execution agent.Cancellation.
If the receiver requests stop before scheduling has produced its value-completion, the resulting sender typically completes via
set_stoppedrather than startingsndrat all — the precise behavior depends on the scheduler.Example.
#include <stdexec/execution.hpp> int main() { using namespace stdexec; auto sched = get_parallel_scheduler(); // Run the entire chain on `sched`: auto sndr = starts_on(sched, just(21) | then([](int x) { return x * 2; })); auto [v] = sync_wait(std::move(sndr)).value(); (void)v; // == 42, computed on `sched` }
See also
stdexec::schedule — the primitive that produces a schedule-sender
See also
stdexec::continues_on — transfer to a scheduler after a sender completes
See also
stdexec::on — run on a scheduler then transfer back to the original
Public Functions
-
template<scheduler _Scheduler, sender _Sender>
inline constexpr auto operator()(_Scheduler &&__sched, _Sender &&__sndr) const -> __well_formed_sender auto Construct a sender that runs
__sndron__sched'sexecution resource.- Template Parameters:
_Scheduler – A type satisfying the
stdexec::schedulerconcept._Sender – A type satisfying the
stdexec::senderconcept.
- Parameters:
__sched – The scheduler whose execution resource will host
__sndr.__sndr – The sender to run on
__sched.
- Returns:
A sender that, when connected to a receiver and started, first schedules onto
__sched, then connects and starts__sndrthere, and forwards__sndr'scompletion to the receiver.
-
template<scheduler _Scheduler, sender _Sender>
-
starts_on_t const stdexec::starts_on
The customization point object for the
starts_onsender adaptor.starts_onis an instance of starts_on_t. See starts_on_t for the full description, completion signatures, and a usage example.
continues_on — transfer to a scheduler after completion
Produces a sender that runs the input sender to completion, then transfers
execution to a given scheduler’s resource before delivering the
completion downstream. Anything chained after continues_on runs on
the new scheduler.
-
struct continues_on_t
A pipeable sender adaptor that transfers a predecessor sender’s completion to a different scheduler’s execution resource.
continues_onlets a sender pipeline change execution context in the middle. Given a predecessor sendersndrand a schedulersched,continues_onproduces a sender that, when connected and started, runssndrto completion on whatever contextsndrran on, then transfers execution tosched'sresource, and only then forwardssndr'scompletion (value, error, or stopped) to the connected receiver. Anything chained aftercontinues_ontherefore runs onsched.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::continues_on(sndr, sched); // direct invocation auto s2 = sndr | stdexec::continues_on(sched); // pipe syntax
The two forms are expression-equivalent. See [exec.continues.on] in the C++26 working draft for the normative specification.
Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // forwarded — but delivered on `sched`'s resource set_error_t(Es)... // forwarded — but delivered on `sched`'s resource set_stopped_t() // forwarded — but delivered on `sched`'s resource
the sender produced by
continues_on(sndr, sched)has the same completion signatures assndr, except that aset_error_t(std::exception_ptr)completion may be added if any ofsndr'scompletion datums are notnothrowdecay-copyable (the datums must be stored across the scheduling hop).continues_ondoes not altersndr'svalues or errors; it only changes the execution context on which the completion is delivered.Exception behavior.
If decay-copying a completion datum across the scheduling hop throws, the exception is delivered through
set_error_t(std::exception_ptr). If scheduling ontoschedfails, an error completion is delivered on an unspecified execution agent.Cancellation.
continues_onrespects the receiver’s stop token while waiting to be scheduled ontosched:if cancellation is requested aftersndrcompletes but before the hop finishes, the resulting sender typically completes viaset_stopped.Example.
#include <stdexec/execution.hpp> int main() { using namespace stdexec; auto io_sched = get_parallel_scheduler(); // pretend: I/O auto cpu_sched = get_parallel_scheduler(); // pretend: compute auto sndr = starts_on(io_sched, just(42)) // produce on io_sched | continues_on(cpu_sched) // hop to cpu_sched | then([](int x) { return x * 2; }); // then() runs on cpu_sched auto [v] = sync_wait(std::move(sndr)).value(); (void)v; // == 84 }
See also
stdexec::schedule — the primitive that produces a schedule-sender
See also
stdexec::starts_on — begin execution on a given scheduler
See also
stdexec::on — run on a different scheduler, then transfer back
Public Functions
-
template<scheduler _Scheduler, sender _Sender>
inline constexpr auto operator()(_Sender &&__sndr, _Scheduler __sched) const -> __well_formed_sender auto Construct a sender that runs
__sndrto completion, then transfers execution to__schedbefore forwarding the completion downstream.- Template Parameters:
_Scheduler – A type satisfying the
stdexec::schedulerconcept._Sender – A type satisfying the
stdexec::senderconcept.
- Parameters:
__sndr – The predecessor sender. Forwarded into the resulting sender.
__sched – The scheduler whose execution resource will host the delivery of
__sndr'scompletion.
- Returns:
A sender with the same completion signatures as
__sndr(plus a possibleset_error_t(std::exception_ptr)for decay-copy failures during the hop). The completions are delivered on__sched'sexecution resource.
-
template<scheduler _Scheduler>
inline constexpr auto operator()(_Scheduler __sched) const noexcept Construct a sender-adaptor closure that, when applied to a sender, produces
continues_on(sndr, __sched).This overload enables the pipe syntax:
sndr | continues_on(__sched)is equivalent tocontinues_on(sndr, __sched).- Template Parameters:
_Scheduler – A type satisfying the
stdexec::schedulerconcept.- Parameters:
__sched – The scheduler to transfer execution to when the closure is later applied to a sender.
- Returns:
A sender-adaptor closure object capturing
__sched.
-
template<scheduler _Scheduler, sender _Sender>
-
constexpr continues_on_t stdexec::continues_on
The customization point object for the
continues_onsender adaptor.continues_onis an instance of continues_on_t. See continues_on_t for the full description, completion signatures, and a usage example.
on — run on a scheduler and return to the original
The “go there, do work, come back” adaptor. Two forms:
on(sched, sndr) runs sndr on sched and returns to the start
scheduler; on(sndr, sched, closure) (and its pipe form
sndr | on(sched, closure)) hops to sched for an inserted
closure then hops back. See on — take a side trip to another scheduler for guidance on when to
reach for which form.
-
struct on_t
A sender adaptor that runs work on a different scheduler and then transfers execution back to the original scheduler.
onis the “go there, do work, come back” scheduling adaptor. It has two distinct shapes:Whole-sender form:
on(sched, sndr)— runs the entirety ofsndronsched'sexecution resource. Whensndrcompletes, execution transfers back to the scheduler that started the operation (the “start scheduler”), and the completion is delivered there.This is the principal difference between
onand starts_on_t —starts_onstays onsched;onreturns home.Closure-insertion form:
on(sndr, sched, closure)— runssndron its current scheduler, then transfers tosched, appliesclosure(a sender-adaptor closure) to the result, runs that onsched, and finally transfers back to the original completion scheduler. Useful for inserting a CPU-bound transform into an otherwise I/O-bound pipeline (or vice versa) without permanently changing context.This form also has a pipe shorthand:
sndr | on(sched, closure).
// Form 1: run sndr on sched, return to start scheduler. auto s1 = stdexec::on(sched, sndr); // Form 2: run sndr in place, hop to sched for closure, hop back. auto s2 = stdexec::on(sndr, sched, stdexec::then([](int x){ return x*2; })); auto s3 = sndr | stdexec::on(sched, stdexec::then([](int x){ return x*2; }));
See [exec.on] in the C++26 working draft for the normative specification.
The round trip.
What distinguishes
onfromstarts_onandcontinues_onis the restoration of the original scheduler:Adaptor
Where work runs
Where completion is delivered
on
schon
schon
s'sschedon
schon
schon the start scheduler
mixed (see above)
on the start scheduler
The “start scheduler” is read from the receiver’s environment via
get_start_scheduler(form 1) orget_completion_scheduler<set_value_t>ofsndr'senvironment (form 2).Completion signatures.
Form 1 (
on(sched, sndr)): essentiallysndr'scompletion signatures, with possible additionalset_error_tcompletions from the two scheduling hops.Form 2 (
on(sndr, sched, closure)): the completion signatures ofclosure(continues_on(sndr, sched))after the final transfer back, again with possible additionalset_error_tcompletions from the scheduling hops.If scheduling onto
sched(or back) fails, an error completion is delivered on an unspecified execution agent.Cancellation.
Cancellation flows through the scheduling hops normally; a stop request observed between hops typically results in
set_stopped.Example.
#include <stdexec/execution.hpp> int main() { using namespace stdexec; auto gpu = get_parallel_scheduler(); // pretend: GPU // Compute on the GPU, but stay on the start scheduler afterwards: auto sndr = just(21) | on(gpu, then([](int x) { return x * 2; })); auto [v] = sync_wait(std::move(sndr)).value(); (void)v; // == 42; sync_wait sees the result on its starting context }
See also
stdexec::schedule — the primitive that produces a schedule-sender
See also
stdexec::starts_on — begin on a scheduler and stay there
See also
stdexec::continues_on — transfer to a scheduler after a sender completes
Public Functions
-
template<scheduler _Scheduler, sender _Sender>
inline constexpr auto operator()(_Scheduler &&__sched, _Sender &&__sndr) const -> __well_formed_sender auto Form 1: run
__sndron__sched, then return to the start scheduler.- Template Parameters:
_Scheduler – A type satisfying the
stdexec::schedulerconcept._Sender – A type satisfying the
stdexec::senderconcept.
- Parameters:
__sched – The scheduler whose execution resource will host
__sndr.__sndr – The sender to run on
__sched.
- Returns:
A sender that, when connected and started, runs
__sndron__schedthen transfers execution back to the start scheduler (taken from the receiver’s environment viaget_start_scheduler) before forwarding__sndr'scompletion to the receiver.
-
template<sender _Sender, scheduler _Scheduler, __sender_adaptor_closure_for<_Sender> _Closure>
inline constexpr auto operator()(_Sender &&__sndr, _Scheduler &&__sched, _Closure &&__clsur) const -> __well_formed_sender auto Form 2: run
__sndrin place, hop to__sched, apply__clsurthere, then hop back.- Template Parameters:
_Sender – A type satisfying the
stdexec::senderconcept._Scheduler – A type satisfying the
stdexec::schedulerconcept._Closure – A sender-adaptor closure suitable for chaining onto
_Sender.
- Parameters:
- Returns:
A sender that completes on the original completion scheduler of
__sndr, with the result of__clsur(continues_on(__sndr, __sched)).
-
template<scheduler _Scheduler, __sender_adaptor_closure _Closure>
inline constexpr auto operator()(_Scheduler &&__sched, _Closure &&__clsur) const Pipe form of Form 2: construct a sender-adaptor closure that, when applied to a sender, produces
on(sndr, __sched, __clsur).- Template Parameters:
_Scheduler – A type satisfying the
stdexec::schedulerconcept._Closure – A sender-adaptor closure.
- Parameters:
__sched – The scheduler to transition to.
__clsur – The adaptor closure to apply on
__sched.
- Returns:
A sender-adaptor closure capturing
__schedand__clsur.
-
on_t const stdexec::on
The customization point object for the
onsender adaptor.onis an instance of on_t. See on_t for the full description, the distinction betweenon,starts_on, andcontinues_on, and usage examples.
Composition adaptors
These adaptors combine multiple senders into one. They are the building blocks for parallel and fan-out patterns.
when_all — run senders concurrently and concatenate values
Takes one or more senders and produces a sender that, when started, runs all of them concurrently and completes when every input has completed. The resulting value-completion is the concatenation of every input’s value datums. If any input fails or is stopped, the others are cancelled and the result is the first error/stopped completion observed. Each input must have exactly one value-completion shape; for senders that can succeed in more than one way, see when_all_with_variant — like when_all for multi-completion senders.
-
struct when_all_t
A variadic sender factory that runs multiple senders concurrently and completes when all of them have completed, concatenating their value datums.
when_allis the canonical parallel composition primitive in the sender model. You give it one or more senders; it returns a single sender that, when connected and started, starts all of the input senders concurrently. When every input has completed,when_all'ssender completes with a value tuple that is the concatenation of every input’s value datums.If any one input fails or is stopped,
when_allrequests stop on the others (via an internalinplace_stop_source) and completes with that error (or withset_stopped). This makeswhen_allnaturally fail-fast: as soon as one branch has gone bad, the rest are asked to wind down.auto s = stdexec::when_all( stdexec::just(1), stdexec::just(2.5), stdexec::just(std::string{"x"})); auto [i, d, str] = stdexec::sync_wait(std::move(s)).value(); // i == 1, d == 2.5, str == "x"
See [exec.when.all] in the C++26 working draft for the normative specification.
Single value-completion requirement.
when_allrequires that each input sender have exactly oneset_value_tcompletion signature — otherwise the output value signature would be a combinatorial explosion of all possible concatenations. The constraint is enforced at connect time with a diagnostic pointing at when_all_with_variant_t, which lifts the restriction (at the cost of producing a variant per input).Completion signatures.
Given inputs
sndr_iwith completion signatures// For each i in 1..n: set_value_t(Vi...) // exactly one such signature per input set_error_t(Eij)... // zero or more per input set_stopped_t() // optional per input
the resulting sender has completion signatures:
set_value_t(V1..., V2..., ..., Vn...) // concatenation of every input set_error_t(Eij)... // union across all inputs set_error_t(std::exception_ptr) // added if any decay-copy may throw set_stopped_t() // added if any input has it, // or if cancellation may happen
The value datums of each input are decay-copied into the resulting sender’s state while it waits for the slowest input to finish; the final tuple is built from those decay-copies. If any decay-copy throws, the operation transitions to the error path.
Concurrency.
“Concurrently” here means the inputs are not sequenced relative to each other —
when_allstarts every child operation in a fold expression before returning fromstart. Whether they actually execute in parallel depends on the schedulers they’re attached to: independentstarts_on/continues_onbranches across different schedulers truly run in parallel; multiplejust-rootedbranches all complete synchronously insidewhen_all'sstart.Error and stop semantics.
At most one completion is delivered to the downstream receiver. If several children produce errors or stopped completions, the first one observed wins; subsequent failures are dropped. Concretely:
First child to call
set_errorwins; its error becomes the result.First child to call
set_stoppedwins (if no error has been seen).On either, the internal stop source is signalled so the remaining children can wind down promptly.
Cancellation.
when_allchains the receiver’s stop token to its internal stop-source, so an outer stop request propagates to every child.Example.
#include <stdexec/execution.hpp> int main() { using namespace stdexec; auto sched = get_parallel_scheduler(); auto pipeline = when_all( starts_on(sched, just(10) | then([](int x){ return x * 2; })), starts_on(sched, just(5) | then([](int x){ return x + 1; }))); auto [a, b] = sync_wait(std::move(pipeline)).value(); // a == 20, b == 6, computed in parallel on `sched` }
See also
stdexec::when_all_with_variant — for inputs with multiple value-completion shapes
See also
stdexec::transfer_when_all — when_all + scheduler transfer (stdexec extension)
See also
stdexec::spawn_future — start a sender eagerly and observe via a sender
Public Functions
-
template<sender... _Senders>
inline constexpr auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto Compose
__sndrs… into a sender that completes when every input has completed.- Template Parameters:
_Senders – A pack of types each satisfying
stdexec::sender. Must be non-empty. Each must have exactly oneset_value_tcompletion signature in the ambient environment.- Parameters:
__sndrs – The senders to compose. Forwarded into the result.
- Returns:
A sender that, when connected and started, concurrently starts every input and value-completes with the concatenation of the input’s value datums.
-
when_all_t const stdexec::when_all
The customization point object for the
when_allsender factory.when_allis an instance of when_all_t. See when_all_t for the full description, completion signatures, error/stop semantics, and a usage example.
when_all_with_variant — like when_all for multi-completion senders
Like when_all, but lifts the “exactly one value-completion per
input” restriction by wrapping each input in
stdexec::into_variant. Each output value-completion
position is a std::variant<std::tuple<...>, ...> of that input’s
possible shapes.
-
struct when_all_with_variant_t
A variadic sender factory like
when_allthat lifts the “exactly one value completion per input” restriction by wrapping each input ininto_variant.when_all_with_variantis for the case where one or more of the inputs can complete with more than one value-completion shape. Wherewhen_allwould refuse to compile,when_all_with_varianttransforms each input viastdexec::into_variantfirst (collapsing that input’s multiple shapes into a singlestd::variant<std::tuple<...>, …> value), and then composes the results with the ordinarywhen_allrules.// sndr_a value-completes with either int or std::string; // sndr_b value-completes with float. auto s = stdexec::when_all_with_variant(sndr_a, sndr_b); auto [variant_a, variant_b] = stdexec::sync_wait(std::move(s)).value(); // variant_a: std::variant<std::tuple<int>, std::tuple<std::string>> // variant_b: std::variant<std::tuple<float>>
See [exec.when.all] in the C++26 working draft for the normative specification (where
when_all_with_variantis specified alongsidewhen_all).Equivalence.
when_all_with_variant(sndrs...)is specified as expression-equivalent towhen_all(into_variant(sndrs)...)(aftertransform_sender), so all ofwhen_all'sconcurrency, error, and cancellation semantics carry over unchanged.See also
stdexec::when_all — single-value-completion variant
See also
stdexec::into_variant — the adaptor that lifts each input
Public Functions
-
template<sender... _Senders>
inline constexpr auto operator()(_Senders&&... __sndrs) const -> __well_formed_sender auto Compose
__sndrs… into a sender that completes when every input has completed, with each input wrapped ininto_variant.- Template Parameters:
_Senders – A non-empty pack of types each satisfying
stdexec::sender. Inputs may have multiple value-completion shapes.- Returns:
A sender equivalent to
when_all(into_variant(__sndrs)...).
-
template<sender... _Senders>
-
when_all_with_variant_t const stdexec::when_all_with_variant
The customization point object for the
when_all_with_variantsender factory.when_all_with_variantis an instance of when_all_with_variant_t. See when_all_with_variant_t for the full description.
into_variant — collapse multi-completion senders into one
Reshapes a sender that may value-complete in more than one way into a
sender that always value-completes with a single
std::variant-of-tuples datum. It is the building block behind
when_all_with_variant and
sync_wait_with_variant,
and is occasionally useful on its own when a downstream algorithm
requires the single-value-completion form.
-
struct into_variant_t
A pipeable sender adaptor that collapses a sender’s multiple value-completion signatures into a single
std::variant-of-tuplesvalue completion.into_varianttakes a sender whoseset_value_tcompletion can be one of several shapes — e.g.set_value_t(int)orset_value_t(std::string)— and produces a sender that always value-completes with exactly one shape: a singlestd::variant<std::tuple<Vs1...>, std::tuple<Vs2...>, ...>datum whose alternatives match the input’s possible value completions.This is the building block behind
when_all_with_variant:it lifts any sender into the single-value-completion category thatwhen_allandsync_waitrequire.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::into_variant(sndr); // direct invocation auto s2 = sndr | stdexec::into_variant(); // pipe syntax (no args)
Completion signatures.
Given a predecessor sender
sndrwith value-completion signaturesset_value_t(Vs1...) // possibly several set_value_t(Vs2...) set_error_t(Es)... // forwarded unchanged set_stopped_t() // forwarded unchanged
the sender produced by
into_variant(sndr)has completion signaturesset_value_t(std::variant<std::tuple<Vs1...>, std::tuple<Vs2...>, ...>) set_error_t(Es)... // unchanged set_error_t(std::exception_ptr) // added if variant construction may throw set_stopped_t() // unchanged
When
sndrvalue-completes with arguments matchingVsi… the resulting sender value-completes with a variant engaged on the corresponding alternative.Exception behavior.
If constructing the variant alternative (which involves decay-copying the original value arguments) throws, the exception is delivered through
set_error_t(std::exception_ptr).Cancellation.
into_variantdoes not interact with the stop token; it only reshapes the value channel.Example.
#include <stdexec/execution.hpp> // Suppose sndr can value-complete with either int or std::string. auto wrapped = stdexec::into_variant(sndr); auto [v] = stdexec::sync_wait(std::move(wrapped)).value(); // v: std::variant<std::tuple<int>, std::tuple<std::string>> std::visit([](auto&& tup) { use(tup); }, v);
See also
stdexec::when_all_with_variant — applies
into_variantto each input internallySee also
stdexec::sync_wait_with_variant — variant-aware top-level wait
See also
stdexec::sync_wait — requires a single value-completion shape
Public Functions
-
template<sender _Sender>
inline constexpr auto operator()(_Sender &&__sndr) const -> __well_formed_sender auto Construct a sender that value-completes with a
std::variantof the possible value-completion tuples of__sndr.- Template Parameters:
_Sender – A type satisfying
stdexec::sender.- Parameters:
__sndr – The predecessor sender. Forwarded into the result.
- Returns:
A sender with a single
set_value_tcompletion whose argument is astd::variantofstd::tuple<Vs...>alternatives.
-
inline constexpr auto operator()() const noexcept
Construct a sender-adaptor closure that, when applied to a sender, produces
into_variant(sndr).This overload enables the pipe syntax:
sndr | into_variant()is equivalent tointo_variant(sndr).- Returns:
A sender-adaptor closure object.
-
template<sender _Sender>
-
into_variant_t const stdexec::into_variant
The customization point object for the
into_variantsender adaptor.into_variantis an instance of into_variant_t. See into_variant_t for the full description and a usage example.
transfer_when_all (deprecated)
Deprecated
This adaptor is not part of the C++26 working draft and is retained
only for backwards compatibility. Write
when_all(sndrs...) | continues_on(sch) instead — the behavior is
identical.
-
struct transfer_when_all_t
Like
when_all, but transfers execution to a scheduler before delivering the combined completion.- Deprecated:
transfer_when_allis deprecated. It is not part of the C++26 working draft and is retained only for backwards compatibility. Writewhen_all(sndrs...) | continues_on(sch)instead; the behavior is identical.
Composition of
when_allwithcontinues_on:transfer_when_all(sch, sndrs...)is expression-equivalent tocontinues_on(when_all(sndrs...), sch). The inputs run concurrently (wherever their respective schedulers run them), and once all have completed, the combined result is delivered onsch'sexecution resource.See also
stdexec::when_all — without the scheduler transfer
See also
stdexec::continues_on — the underlying transfer primitive
Public Functions
-
template<scheduler _Scheduler, sender... _Senders>
inline constexpr auto operator()(_Scheduler __sched, _Senders&&... __sndrs) const -> __well_formed_sender auto Compose
__sndrs… and deliver the combined completion on__sched'sexecution resource.- Template Parameters:
_Scheduler – A type satisfying
stdexec::scheduler._Senders – A non-empty pack of
stdexec::sendertypes.
-
constexpr transfer_when_all_t stdexec::transfer_when_all
The customization point object for the
transfer_when_allsender factory.- Deprecated:
See transfer_when_all_t. Use
when_all(...) | continues_on(sch)instead.
transfer_when_all_with_variant (deprecated)
Deprecated
This adaptor is not part of the C++26 working draft and is retained
only for backwards compatibility. Write
when_all_with_variant(sndrs...) | continues_on(sch) instead.
-
struct transfer_when_all_with_variant_t
Like
when_all_with_variant, but transfers execution to a scheduler before delivering the combined completion.- Deprecated:
transfer_when_all_with_variantis deprecated. It is not part of the C++26 working draft and is retained only for backwards compatibility. Writewhen_all_with_variant(sndrs...) | continues_on(sch)instead; the behavior is identical.
Composition of
when_all_with_variantandcontinues_on:transfer_when_all_with_variant(sch, sndrs...)is expression-equivalent tocontinues_on(when_all_with_variant(sndrs...), sch).See also
See also
Public Functions
-
template<scheduler _Scheduler, sender... _Senders>
inline constexpr auto operator()(_Scheduler &&__sched, _Senders&&... __sndrs) const -> __well_formed_sender auto Compose
__sndrs… (each wrapped ininto_variant) and deliver the combined completion on__sched'sexecution resource.
-
constexpr transfer_when_all_with_variant_t stdexec::transfer_when_all_with_variant
The customization point object for the
transfer_when_all_with_variantsender factory.- Deprecated:
See transfer_when_all_with_variant_t. Use
when_all_with_variant(...) | continues_on(sch)instead.
Parallel-loop adaptors
The bulk family invokes a callable over an integer index space,
under a given execution policy. They are the parallel-loop primitives
of the sender model — the entry point for GPU/parallel-scheduler
customizations to take over.
bulk — apply a function to each index
Invokes f(i, vs...) for every i in [0, shape) under the
given execution policy. Lowers to bulk_chunked internally so that
domain customizations of bulk_chunked apply transparently. See
bulk — apply a function over an index space for a worked example and policy discussion.
-
struct bulk_t : public stdexec::__bulk::__generic_bulk_t<bulk_t>
A pipeable sender adaptor that applies a function to each index in
[0, shape) under a given execution policy.bulkis the parallel-loop primitive of the sender model. You give it a sender, an execution policy (e.g.stdexec::par), an integral shape, and a callable; you get back a sender that, when started, invokesf(i, vs...)for everyiin[0, shape) — wherevs… are the predecessor’s value-completion datums. The execution policy controls whether the invocations may run in parallel.See [exec.bulk] in the C++26 working draft.
The signature of the operator overloads (inherited from a detail base) is:
template <sender Sender, /* execution_policy */ Policy, integral Shape, copy_constructible Fun> auto operator()(Sender&& sndr, Policy&& pol, Shape shape, Fun fun) const -> sender auto; // direct template <class Policy, integral Shape, copy_constructible Fun> auto operator()(Policy&& pol, Shape shape, Fun fun) const; // closure
Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::bulk(sndr, stdexec::par, 1024, fn); auto s2 = sndr | stdexec::bulk(stdexec::par, 1024, fn);
Completion signatures.
bulkforwards the predecessor’s completion signatures, optionally addingset_error_t(std::exception_ptr)if invokingfnmay throw (or if internal allocation may throw):set_value_t(Vs...) // forwarded unchanged from sndr set_error_t(Es)... // forwarded unchanged from sndr set_error_t(std::exception_ptr) // added if fn may throw set_stopped_t() // forwarded unchanged (if present)
fnis invoked with the value-completion datums ofsndrpreserved across all invocations — every iteration sees the samevs…Execution policy.
The policy argument follows the
<execution>conventions:stdexec::seqfor sequenced execution,stdexec::parfor permitted-parallel,stdexec::par_unseqfor permitted parallel and vectorized. A custom scheduler’s domain may interpret these differently — e.g. a GPU domain may lowerparto a CUDA kernel launch.Implementation note: lowering to
bulk_chunked**.**Internally,
bulkis implemented in terms of bulk_chunked_t — itstransform_sendermember rewritesbulk(sndr, pol, n, f)into abulk_chunkedover the same shape withfwrapped in a per-chunk loop. If a domain customizesbulk_chunked,bulkpicks up that customization automatically.See also
stdexec::bulk_chunked — explicit-chunk variant
See also
stdexec::bulk_unchunked — strict per-index variant (no chunking allowed)
See also
stdexec::when_all — concurrent composition without an index space
bulk_chunked — apply a function per chunk of indices
Invokes f(begin, end, vs...) for chunks of the index space.
The implementation may split into any number of chunks (one,
shape, anything between).
-
struct bulk_chunked_t : public stdexec::__bulk::__generic_bulk_t<bulk_chunked_t>
A pipeable sender adaptor that invokes a function with chunked sub-ranges of an integer index space.
Where bulk_t passes a single index to its callable,
bulk_chunkedpasses a half-open range[begin, end) covering some subset of[0, shape). The implementation may split[0, shape) into any number of chunks (including one chunk equal to the whole range, orshapechunks of one element each) — the only guarantee is that every index in[0, shape) is covered by exactly one chunk.See [exec.bulk] in the C++26 working draft.
The signature of the operator overloads (inherited from a detail base) is:
template <sender Sender, /* execution_policy */ Policy, integral Shape, copy_constructible Fun> auto operator()(Sender&& sndr, Policy&& pol, Shape shape, Fun fun) const -> sender auto; // direct template <class Policy, integral Shape, copy_constructible Fun> auto operator()(Policy&& pol, Shape shape, Fun fun) const; // closure
The callable is invoked as
fun(begin, end, vs...), wherevs… are the predecessor’s value-completion datums (shared across all chunks).When to use
bulk_chunkedvs.bulk**:**Use
bulkwhen the per-iteration body is small and the loop is the payload —bulk'slowering tobulk_chunkedwill let the runtime pick chunk sizes for you. Usebulk_chunkeddirectly when the body benefits from per-chunk amortization (allocations, accumulators, vectorization setup) that you want to do once per chunk rather than once per index.See also
stdexec::bulk — index-at-a-time variant (lowers to this)
See also
stdexec::bulk_unchunked — strict per-index variant (no chunking)
-
constexpr bulk_chunked_t stdexec::bulk_chunked
The customization point object for the
bulk_chunkedsender adaptor.bulk_chunkedis an instance of bulk_chunked_t. See bulk_chunked_t for the full description.
bulk_unchunked — apply a function per index, no chunking allowed
Like bulk but forbids the implementation from combining multiple
indices into a single call. Use when per-iteration state (thread-local
accumulators, per-index hardware resources) prevents batching.
-
struct bulk_unchunked_t : public stdexec::__bulk::__generic_bulk_t<bulk_unchunked_t>
A pipeable sender adaptor that invokes a function once per index in
[0, shape), without permission to chunk.bulk_unchunkedhas the same per-index invocation pattern as bulk_t —fun(i, vs...)for everyi— but explicitly forbids the implementation from combining multiple indices into a single call. Spec-recommended (but not required) practice is for each iteration to run on a distinct execution agent.Use this only when the body of the loop has per-iteration state or synchronization that cannot be batched — e.g., per-thread-local accumulators, per-index hardware resources, observable side effects that must be one-per-index. For ordinary parallel loops, prefer bulk_t (which lowers to
bulk_chunkedand lets the runtime make chunk-size decisions).See [exec.bulk] in the C++26 working draft.
The signature of the operator overloads (inherited from a detail base) is:
template <sender Sender, /* execution_policy */ Policy, integral Shape, copy_constructible Fun> auto operator()(Sender&& sndr, Policy&& pol, Shape shape, Fun fun) const -> sender auto; // direct template <class Policy, integral Shape, copy_constructible Fun> auto operator()(Policy&& pol, Shape shape, Fun fun) const; // closure
See also
stdexec::bulk — chunking-permitted index-at-a-time variant
See also
stdexec::bulk_chunked — explicit-chunk variant
-
constexpr bulk_unchunked_t stdexec::bulk_unchunked
The customization point object for the
bulk_unchunkedsender adaptor.bulk_unchunkedis an instance of bulk_unchunked_t. See bulk_unchunked_t for the full description and when to reach for it.
Stopped-channel translator adaptors
These adaptors don’t change the behavior of a pipeline; they translate one completion channel into another, exposing a friendlier shape to downstream code.
stopped_as_error — translate stopped into an error
Converts a set_stopped completion into a set_error completion
carrying a caller-supplied error datum. The resulting sender no longer
has a stopped channel.
-
struct stopped_as_error_t
A pipeable sender adaptor that converts a predecessor’s stopped completion into an error completion carrying a user-supplied error value.
stopped_as_erroris a “translator” adaptor: it doesn’t change what happens in the value channel, but it rewrites aset_stoppedcompletion into aset_errorcompletion with a specific datum. This is useful when downstream code needs to distinguish a cancellation from a real error and you want the cancellation to be delivered as an error of a particular type (typically because the consumer can’t or won’t handle the stopped channel).Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::stopped_as_error(sndr, my_error); auto s2 = sndr | stdexec::stopped_as_error(my_error);
Equivalence.
stopped_as_error(sndr, err)is implemented (and is observationally equivalent to)let_stopped(sndr, [err]{ return just_error(err); }). Use this adaptor whenever you would have written that pattern by hand — it is shorter, clearer at the call site, and the implementation can be specialized more efficiently in the future.Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(Vs...) // forwarded unchanged set_error_t(Es)... // forwarded unchanged set_stopped_t() // consumed
the sender produced by
stopped_as_error(sndr, err)has completion signaturesset_value_t(Vs...) // forwarded unchanged set_error_t(Es)... // forwarded unchanged set_error_t(std::decay_t<E>) // the supplied error, decay-copied
The original
set_stopped_tcompletion is replaced; the resulting sender will never deliverset_stopped.Example.
using namespace stdexec; auto sndr = just_stopped() | stopped_as_error(std::runtime_error{"cancelled"}); try { sync_wait(std::move(sndr)); // throws std::runtime_error } catch (std::runtime_error const& e) { // e.what() == "cancelled" }
See also
stdexec::stopped_as_optional — convert stopped into a value-channel
std::nulloptSee also
stdexec::upon_stopped — handle stopped synchronously
See also
stdexec::let_stopped — handle stopped with a sender-returning callback
Public Functions
-
template<sender _Sender, __movable_value _Error>
inline constexpr auto operator()(_Sender &&__sndr, _Error __err) const -> __well_formed_sender auto Construct a sender that translates
__sndr'sset_stoppedcompletion into aset_errorcompletion carrying__err.- Template Parameters:
_Sender – A type satisfying
stdexec::sender._Error – A decayed, move-constructible error datum type (satisfying the internal
__movable_valueconcept).
- Parameters:
__sndr – The predecessor sender. Forwarded into the result.
__err – The error datum to deliver if
__sndris stopped. Decay-copied into the resulting sender.
-
template<__movable_value _Error>
inline constexpr auto operator()(_Error __err) const noexcept(__nothrow_move_constructible<_Error>) Construct a sender-adaptor closure for the pipe form.
sndr | stopped_as_error(__err)is equivalent tostopped_as_error(sndr, __err).
-
template<sender _Sender, __movable_value _Error>
-
stopped_as_error_t const stdexec::stopped_as_error
The customization point object for the
stopped_as_errorsender adaptor.stopped_as_erroris an instance of stopped_as_error_t. See stopped_as_error_t for the full description and a usage example.
stopped_as_optional — translate stopped into a value-channel nullopt
Converts a set_stopped completion into a value-channel
std::optional<T>{std::nullopt}, wrapping the predecessor’s value
in std::optional<T>. Requires the predecessor to have exactly one
value completion with one argument.
-
struct stopped_as_optional_t
A pipeable sender adaptor that converts a predecessor’s stopped completion into a value-channel
std::nullopt, wrapping the value-completion datum in astd::optional.stopped_as_optionalis the value-channel mirror of stopped_as_error_t. Wherestopped_as_errorturns cancellation into an error,stopped_as_optionalturns cancellation into a “no value” signal on the value channel. The resulting sender value-completes with astd::optional<T>: engaged if the predecessor produced a value, disengaged if the predecessor was stopped.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::stopped_as_optional(sndr); auto s2 = sndr | stdexec::stopped_as_optional();
Use this when downstream code prefers branching on a
std::optional(a familiar idiom) over branching on an emptystd::optional<std::tuple<...>>fromsync_waitor handling theset_stoppedchannel via an adaptor.Single value-completion requirement.
stopped_as_optionalrequires the predecessor to have exactly one value-completion signature with exactly one argument. (How would we wrap multiple values in a singlestd::optional?) If the predecessor has multiple value completions, or zero/multiple value arguments, the program is ill-formed with a focused diagnostic (“the sender must have exactly one value completion with one
argument”).
Completion signatures.
Given a predecessor sender
sndrwith completion signaturesset_value_t(T) // exactly one value-completion with one argument set_error_t(Es)... // zero or more set_stopped_t() // consumed
the sender produced by
stopped_as_optional(sndr)has completion signaturesset_value_t(std::optional<std::decay_t<T>>) set_error_t(Es)... // forwarded unchanged set_error_t(std::exception_ptr) // added if wrapping may throw // (no set_stopped_t in the output)
The original
set_stopped_tcompletion is consumed: the resulting sender will never deliverset_stopped.Exception behavior.
If constructing the
std::optionalfrom the predecessor’s value throws (e.g., the value type’s copy constructor throws), the exception is delivered throughset_error_t(std::exception_ptr).Example.
using namespace stdexec; auto sndr = just(42) | stopped_as_optional(); auto [opt] = sync_wait(std::move(sndr)).value(); // opt == std::optional<int>{42} auto sndr2 = just_stopped() | stopped_as_optional(); // ...but to make stopped_as_optional well-formed here we need to give // the predecessor a value-shape; in practice you compose it on a // sender that may either succeed or be stopped: auto pipeline = /* some sender that produces an int or is stopped */ | stopped_as_optional();
See also
stdexec::stopped_as_error — convert stopped into an error
See also
stdexec::upon_stopped — handle stopped synchronously
See also
stdexec::let_stopped — handle stopped with a sender-returning callback
See also
stdexec::sync_wait — also uses an outer
std::optionalto signal stopPublic Functions
-
template<sender _Sender>
inline constexpr auto operator()(_Sender &&__sndr) const -> __well_formed_sender auto Construct a sender that wraps
__sndr'svalue completion in astd::optionaland reroutesset_stoppedto a disengaged optional on the value channel.- Template Parameters:
_Sender – A type satisfying
stdexec::senderwhose completion signatures include exactly oneset_value_t(T)signature.- Parameters:
__sndr – The predecessor sender. Forwarded into the result.
-
inline auto operator()() const noexcept
Construct a sender-adaptor closure for the pipe form.
sndr | stopped_as_optional()is equivalent tostopped_as_optional(sndr). The empty parentheses are required by the pipe-closure convention; there are no captured arguments.
-
template<sender _Sender>
-
stopped_as_optional_t const stdexec::stopped_as_optional
The customization point object for the
stopped_as_optionalsender adaptor.stopped_as_optionalis an instance of stopped_as_optional_t. See stopped_as_optional_t for the full description and a usage example.
Environment adaptors
write_env — inject values into the environment
Augments the environment seen by a predecessor sender with
additional queries. The inverse of read_env:
read_env exposes environment values into the value channel,
write_env injects environment values into a child sender’s
environment.
-
__write_env_t const stdexec::write_env
A pipeable sender adaptor that augments the environment seen by a predecessor sender with additional queries.
write_envis the inverse of read_env. Whereread_envreads a value from the receiver’s environment and exposes it on the value channel,write_envinjects values into the environment a child sender sees — overriding or augmenting what the eventual receiver exposes.You give it a sender and an environment (typically built with
stdexec::envandstdexec::prop); you get back a sender that, when connected, presents the union of the supplied environment and the connected receiver’s environment to its predecessor. Anything the predecessor reaches for viaget_env/read_envsees the merged view.Both call syntaxes are supported (the second is the pipeable form):
auto s1 = stdexec::write_env(sndr, env); auto s2 = sndr | stdexec::write_env(env);
The supplied environment shadows the receiver’s environment for any query the supplied environment can answer; queries it cannot answer fall through to the receiver’s environment unchanged.
Common uses.
Injecting a stop token:
sndr | write_env(prop{get_stop_token, my_token})so a sub-pipeline observes a different cancellation signal than the outer pipeline.Supplying an allocator:
sndr | write_env(prop{get_allocator, my_alloc})so child operations allocate viamy_alloc.Hooking domain customization: a custom scheduler may inject its domain into the environment for senders that don’t have a scheduler in their chain.
Completion signatures.
write_envpreserves the predecessor’s completion signatures unchanged. (The predecessor may compute different signatures depending on what’s in its environment — so the supplied env may influence which signatures the framework computes — butwrite_envdoes not itself add or remove any.)Example.
using namespace stdexec; auto inner_sndr = read_env(get_stop_token) | then([](auto tok) { return tok.stop_requested(); }); stop_source src; auto pipeline = inner_sndr | write_env(prop{get_stop_token, src.get_token()}); auto [requested] = sync_wait(std::move(pipeline)).value(); // requested == src.stop_requested(), regardless of the outer // pipeline's stop token.
See also
stdexec::read_env — read a value from the environment
See also
stdexec::env — construct an environment from properties
See also
stdexec::prop — bind a query CPO to a value
See also
stdexec::get_env — the CPO that exposes the merged environment
Sender Consumers
A sender consumer takes a sender, connects it to a receiver, and starts the resulting operation. Consumers sit at the tail of a sender pipeline — they are the point at which asynchronous work actually runs. They fall into two broad families:
Synchronous waiters (sync_wait, sync_wait_with_variant) block the calling thread until the pipeline completes and return the result.
Eager launchers (start_detached, spawn, spawn_future) start the pipeline immediately and either discard the result (spawn and start_detached) or expose it as a sender that observes the running operation (spawn_future).
See Picking a consumer for a side-by-side comparison and guidance on which consumer to reach for.
sync_wait — block until the sender completes
Synchronously waits for a single-value-completion sender to complete on
the calling thread. Returns an engaged std::optional<std::tuple<...>>
on success, an empty optional on stopped, and throws on error.
-
struct sync_wait_t
A sender consumer that synchronously blocks the calling thread until a sender completes and returns its result.
sync_waitis the bridge from the asynchronous sender world back into synchronous code. You give it a sender; it connects the sender to a built-in receiver, starts the resulting operation, then drives an internalrun_loopon the calling thread until the operation completes. The result is returned as astd::optionalof a tuple of the value-completion datums.This is the most common way to “run” a sender in a top-level program or a test — it’s what you reach for in a
main()or when synchronously waiting on a single sub-pipeline. For fire-and-forget execution, preferexec::start_detachedorstdexec::spawn.auto [v] = stdexec::sync_wait(stdexec::just(42)).value(); // v == 42
See [exec.sync.wait] in the C++26 working draft for the normative specification.
Completion behavior.
Given an input sender
sndrthat, in some environment, completes with exactly one of:Sender completion
What
sync_waitdoesset_value_t(Vs...)Returns
std::optional<std::tuple<Vs...>>engaged.set_error_t(std::exception_ptr)Rethrows the exception via
std::rethrow_exception.set_error_t(std::error_code)Throws
std::system_error(error_code).set_error_t(E)Throws
Edirectly.set_stopped_t()Returns an empty (disengaged)
std::optional.Single-value-completion requirement.
sync_waitmandates that its argument sender have exactly oneset_value_tcompletion signature. A sender that can succeed in more than one way (e.g.just(1) | when_all(just(std::string{"x"}))yielding two distinct tuples) requiressync_wait_with_variantinstead. The static assertion insync_waitwill point this out at compile time, with a hint to use the variant form.Delegation scheduler.
The internal
run_loopis exposed viaget_delegation_scheduleron the receiver’s environment, so senders that need to enqueue work back onto the waiting thread (e.g. continuations after an I/O wait) can do so safely. This is what enables algorithms likecontinues_onto return execution to the calling thread ofsync_wait.When to use
sync_wait**:**On any thread that participates in an event loop or executor — you will block it.
sync_waitis for top-level synchronization (main, tests, leaf utilities), not pipeline composition.When you don’t need the result. Use
exec::start_detachedorstdexec::spawnfor fire-and-forget.
See also
stdexec::sync_wait_with_variant — sync_wait for multi-completion senders
See also
exec::start_detached — fire-and-forget consumer (no result)
See also
stdexec::spawn — fire-and-forget into a scope
See also
stdexec::spawn_future — spawn into a scope and observe via a sender
Public Functions
-
template<stdexec::sender_in<stdexec::__sync_wait::__env> _CvSender>
inline auto operator()(_CvSender &&__sndr) const Connect
__sndrto an internal receiver, start the operation, and drive arun_loopuntil completion.- Template Parameters:
_CvSender – A type satisfying
stdexec::sender_infor the built-insync_waitenvironment.- Parameters:
__sndr – The sender to drive to completion. Must have exactly one
set_value_tcompletion signature.- Throws:
The – error datum, if
__sndrcompletes withset_error(rethrown viastd::rethrow_exceptionforstd::exception_ptr, viastd::system_errorforstd::error_code, or directly otherwise).- Returns:
std::optional<std::tuple<Vs...>>whereVs… are the value-completion datum types of__sndr. The optional is engaged onset_value, disengaged onset_stopped.- Pre:
__sndrmust have exactly oneset_value_tcompletion signature, otherwise the program is ill-formed with a diagnostic pointing atsync_wait_with_variant.
-
sync_wait_t const stdexec::sync_wait
The customization point object for the
sync_waitsender consumer.sync_waitis an instance of sync_wait_t. See sync_wait_t for the full description, completion-behavior table, and a usage example.
sync_wait_with_variant — block until a multi-completion sender completes
Like sync_wait but for senders that may complete with more than one
value-completion shape. Returns an engaged
std::optional<std::variant<std::tuple<...>...>> on success.
-
struct sync_wait_with_variant_t
A sender consumer that synchronously blocks the calling thread until a multi-value-completion sender completes, returning the result as a variant of tuples.
sync_wait_with_variantis the multi-completion sibling of sync_wait_t. A sender that can succeed in more than one way — for example, an algorithm that may complete with either anintor astd::string— cannot be passed tosync_wait, because the latter returns a single fixed tuple type.sync_wait_with_variantaccepts such senders and returns the result as astd::variantof all the possible value-tuple shapes.// sndr completes with either set_value_t(int) or set_value_t(std::string). auto opt = stdexec::sync_wait_with_variant(std::move(sndr)); if (opt) { std::visit([](auto&& tup) { // tup is either std::tuple<int> or std::tuple<std::string>. }, *opt); }
See [exec.sync.wait.var] in the C++26 working draft for the normative specification.
Completion behavior.
Given an input sender
sndrwith value-completion signaturesset_value_t(Vs1...), set_value_t(Vs2...), ..., the return type isstd::optional<std::variant<std::tuple<Vs1...>, std::tuple<Vs2...>, ...>>
The handling of
set_error_tandset_stopped_tmatches sync_wait_t : errors are thrown,set_stoppedyields a disengaged optional.When to use
sync_wait_with_variantvs.sync_wait**:** Usesync_waitwhen the sender has exactly one value-completion shape; usesync_wait_with_variantotherwise.sync_wait'sstatic assertion will steer you here if needed.See also
stdexec::sync_wait — for single-value-completion senders
See also
stdexec::into_variant — adaptor that collapses multi-completion senders into a variant
Public Functions
-
template<stdexec::sender_in<stdexec::__sync_wait::__env> _CvSender>
inline auto operator()(_CvSender &&__sndr) const -> decltype(auto) Connect
__sndr, start the operation, drive arun_loopuntil completion, and return the result as a variant of tuples.- Template Parameters:
_CvSender – A type satisfying
stdexec::sender_infor thesync_waitenvironment.- Parameters:
__sndr – The sender to drive to completion. May have any number of
set_value_tcompletion signatures.- Throws:
The – error datum, if
__sndrcompletes withset_error, using the same rules as sync_wait_t.- Returns:
std::optional<std::variant<std::tuple<Vs1...>, …>> engaged onset_value, disengaged onset_stopped.
-
template<stdexec::sender_in<stdexec::__sync_wait::__env> _CvSender>
-
sync_wait_with_variant_t const stdexec::sync_wait_with_variant
The customization point object for the
sync_wait_with_variantsender consumer.sync_wait_with_variantis an instance of sync_wait_with_variant_t. See sync_wait_with_variant_t for the full description and a usage example.
start_detached (extension) — fire and forget
Eagerly starts a sender and discards its result. The operation state is heap-allocated and cleans itself up on completion. stdexec extension — not part of the C++26 working draft. For the standardized scope-tracked equivalent, see spawn.
-
struct start_detached_t
A sender consumer that eagerly starts a sender and forgets it.
start_detachedconnects its argument sender to a built-in receiver and starts the resulting operation immediately, allocating the operation state on the heap (using an allocator from the optional environment) so it can outlive the call. The completion of the sender deallocates the operation state and discards the result; nothing is returned to the caller.Use
start_detachedfor top-level fire-and-forget work that has no caller waiting on its result and no enclosing async scope — for example, kicking off a logging or telemetry pipeline frommain(), or a one-shot background task at program startup. For fire-and-forget work that should be tracked by a scope (so the scope can be joined at shutdown), preferstdexec::spawn. For top-level waiting, usestdexec::sync_wait.exec::start_detached(stdexec::just(42) | stdexec::then([](int x) { std::println("background work produced {}", x); }));
Completion behavior.
The sender must complete via
set_valueorset_stopped— both are accepted and the result is discarded. The sender must not complete viaset_error:there is no caller to deliver the error to, and an error completion is therefore considered a contract violation. The implementation enforces this with a static assertion when possible; if the sender’s completion signatures includeset_error_tthe program is ill-formed.Allocator support.
The two-argument overload accepts an environment from which an allocator can be queried (via
stdexec::get_allocator). That allocator is used to allocate the operation state, so callers can avoid the default globalnewfor hot paths.Cancellation.
start_detacheddoes not arrange for cancellation of the spawned work. If the operation observes a stop token via the environment, it can self-cancel; otherwise the work runs to natural completion.See also
stdexec::sync_wait — top-level synchronous wait that returns the result
See also
stdexec::spawn — fire-and-forget into a scope (standardized in C++26)
See also
stdexec::spawn_future — spawn into a scope and observe via a sender
Note
start_detachedis an stdexec extension. It is not part of the C++26 working draft. The standardized way to spawn fire-and-forget work isstdexec::spawn(see [exec.spawn]), which requires an async scope to take ownership of the operation.Public Functions
-
template<stdexec::sender_in<stdexec::__root_env> _Sender>
inline void operator()(_Sender &&__sndr) const Eagerly start
__sndr; allocate its operation state on the heap with the default allocator.- Template Parameters:
_Sender – A sender type with no
set_error_tcompletions.- Parameters:
__sndr – The sender to launch. Forwarded into the heap-allocated operation state.
- Pre:
__sndrmust not be able to complete withset_error.
-
template<class _Env, stdexec::sender_in<stdexec::__as_root_env_t<_Env>> _Sender>
inline void operator()(_Sender &&__sndr, _Env &&__env) const Eagerly start
__sndr; use the allocator from__envto allocate the operation state.- Template Parameters:
_Env – An environment type. Queried with
stdexec::get_allocatorfor an allocator; falls back tostd::allocatorif absent._Sender – A sender type with no
set_error_tcompletions.
- Parameters:
__sndr – The sender to launch.
__env – The environment used both for the allocator query and for the receiver’s environment.
- Pre:
__sndrmust not be able to complete withset_error.
-
template<stdexec::sender_in<stdexec::__root_env> _Sender>
-
constexpr start_detached_t experimental::execution::start_detached
The customization point object for the
start_detachedsender consumer.start_detachedis an instance of start_detached_t. See start_detached_t for the full description and a usage example.
spawn — fire and forget into an async scope
Eagerly starts a sender and ties its lifetime to a given async scope.
The argument sender must not be able to complete with set_error.
The standardized way to launch fire-and-forget work whose lifetime
should be tracked.
Warning
doxygenstruct: Cannot find class “stdexec::spawn_t” in doxygen xml output for project “stdexec” from directory: /home/runner/work/stdexec/stdexec/build/docs/build/doxygen/xml
-
constexpr spawn_t stdexec::spawn
The customization point object for the
spawnsender consumer.spawnis an instance of spawn_t. See spawn_t for the full description, scope semantics, and a usage example.
spawn_future — fire and observe via a sender
Like spawn but additionally returns a sender that completes when the
spawned operation completes. The returned sender is a one-shot
observer of work that is already running, not a re-runnable handle.
Warning
doxygenstruct: Cannot find class “stdexec::spawn_future_t” in doxygen xml output for project “stdexec” from directory: /home/runner/work/stdexec/stdexec/build/docs/build/doxygen/xml
-
constexpr spawn_future_t stdexec::spawn_future
The customization point object for the
spawn_futuresender consumer.spawn_futureis an instance of spawn_future_t. See spawn_future_t for the full description, the eager-start semantics, and a usage example.
Utilities
TODO: Add utilities section