std::execution: Sender
std::execution offers three types of senders: factories, adapters, and consumers. I’ll take a closer look at these today.
Most of the following content is from proposal P2300R10. I will try to represent it more concisely.
Sender Factory
A sender factory is an algorithm that takes no senders as parameters and returns a sender.
execution::schedule
execution::sender auto schedule(
execution::scheduler auto scheduler
);
Returns a sender, which starts on the provided scheduler.
execution::just
execution::sender auto just(
auto ...&& values
);
Returns a sender, which sends the provided values.
execution::just_error
execution::sender auto just_error(
auto && error
);
Returns a sender, which completes with the specific error.
execution::just_stopped
execution::sender auto just_stopped();
Returns a sender, which completes immediately by calling the receiver’s set_stopped.
execution::read_env
execution::sender auto read_env(auto tag);
Returns a sender that reaches into a receiver’s environment and pulls out the current value associated with the environment value tag. It then sends the value read back to the receiver through the value channel. For instance, read_env(get_scheduler) is a sender that asks the receiver for the currently suggested scheduler and passes it to the receiver’s set_value completion-signal.
This can be useful when scheduling nested dependent work. The following sender pulls the current scheduler into the value channel and then schedules more work onto it.
Sender Adaptor
A sender adaptor is an algorithm that takes one or more senders as parameters and returns a sender.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
Sender adaptors are lazy. Sender consumers such as this_thread::sync_wait start senders.
execution::continues_on
execution::sender auto continues_on(
execution::sender auto input,
execution::scheduler auto scheduler
);
Returns a sender describing the transition from the execution agent of the input sender to the execution agent of the target scheduler.
execution::then
execution::sender auto then(
execution::sender auto input,
std::invocable<values-sent-by(input)...> function
);
then returns a sender describing the task graph described by the input sender, with an added node of invoking the provided function with the values sent by the input sender as arguments.
execution::upon_*
execution::sender auto upon_error(
execution::sender auto input,
std::invocable<errors-sent-by(input)...> function
);
execution::sender auto upon_stopped(
execution::sender auto input,
std::invocable auto function
);
upon_error and upon_stopped are similar to then, but where then works with values sent by the input sender, upon_error works with errors, and upon_stopped is invoked when the “stopped” signal is sent.
A nice example about then,upon_error, and upon_stopped has the prototype library stdexec.
The following example shows an HTTP request handler.(https://github.com/NVIDIA/stdexec/blob/main/examples/server_theme/then_upon.cpp#L151)
/*
* Copyright (c) 2022 Lucian Radu Teodorescu
*
* Licensed under the Apache License Version 2.0 with LLVM Exceptions
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://llvm.org/LICENSE.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Handler for the "classify" request type
ex::sender auto handle_classify_request(const http_request& req) {
return
// start with the input buffer
ex::just(req)
// extract the image from the input request
| ex::then(extract_image)
// analyze the content of the image and classify it
// we are doing the processing on the same thread
| ex::then(do_classify)
// handle errors
| ex::upon_error(on_classification_error)
// handle cancellation
| ex::upon_stopped(on_classification_cancelled)
// transform this into a response
| ex::then(to_response)
// done
;
}
The function extracts images and returns an ex::sender object, representing an asynchronous operation that can be composed of other operations.
The function takes a constant reference to an http_request req. The processing pipeline begins with the ex::just(req) function, creating a sender that starts with the HTTP request’s input buffer.
Error handling is incorporated into the pipeline using the ex::upon_error function, which specifies the on_classification_error function to handle any errors that occur during the classification process.
The pipeline then uses the ex::then function to chain a series of operations. The first operation, extract_image, extracts the image from the input request. The next operation, do_classify, classifies the extracted image’s content. This processing is done on the same thread.
Similarly, the ex::upon_stopped function is used to handle the cancellation of the operation, specifying the on_classification_cancelled function.
Finally, the to_response function is used to transform the classification result into an HTTP response. This transformation is also done using the ex::then function.
This sender adaptor changes the execution resource on which the sender runs. It does not adapt the sender.
This code demonstrates a composable and asynchronous processing pipeline for gracefully handling HTTP requests, classifying images, and errors and cancellations.
execution::starts_on
execution::sender auto starts_on(
execution::scheduler auto sched,
execution::sender auto snd
);
What’s Next?
std::execution has more sender adaptors to offer: execution::let_*, execution::into_variant, execution::stopped_as_optional, execution::stopped_as_error, execution::bulk, execution::split, and execution::when_all.