### Basic CMake Project Setup Source: https://github.com/nvidia/stdexec/blob/main/test_package/CMakeLists.txt Initializes a CMake project and enables testing. This is a standard starting point for CMake projects. ```cmake cmake_minimum_required(VERSION 3.25.0) project(PackageTest) enable_testing() ``` -------------------------------- ### Build stdexec tests and examples Source: https://github.com/nvidia/stdexec/blob/main/README.md Use these CMake commands to build the test suite and examples for the stdexec library. Ensure you have Ninja and CMake installed. ```bash cmake -S . -B build -G Ninja cmake --build build ctest --test-dir build ``` -------------------------------- ### stdexec::foo CPO User Guide Example Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Demonstrates the simplest possible usage of a stdexec CPO, 'foo', showing how to create a sender and wait for its result synchronously. This pattern is useful for illustrating the basic functionality of a CPO. ```cpp auto sndr = stdexec::just(21) | stdexec::foo([](int x) { return x * 2; }); auto [v] = stdexec::sync_wait(std::move(sndr)).value(); // v == 42 ``` -------------------------------- ### Build Documentation and Examples Source: https://github.com/nvidia/stdexec/blob/main/CMakeLists.txt Options to control the building of documentation and examples for stdexec. ```cmake option(STDEXEC_BUILD_DOCS "Build stdexec documentation" OFF) option(STDEXEC_BUILD_EXAMPLES "Build stdexec examples" ON) ``` -------------------------------- ### Build Fibonacci TBB Example Source: https://github.com/nvidia/stdexec/blob/main/examples/CMakeLists.txt Builds an executable for a Fibonacci benchmark example using TBB. It links against stdexec example settings and the TBB execution backend. ```cmake add_executable(example.benchmark.fibonacci benchmark/fibonacci.cpp) target_link_libraries(example.benchmark.fibonacci PRIVATE stdexec_example_settings STDEXEC::tbbexec) ``` -------------------------------- ### Build Asio Thread Pool Example Source: https://github.com/nvidia/stdexec/blob/main/examples/CMakeLists.txt Builds an executable for an Asio thread pool benchmark example. It links against stdexec example settings, the Asio execution backend, and optionally TBB. ```cmake add_executable(example.benchmark.asio_thread_pool benchmark/asio_thread_pool.cpp) target_link_libraries(example.benchmark.asio_thread_pool PRIVATE stdexec_example_settings STDEXEC::asioexec $) ``` -------------------------------- ### Full stdexec Example: Custom Sender Adaptor Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md A complete, compilable example demonstrating the implementation of a custom sender adaptor 'simple_then' which chains operations. It includes the sender, receiver, factory, and a usage example with sync_wait. ```cpp #include #include #include #include #include // ---------- The wrapping receiver ----------------------------------- template struct simple_then_receiver { using receiver_concept = stdexec::receiver_tag; R rcvr_; Fn fn_; template void set_value(Vs&&... vs) noexcept { try { stdexec::set_value( std::move(rcvr_), std::invoke(std::move(fn_), static_cast(vs)...)); } catch (...) { stdexec::set_error(std::move(rcvr_), std::current_exception()); } } template void set_error(E&& e) noexcept { stdexec::set_error(std::move(rcvr_), static_cast(e)); } void set_stopped() noexcept { stdexec::set_stopped(std::move(rcvr_)); } auto get_env() const noexcept { return stdexec::get_env(rcvr_); } }; // ---------- The sender ---------------------------------------------- template struct simple_then_sender { using sender_concept = stdexec::sender_tag; using completion_signatures = stdexec::completion_signatures< stdexec::set_value_t(int), stdexec::set_error_t(std::exception_ptr), stdexec::set_stopped_t()>; Sndr sndr_; Fn fn_; template auto connect(R rcvr) && { return stdexec::connect( std::move(sndr_), simple_then_receiver{std::move(rcvr), std::move(fn_)}); } }; // ---------- The factory --------------------------------------------- template auto simple_then(Sndr&& sndr, Fn&& fn) { return simple_then_sender, std::decay_t>{ static_cast(sndr), static_cast(fn)}; } // ---------- Try it out ---------------------------------------------- int main() { auto pipeline = simple_then( stdexec::just(21), [](int x) { return x * 2; }); auto [v] = stdexec::sync_wait(std::move(pipeline)).value(); assert(v == 42); } ``` -------------------------------- ### Build TBB Thread Pool Example Source: https://github.com/nvidia/stdexec/blob/main/examples/CMakeLists.txt Builds an executable for a TBB thread pool benchmark example. It links against stdexec example settings and the TBB execution backend. ```cmake add_executable(example.benchmark.tbb_thread_pool benchmark/tbb_thread_pool.cpp) target_link_libraries(example.benchmark.tbb_thread_pool PRIVATE stdexec_example_settings STDEXEC::tbbexec) ``` -------------------------------- ### Define CPU Example Target Source: https://github.com/nvidia/stdexec/blob/main/examples/nvexec/CMakeLists.txt Configures an executable for a CPU example, linking necessary libraries and setting language properties. ```cmake function(def_cpu_example example) split(${example} target source) add_executable(${target} ${source}) set_executable_output_name(${target}) target_link_libraries(${target} PRIVATE stdpar_multicore STDEXEC::stdexec stdexec_executable_flags ) set_source_files_properties(${source} PROPERTIES LANGUAGE ${_lang_cxx}) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") target_link_options(${target} PRIVATE -lc++abi) endif() endfunction() ``` -------------------------------- ### Complete stdexec Scheduler Example Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md A full, compilable example demonstrating a custom inline scheduler, including operation state, schedule sender, and usage in `main` with `stdexec::schedule`, `stdexec::then`, `stdexec::sync_wait`, and `stdexec::starts_on`. It also includes a static assertion to verify scheduler concept satisfaction. ```cpp #include #include #include // ---------- Operation state ----------------------------------------- template struct simple_inline_opstate { using operation_state_concept = stdexec::operation_state_tag; R rcvr_; explicit simple_inline_opstate(R rcvr) noexcept : rcvr_(std::move(rcvr)) {} simple_inline_opstate(simple_inline_opstate&&) = delete; void start() noexcept { stdexec::set_value(std::move(rcvr_)); } }; // ---------- Schedule-sender ----------------------------------------- struct simple_inline_schedule_sender { using sender_concept = stdexec::sender_tag; using completion_signatures = stdexec::completion_signatures< stdexec::set_value_t()>; template auto connect(R rcvr) const noexcept { return simple_inline_opstate{std::move(rcvr)}; } }; // ---------- Scheduler ----------------------------------------------- struct simple_inline_scheduler { auto schedule() const noexcept { return simple_inline_schedule_sender{}; } bool operator==(simple_inline_scheduler const&) const noexcept = default; }; // ---------- Try it out ---------------------------------------------- int main() { // Use schedule() directly: auto s = stdexec::schedule(simple_inline_scheduler{}) | stdexec::then([] { return 42; }); auto [v] = stdexec::sync_wait(std::move(s)).value(); assert(v == 42); // Use starts_on to run an entire pipeline on the scheduler: auto t = stdexec::starts_on( simple_inline_scheduler{}, stdexec::just(21) | stdexec::then([](int x) { return x * 2; })); auto [w] = stdexec::sync_wait(std::move(t)).value(); assert(w == 42); // The scheduler concept is satisfied: static_assert(stdexec::scheduler); } ``` -------------------------------- ### Build Taskflow Thread Pool Example Source: https://github.com/nvidia/stdexec/blob/main/examples/CMakeLists.txt Builds an executable for a Taskflow thread pool benchmark example. It links against stdexec example settings, the Taskflow execution backend, and optionally TBB. ```cmake add_executable(example.benchmark.taskflow_thread_pool benchmark/taskflow_thread_pool.cpp) target_link_libraries(example.benchmark.taskflow_thread_pool PRIVATE stdexec_example_settings STDEXEC::taskflowexec $) ``` -------------------------------- ### Example Usage of simple_then Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md Demonstrates how to use the custom `simple_then` sender adaptor with `stdexec::sync_wait`. This shows the typical pipeline creation and execution. ```cpp auto pipeline = simple_then( stdexec::just(21), [](int x) { return x * 2; }); auto [v] = stdexec::sync_wait(std::move(pipeline)).value(); // v == 42 ``` -------------------------------- ### Build Nested TBB Thread Pool Example Source: https://github.com/nvidia/stdexec/blob/main/examples/CMakeLists.txt Builds an executable for a nested TBB thread pool benchmark example. It links against stdexec example settings and the TBB execution backend. ```cmake add_executable(example.benchmark.tbb_thread_pool_nested benchmark/tbb_thread_pool_nested.cpp) target_link_libraries(example.benchmark.tbb_thread_pool_nested PRIVATE stdexec_example_settings STDEXEC::tbbexec) ``` -------------------------------- ### Define GPU Example Target Source: https://github.com/nvidia/stdexec/blob/main/examples/nvexec/CMakeLists.txt Configures an executable for a GPU example, linking necessary libraries and setting language properties. ```cmake function(def_gpu_example example) split(${example} target source) add_executable(${target} ${source}) set_executable_output_name(${target}) target_link_libraries(${target} PRIVATE nvexec_example STDEXEC::nvexec stdexec_executable_flags ) set_source_files_properties(${source} PROPERTIES LANGUAGE ${_lang_gpu}) endfunction() ``` -------------------------------- ### Define Example Target Source: https://github.com/nvidia/stdexec/blob/main/examples/CMakeLists.txt Defines a build target for a specific example executable. This is typically used within a loop to process a list of example definitions. ```cmake def_example(${example}) ``` -------------------------------- ### Implement Operation State's start() Member Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md Implement the start() member for an operation state. This function must be noexcept, return void, and commit to never throwing. It is responsible for initiating the operation's execution. ```cpp template struct my_opstate { using operation_state_concept = stdexec::operation_state_tag; // Immovable after construction: my_opstate(my_opstate&&) = delete; R rcvr_; int value_; void start() noexcept { stdexec::set_value(std::move(rcvr_), value_); } }; ``` -------------------------------- ### Build Distributed MPI Example Source: https://github.com/nvidia/stdexec/blob/main/examples/nvexec/CMakeLists.txt Builds a distributed MPI example executable using the defined `def_mpi_example` function. This specific example is for Maxwell architecture and includes an optional OVERLAP mode. ```cmake def_mpi_example(example.nvexec.maxwell_distributed maxwell_distributed.cu) ``` ```cmake def_mpi_example(example.nvexec.maxwell_distributed_ov maxwell_distributed.cu OVERLAP) ``` -------------------------------- ### Connect and Start an Operation Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md Connect a sender to a receiver to create an operation state, then explicitly start the operation. Operation states are immovable and must be kept alive until completion. ```cpp auto op = stdexec::connect(sndr, MyReceiver{}); // Connect sender to receiver stdexec::start(op); // Start the operation ``` -------------------------------- ### CPO Per-Overload Comment Example Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Document each `operator()` overload with a short comment block including `@brief`, `@tparam`, `@param`, `@returns`, and `@pre`. This example shows the documentation for a typical overload. ```cpp //! @brief Construct a sender that adapts @c __sndr by invoking @c __fun //! with each value-completion argument pack it produces. //! //! @tparam _Sender A type satisfying the @c stdexec::sender concept. //! @tparam _Fun A decayed, move-constructible callable type //! (satisfying the internal __movable_value concept). //! //! @param __sndr The predecessor sender ... //! @param __fun The function (or callable) to invoke ... //! //! @returns A sender that, when connected to a receiver and started, ... //! //! @pre @c __fun must be invocable with every value-completion argument //! pack of @c __sndr ... template constexpr auto operator()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender auto; ``` -------------------------------- ### Spawning Work and Observing its Result with spawn_future Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Use stdexec::spawn_future to start a sender into a scope and get a sender that delivers the spawned operation's result. The work starts eagerly when spawn_future is called. ```cpp exec::async_scope scope; auto future = stdexec::spawn_future(stdexec::just(42) | stdexec::then([](int x){ return x * 2; }), scope.get_token()); // The work is already running. Do something else here ... auto [v] = stdexec::sync_wait(std::move(future)).value(); // v == 84 stdexec::sync_wait(scope.join()); ``` -------------------------------- ### `on` — run on a scheduler and return to the original Source: https://github.com/nvidia/stdexec/blob/main/docs/source/reference/index.md The “go there, do work, come back” adaptor. Runs work on a different scheduler and then returns to where it started. ```APIDOC ## `on` — run on a scheduler and return to the original ### Description 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](../user/index.md#userguide-on) for guidance on when to reach for which form. ### Method APPLIES_TO_SENDER ### Endpoint N/A (SDK Method) ### Parameters N/A ### Request Example N/A ### Response N/A ``` -------------------------------- ### `starts_on` — run a sender on a scheduler Source: https://github.com/nvidia/stdexec/blob/main/docs/source/reference/index.md Produces a sender that runs an input sender starting on a given scheduler’s execution resource, delivering the completion on that same resource. ```APIDOC ## `starts_on` — run a sender on a scheduler ### Description 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). ### Method APPLIES_TO_SENDER ### Endpoint N/A (SDK Method) ### Parameters N/A ### Request Example N/A ### Response N/A ``` -------------------------------- ### Install stdexec with CPM Source: https://github.com/nvidia/stdexec/blob/main/README.md Use CPM.cmake to automatically fetch and configure stdexec. Ensure CPM.cmake is available in your project. ```cmake CPMAddPackage( NAME stdexec GITHUB_REPOSITORY NVIDIA/stdexec GIT_TAG main # or a specific tag ) target_link_libraries(my_target PRIVATE STDEXEC::stdexec) ``` -------------------------------- ### Compose Async Pipeline with Sender Algorithms Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Demonstrates creating an asynchronous pipeline using `stdexec::just`, `stdexec::on`, `stdexec::then`, and `stdexec::sync_wait`. This example shows how to chain operations and retrieve the final result. ```cpp auto pipeline = stdexec::just(42) | stdexec::on(some_scheduler, stdexec::then([](int i) { return i * 2; })) | stdexec::then([](int i) { return i + 1; }); auto [result] = stdexec::sync_wait(std::move(pipeline)).value(); ``` -------------------------------- ### Run Concurrent Work with stdexec Source: https://github.com/nvidia/stdexec/blob/main/README.md This example demonstrates building and launching a lazy pipeline of three concurrent tasks using stdexec's `when_all` and `on` primitives. It requires C++20 or later and the stdexec library. ```c++ #include #include namespace ex = stdexec; int main() { auto sched = ex::get_parallel_scheduler(); auto fun = [](int i) { return i * i; }; // Build a lazy pipeline: three squares, computed in parallel. auto work = ex::when_all(ex::on(sched, ex::just(0) | ex::then(fun)), ex::on(sched, ex::just(1) | ex::then(fun)), ex::on(sched, ex::just(2) | ex::then(fun))); // Launch the work and wait for the result. auto [i, j, k] = ex::sync_wait(std::move(work)).value(); std::printf("%d %d %d\n", i, j, k); // prints "0 1 4" } ``` -------------------------------- ### Obtain and Schedule with System Scheduler Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Obtain the default system scheduler and create a sender that schedules work on it. The sender, when started, will execute on the scheduler's context. ```cpp auto sched = stdexec.get_parallel_scheduler(); // Obtain the default system scheduler auto sndr = stdexec.schedule(sched); // Create a sender from the scheduler ``` -------------------------------- ### Create and Wait for a Simple Sender Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Create a sender that immediately yields a value and then use sync_wait to start the computation and retrieve the result. Ensure the result is as expected. ```cpp auto sndr = stdexec.just(42); // Sender that yields 42 immediately auto [result] = stdexec.sync_wait(sndr).value(); // Start the work & wait for the result assert(result.value() == 42); ``` -------------------------------- ### Define MPI Example Target Source: https://github.com/nvidia/stdexec/blob/main/examples/nvexec/CMakeLists.txt Defines a CMake function to create an executable for an MPI example. It links necessary libraries and sets GPU language properties. ```cmake function(def_mpi_example target source) add_executable(${target} ${source}) set_executable_output_name(${target}) target_link_libraries(${target} PRIVATE nvexec_example STDEXEC::nvexec stdexec_executable_flags ) target_compile_definitions(${target} PRIVATE ${ARGN}) set_source_files_properties(${source} PROPERTIES LANGUAGE ${_lang_gpu}) endfunction() ``` -------------------------------- ### Custom Scheduler with Noticing Domain Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md Implements a custom scheduler that publishes a domain to observe scheduler consultations at connect time. This example demonstrates the plumbing for custom domains and schedulers. ```cpp #include #include #include #include // Module-level state so we can observe whether our domain was consulted. static std::atomic g_transform_count{0}; // ---------- The domain ---------------------------------------------- struct my_domain { // The framework calls this at connect time. We don't rewrite the // sender — we just record that we were consulted, then forward. template static auto transform_sender(OpTag, Sndr&& sndr, Env const&) { g_transform_count.fetch_add(1, std::memory_order_relaxed); return static_cast(sndr); } }; // ---------- A scheduler that publishes my_domain -------------------- template struct my_sched_opstate { using operation_state_concept = stdexec::operation_state_tag; R rcvr_; explicit my_sched_opstate(R rcvr) noexcept : rcvr_(std::move(rcvr)) {} my_sched_opstate(my_sched_opstate&&) = delete; void start() noexcept { stdexec::set_value(std::move(rcvr_)); } }; struct my_scheduler; // forward struct my_schedule_sender { using sender_concept = stdexec::sender_tag; using completion_signatures = stdexec::completion_signatures; struct attrs_t { auto query(stdexec::get_completion_scheduler_t) const noexcept -> my_scheduler; auto query(stdexec::get_completion_domain_t) const noexcept { return my_domain{}; } }; auto get_env() const noexcept { return attrs_t{}; } template auto connect(R rcvr) const noexcept { return my_sched_opstate{std::move(rcvr)}; } }; struct my_scheduler { auto schedule() const noexcept { return my_schedule_sender{}; } // A scheduler is its own completion scheduler, and claims my_domain // as the domain on which it completes. auto query(stdexec::get_completion_scheduler_t) const noexcept { return *this; } auto query(stdexec::get_completion_domain_t) const noexcept { return my_domain{}; } bool operator==(my_scheduler const&) const noexcept = default; }; inline auto my_schedule_sender::attrs_t::query( stdexec::get_completion_scheduler_t) const noexcept -> my_scheduler { return {}; } // ---------- Try it out ---------------------------------------------- int main() { static_assert(stdexec::scheduler); auto count_before = g_transform_count.load(); auto pipeline = stdexec::schedule(my_scheduler{}) | stdexec::then([] { return 42; }); auto [v] = stdexec::sync_wait(std::move(pipeline)).value(); assert(v == 42); // The domain was consulted at least once at connect time: assert(g_transform_count.load() > count_before); } ``` -------------------------------- ### Install stdexec with add_subdirectory Source: https://github.com/nvidia/stdexec/blob/main/README.md Clone the stdexec repository alongside your project and add it as a subdirectory in CMake. Link against the STDEXEC::stdexec target. ```bash git clone https://github.com/NVIDIA/stdexec.git ``` ```cmake add_subdirectory(stdexec) target_link_libraries(my_target PRIVATE STDEXEC::stdexec) ``` -------------------------------- ### spawn Source: https://github.com/nvidia/stdexec/blob/main/docs/source/reference/index.md Eagerly starts a sender and ties its lifetime to a given async scope. This is the standardized way to launch fire-and-forget work whose lifetime should be tracked. ```APIDOC ## spawn ### Description 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`. ### Note The standardized way to launch fire-and-forget work whose lifetime should be tracked. ``` -------------------------------- ### start_detached Source: https://github.com/nvidia/stdexec/blob/main/docs/source/reference/index.md Eagerly starts a sender and discards its result. The operation state is heap-allocated and cleans itself up on completion. This is an stdexec extension. ```APIDOC ## start_detached ### Description Eagerly starts a sender and discards its result. The operation state is heap-allocated and cleans itself up on completion. ### Note **stdexec extension** — not part of the C++26 working draft. For the standardized scope-tracked equivalent, see [spawn](#ref-spawn). ``` -------------------------------- ### Starting a Sender with start_detached Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Use exec::start_detached for fire-and-forget operations where the sender must not complete with set_error. The operation state is heap-allocated and self-destructs. ```cpp exec::start_detached( stdexec::just(42) | stdexec::then([](int x) { std::println("background: {}", x); })); ``` -------------------------------- ### Run Senders Concurrently with `when_all` Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Use `when_all` to run multiple senders concurrently. It returns a single sender that, when started, executes all input senders. Upon completion of all inputs, it value-completes with a tuple concatenating all input values. For true parallelism, ensure each branch is started on a parallel scheduler using `starts_on`. ```cpp auto sndr = stdexec::when_all( stdexec::just(1), stdexec::just(2.5), stdexec::just(std::string{"x"})); auto [i, d, s] = stdexec::sync_wait(std::move(sndr)).value(); // i == 1, d == 2.5, s == "x" ``` ```cpp auto cpu = stdexec::get_parallel_scheduler(); auto sndr = stdexec::when_all( stdexec::starts_on(cpu, sndr_a), stdexec::starts_on(cpu, sndr_b), stdexec::starts_on(cpu, sndr_c)); ``` -------------------------------- ### Schedule Pipeline on Scheduler with stdexec::schedule Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Use `stdexec::schedule` to start a pipeline on a specific execution context. It returns a sender that, when started, completes from the context of the given scheduler, allowing subsequent operations to run on that context. ```cpp auto sched = stdexec::get_parallel_scheduler(); auto sndr = stdexec::schedule(sched) // hop onto sched | stdexec::then([] { return 42; }); // ... and compute on it auto [v] = stdexec::sync_wait(std::move(sndr)).value(); // v == 42, computed on the parallel scheduler ``` -------------------------------- ### Transform Sender Example Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md This pattern inspects the sender's tag and rewrites senders for specific domains. Use this when implementing custom sender transformations. ```cpp template static auto transform_sender(OpTag op, Sndr&& sndr, Env const& env) { using tag = stdexec::tag_of_t; if constexpr (std::same_as) { return /* my-domain's version of then(sndr, fn) */; } else if constexpr (std::same_as) { return /* my-domain's version of bulk(...) */; } else { return static_cast(sndr); // pass through } } ``` -------------------------------- ### Configure Relacy Interface Library Source: https://github.com/nvidia/stdexec/blob/main/test/rrd/CMakeLists.txt Defines an INTERFACE library for Relacy, specifying include directories for both build and install contexts. This allows other targets to easily use Relacy. ```cmake add_library(relacy INTERFACE) target_include_directories(relacy INTERFACE $ $ $ ) ``` -------------------------------- ### CPO Inline Section Heading Example Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Use bold inline headings for sub-sections within CPO documentation. This example shows the heading for 'Completion signatures.' ```cpp //! **Completion signatures.** //! //! Prose here, including code blocks, can span multiple paragraphs… ``` -------------------------------- ### Use starts_on to begin work on a specific scheduler Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Use `starts_on` when you want a pipeline to run on a specific scheduler and remain there. The scheduler is provided as the first argument, and it has no pipe form. The completion is delivered on the same context. ```cpp auto sched = stdexec::get_parallel_scheduler(); auto sndr = stdexec::starts_on(sched, stdexec::just(21) | stdexec::then([](int x) { return x * 2; })); auto [v] = stdexec::sync_wait(std::move(sndr)).value(); // v == 42, computed on `sched` ``` ```cpp // starts_on(sch, sndr) is semantically equivalent to: stdexec::schedule(sch) | stdexec::let_value([sndr]() mutable { return std::move(sndr); }); ``` -------------------------------- ### Operation state for simple_inline_scheduler Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md Defines the operation state for the inline scheduler. It holds the receiver and delivers an empty value completion synchronously upon calling `start()`. The `start()` method must be `noexcept` and returns `void`. The move constructor is deleted to assert immovability. ```cpp template struct simple_inline_opstate { using operation_state_concept = stdexec::operation_state_tag; R rcvr_; explicit simple_inline_opstate(R rcvr) noexcept : rcvr_(std::move(rcvr)) {} // Operation states must remain at a stable address once started: simple_inline_opstate(simple_inline_opstate&&) = delete; void start() noexcept { stdexec::set_value(std::move(rcvr_)); } }; ``` -------------------------------- ### Add CUDA Subdirectory Source: https://github.com/nvidia/stdexec/blob/main/examples/CMakeLists.txt Adds the 'nvexec' subdirectory to the build if the STDEXEC_ENABLE_CUDA flag is set, enabling CUDA-related examples. ```cmake if(STDEXEC_ENABLE_CUDA) add_subdirectory(nvexec) endif() ``` -------------------------------- ### User-visible API for simple_inline_scheduler Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md This demonstrates the typical usage pattern for the custom inline scheduler, showing how to schedule a continuation and wait for its result synchronously. ```cpp simple_inline_scheduler sched; auto s = stdexec::schedule(sched) | stdexec::then([] { return 42; }); auto [v] = stdexec::sync_wait(std::move(s)).value(); // v == 42 ``` -------------------------------- ### Build stdexec Documentation Locally Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Command to rebuild the documentation locally. Ensure CMake is configured with STDEXEC_BUILD_DOCS=ON. ```sh cmake -B build/docs -S . -DSTDEXEC_BUILD_DOCS=ON \ -DSTDEXEC_BUILD_EXAMPLES=OFF -DSTDEXEC_BUILD_TESTS=OFF cmake --build build/docs --target docs ``` -------------------------------- ### Find P2300 Package Source: https://github.com/nvidia/stdexec/blob/main/test_package/CMakeLists.txt Finds the P2300 package, which is required for the project. Ensure P2300 is installed and discoverable by CMake. ```cmake find_package(P2300 REQUIRED) ``` -------------------------------- ### CPO Type Brief Description Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Use this for the initial one-sentence description of what the adaptor, factory, or consumer does abstractly. ```cpp //! @brief A pipeable sender adaptor that transforms a predecessor sender's //! value completion by invoking a callable on the values it produces. struct foo_t { /* … */ }; ``` -------------------------------- ### Configure NUMA Affinity for Static Thread Pool Source: https://github.com/nvidia/stdexec/blob/main/CMakeLists.txt Enables NUMA affinity for the static_thread_pool. Requires the 'numa' package to be installed and linked. ```cmake option (STDEXEC_ENABLE_NUMA "Enable NUMA affinity for static_thread_pool" OFF) if (STDEXEC_ENABLE_NUMA) find_package(numa REQUIRED) target_link_libraries(stdexec INTERFACE numa::numa) target_compile_definitions(stdexec INTERFACE STDEXEC_ENABLE_NUMA) endif() ``` -------------------------------- ### Minimal CMakeLists.txt with CPM for stdexec Source: https://github.com/nvidia/stdexec/blob/main/README.md A basic CMakeLists.txt file demonstrating how to use CPM to include stdexec and link it to an executable. Requires CMake version 3.25.0 or higher. ```cmake cmake_minimum_required(VERSION 3.25.0) project(stdexec_example LANGUAGES CXX) include(CPM.cmake) # see https://github.com/cpm-cmake/CPM.cmake#adding-cpm CPMAddPackage( NAME stdexec GITHUB_REPOSITORY NVIDIA/stdexec GIT_TAG main ) add_executable(example example.cpp) target_link_libraries(example PRIVATE STDEXEC::stdexec) ``` -------------------------------- ### Framework's Domain Consultation at Connect Source: https://github.com/nvidia/stdexec/blob/main/docs/source/developer/index.md This pseudo-code illustrates how `stdexec::connect` consults domains to transform sender expressions. It shows the two-phase transformation process involving completing-domain and starting-domain. ```cpp // pseudo-code auto env = stdexec::get_env(rcvr); auto completing_domain = /* read get_completion_domain from sndr's attributes */; auto starting_domain = /* read get_domain from env (or default) */; auto sndr1 = completing_domain.transform_sender(set_value_t{}, sndr, env); auto sndr2 = starting_domain .transform_sender(start_t{}, sndr1, env); auto op = sndr2.connect(rcvr); // (or static_connect, awaitable, ...) ``` -------------------------------- ### CPO Detailed Description and Call Syntaxes Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Provide 2-4 paragraphs of prose covering what the CPO does and why it's useful. Include both direct and pipe call syntaxes in a code block. End with a pointer to the normative specification using the `[exec.*]` format. ```cpp // Direct call syntax: foo(sender, callable); // Pipe syntax: sender | foo(callable); ``` -------------------------------- ### Chain sender operations Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Demonstrates chaining multiple sender adaptors, `then` and `let_value`, to construct a complex asynchronous pipeline. `let_value` is used when the operation itself returns a sender. ```cpp // Wrong: the resulting value type is a sender, not the eventual int. auto bad = stdexec::just(7) | stdexec::then(fetch_async); // Right: let_value chains the returned sender into the pipeline. auto good = stdexec::just(7) | stdexec::let_value(fetch_async); ``` -------------------------------- ### Find and Link Threading Library Source: https://github.com/nvidia/stdexec/blob/main/CMakeLists.txt This snippet finds the Threads library and configures it for preferred PTHREAD usage. It also sets up export sets for build and installation. ```cmake set(CMAKE_THREAD_PREFER_PTHREAD TRUE) rapids_find_package(Threads REQUIRED BUILD_EXPORT_SET stdexec-exports INSTALL_EXPORT_SET stdexec-exports ) ``` -------------------------------- ### Build stdexec with Clang and libc++ Source: https://github.com/nvidia/stdexec/blob/main/README.md Configure the build to use Clang with the libc++ standard library. This is necessary for projects requiring libc++. ```bash cmake -S . -B build/libcxx \ -DCMAKE_CXX_COMPILER=$(which clang++) \ -DCMAKE_CXX_FLAGS=-stdlib=libc++ cmake --build build/libcxx ``` -------------------------------- ### Configure Doxyfile for Pragma Macros Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Predefine pragma macros to empty token sequences in the Doxyfile to prevent Doxygen parsing issues with GCC/Clang pragmas. ```text "STDEXEC_PRAGMA_IGNORE_GNU(X)= " "STDEXEC_PRAGMA_POP()= " "STDEXEC_PRAGMA_PUSH()= " "STDEXEC_PRAGMA_IGNORE_EDG(X)= " "STDEXEC_PRAGMA_IGNORE_MSVC(X)= " ``` -------------------------------- ### stdexec::on Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md The `on` adaptor can be used to perform a side trip to another scheduler and then return to the original scheduler. ```APIDOC ## `on` — side trip to another scheduler ### Description Use `stdexec::on` when you want a *side trip* to another scheduler — do some work there, then come back to where you started. This adaptor facilitates temporary execution on a different scheduler with a return to the original context. ### Method Signature `stdexec::on(scheduler, sender)` `sender | stdexec::on(scheduler, completion_scheduler)` ### Notes This adaptor is useful for performing a specific task on an alternate scheduler and ensuring that subsequent operations, or the final completion, are handled on the original or a specified completion scheduler, effectively creating a round-trip. ``` -------------------------------- ### CPO Cross-References with @see Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Use `@see` lines to point to closely related CPOs, with a single em-dash introducing each one-line description. This helps users navigate related functionality. ```cpp //! @see stdexec::upon_error — adapt the error channel //! @see stdexec::upon_stopped — adapt the stopped channel //! @see stdexec::let_value — adapt the value channel with a sender-returning function ``` -------------------------------- ### Inject Literal Values with stdexec::just Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Use `stdexec::just` to create a sender that immediately delivers specified values to a receiver. It's useful for starting pipelines with fixed values or for feeding test data. ```cpp auto sndr = stdexec::just(21) | stdexec::then([](int x) { return x * 2; }); auto [v] = stdexec::sync_wait(std::move(sndr)).value(); // v == 42 ``` ```cpp auto s0 = stdexec::just(); // value-completes with no datums auto s2 = stdexec::just(1, "hello"); // value-completes with int, string ``` -------------------------------- ### Launching Fire-and-Forget Work with spawn Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Use stdexec::spawn to launch fire-and-forget work owned by an async scope. The scope tracks the operation for later joining. The sender must not complete with set_error. ```cpp exec::async_scope scope; stdexec::spawn( stdexec::just(42) | stdexec::then([](int x) { std::println("background: {}", x); }), scope.get_token()); // ... later, before scope is destroyed ... stdexec::sync_wait(scope.join()); ``` -------------------------------- ### Turn Cancellation into std::nullopt with stdexec::stopped_as_optional Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md Converts a set_stopped completion into a value completion of std::optional{std::nullopt}. Use when the optional shape is desired within the pipeline, for example, to feed into a std::then. ```cpp auto sndr = stdexec::just(42) | stdexec::stopped_as_optional(); auto [opt] = stdexec::sync_wait(std::move(sndr)).value(); // opt == std::optional{42} ``` -------------------------------- ### stdexec::continues_on Source: https://github.com/nvidia/stdexec/blob/main/docs/source/user/index.md `continues_on` takes a sender and a scheduler, runs the sender to completion, then transfers execution to the scheduler before forwarding the completion downstream. It’s the canonical way to *hand off* between execution contexts. ```APIDOC ## `continues_on` — transfer contexts mid-pipeline ### Description `stdexec::continues_on` takes a sender and a scheduler, runs the sender to completion, then transfers execution to the scheduler before forwarding the completion downstream. It’s the canonical way to *hand off* between execution contexts. ### Method Signature `sender | stdexec::continues_on(scheduler) ` ### Example ```cpp auto io_sched = stdexec::get_parallel_scheduler(); // pretend: I/O auto cpu_sched = stdexec::get_parallel_scheduler(); // pretend: compute auto sndr = stdexec::starts_on(io_sched, stdexec::just(42)) // produce on io_sched | stdexec::continues_on(cpu_sched) // hop to cpu_sched | stdexec::then([](int x) { return x * 2; }); // then() runs on cpu_sched auto [v] = stdexec::sync_wait(std::move(sndr)).value(); // v == 84 ``` ### Notes `continues_on` does *not* alter the values, errors, or stopped status of its predecessor — it only changes the execution context they’re delivered on. If you want to also transform the value, chain a `stdexec::then` after. **When *not* to use** `continues_on` **:** If you only want to *temporarily* run on a different scheduler and then come back, use `stdexec::on` (Form 2) — it round-trips, `continues_on` doesn’t. ``` -------------------------------- ### Configure Doxyfile for Variadic Macros Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Predefine variadic macros using '(...)' in the Doxyfile to ensure correct argument absorption and prevent parsing errors in Sphinx. ```text "STDEXEC_ATTRIBUTE(...)= " ``` -------------------------------- ### Reference Section Structure for CPOs Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Structure the reference section for each CPO using specific RST directives. Include a summary, a brief orientation paragraph with a link to the User's Guide, and Doxygen directives to render the CPO's details. ```rst .. _ref-foo: ``foo`` — one-line summary of what foo does ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ One short paragraph orienting the reader, with a forward-link to the User's Guide: see :ref:`UserGuide_foo` for an approachable introduction with worked examples; the full reference follows. .. doxygenstruct:: stdexec::foo_t :members: .. doxygenvariable:: stdexec::foo ``` -------------------------------- ### `when_all` — run senders concurrently and concatenate values Source: https://github.com/nvidia/stdexec/blob/main/docs/source/reference/index.md Takes one or more senders and produces a sender that runs all of them concurrently and completes when every input has completed, concatenating their values. ```APIDOC ## `when_all` — run senders concurrently and concatenate values ### Description 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](#ref-when-all-with-variant). ### Method APPLIES_TO_SENDER ### Endpoint N/A (SDK Method) ### Parameters N/A ### Request Example N/A ### Response N/A ``` -------------------------------- ### when_all_with_variant Source: https://github.com/nvidia/stdexec/blob/main/docs/source/reference/index.md Similar to `when_all`, but handles multi-completion senders by wrapping each input in `stdexec::into_variant`. Each output completion is a `std::variant` of the input's possible shapes. ```APIDOC ## `when_all_with_variant` ### Description 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, ...>` of that input’s possible shapes. ### `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](#ref-when-all-with-variant) and [sync_wait_with_variant](#ref-sync-wait-with-variant), and is occasionally useful on its own when a downstream algorithm requires the single-value-completion form. ``` -------------------------------- ### Documenting Function Templates with doxygenfile Source: https://github.com/nvidia/stdexec/blob/main/docs/CONTRIBUTING-docs.md Use 'doxygenfile' to render the documentation block from a header file for function templates. This method pulls comments from the file and renders them inline, losing per-overload separation but retaining prose. It's often paired with explicit prose in surrounding RST to name different flavors of the template. ```rst .. doxygenfile:: __get_completion_signatures.hpp :sections: briefdescription detaileddescription ```