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.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Embedded Programming with Modern C++": January 2025
  • "Generic Programming (Templates) with C++": February 2025
  • "Clean Code: Best Practices for Modern C++": May 2025
  • 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.

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org

    Modernes C++ Mentoring,