C++26stlexpert
std::execution::bulk, split, ensure_started (C++26 Addition)
C++26 · The Next Frontier · Compiler flag: -std=c++26
Explanation
Advanced sender adaptors. `bulk` executes a function over an index range in parallel. `split` allows a sender to be consumed by multiple receivers. `ensure_started` eagerly starts an async operation so it runs in the background.
Real-World Scenario
Process a large array of game entities in parallel using `bulk`, share computation results across multiple consumers with `split`, and pre-warm a cache with `ensure_started`.
Quick Snippet
snippet.cpp
#include <execution>
namespace ex = std::execution;
// Parallel bulk operation over 1000 elements
auto work = ex::just(std::vector<float>(1000))
| ex::bulk(1000, [](int idx, auto& vec) {
vec[idx] = compute(idx);
});
// Split: share a sender across multiple consumers
auto shared = ex::schedule(sched) | ex::then([] { return load_config(); }) | ex::split();
auto consumer1 = shared | ex::then([](auto cfg) { return init_graphics(cfg); });
auto consumer2 = shared | ex::then([](auto cfg) { return init_audio(cfg); });
// Ensure started: begin work eagerly
auto background = ex::schedule(sched)
| ex::then([] { return precompute_data(); })
| ex::ensure_started();Full Example
execution_advanced.cpp
// CodeNebula - C++26 Example: bulk, split, ensure_started
// Compile: g++ -std=c++26 -Wall -Wextra -o example execution_advanced.cpp
// Note: Requires C++26 compiler support
#include <iostream>
#include <vector>
// Advanced std::execution adaptors in C++26:
// bulk(sender, count, f) - parallel for-loop: f(i, value) for i in [0, count)
// split(sender) - allows a sender to be consumed multiple times
// ensure_started(sender) - eagerly starts work, returns sender with result
namespace exec_sim {
template <typename T>
struct just_sender { T value; };
template <typename T>
just_sender<T> just(T v) { return {std::move(v)}; }
template <typename T, typename F>
auto bulk(just_sender<T> s, std::size_t count, F f) {
for (std::size_t i = 0; i < count; ++i) f(i, s.value);
return s;
}
template <typename T>
struct split_sender {
T value;
just_sender<T> get() const { return {value}; }
};
template <typename T>
split_sender<T> split(just_sender<T> s) { return {s.value}; }
template <typename T>
just_sender<T> ensure_started(just_sender<T> s) {
std::cout << " [ensure_started] Work started eagerly\n";
return s;
}
template <typename T>
T sync_wait(just_sender<T> s) { return s.value; }
}
int main() {
std::cout << "--- bulk adaptor ---\n";
exec_sim::bulk(exec_sim::just(10), 5, [](std::size_t i, int val) {
std::cout << " bulk[" << i << "]: " << val << " * " << (i+1)
<< " = " << val * static_cast<int>(i+1) << "\n";
});
std::cout << "\n--- split adaptor ---\n";
auto shared = exec_sim::split(exec_sim::just(42));
std::cout << " Consumer 1: " << exec_sim::sync_wait(shared.get()) << "\n";
std::cout << " Consumer 2: " << exec_sim::sync_wait(shared.get()) << "\n";
std::cout << "\n--- ensure_started adaptor ---\n";
auto eager = exec_sim::ensure_started(exec_sim::just(99));
std::cout << " Result: " << exec_sim::sync_wait(eager) << "\n";
return 0;
}
// Expected output:
// --- bulk adaptor ---
// bulk[0]: 10 * 1 = 10
// bulk[1]: 10 * 2 = 20
// bulk[2]: 10 * 3 = 30
// bulk[3]: 10 * 4 = 40
// bulk[4]: 10 * 5 = 50
//
// --- split adaptor ---
// Consumer 1: 42
// Consumer 2: 42
//
// --- ensure_started adaptor ---
// [ensure_started] Work started eagerly
// Result: 99
Run command
g++ -std=c++26 -Wall -Wextra -o example execution_advanced.cpp