Algorithms¶
Lifting functions¶
-
constexpr auto lf::lift =
[]<class F, class... Args>(auto, F &&func, Args &&...args)static ->task<std::invoke_result_t<F, Args...>>requires std::invocable<F, Args...>{co_return std::invoke(std::forward<F>(func), std::forward<Args>(args)...);}¶ A higher-order function that lifts a function into an asynchronous function.
This is useful for when you want to fork a regular function:
auto work(int x) -> int;
Then later in some async context you can do:
{ int a, b; co_await fork[a, lift](work, 42); co_await fork[b, lift](work, 007); co_await join; }
Note
The lifted function will accept arguments by forwarding reference.
Iteration with for_each¶
-
constexpr impl::for_each_overload lf::for_each = {}¶
A parallel implementation of
std::ranges::for_each.Effective call signature:
template <std::random_access_iterator I, std::sized_sentinel_for<I> S, typename Proj = std::identity, indirectly_unary_invocable<projected<I, Proj>> Fun > void for_each(I head, S tail, std::iter_difference_t<I> n, Fun fun, Proj proj = {});
Overloads exist for a random-access range (instead of
headandtail) andncan be omitted (which will setn = 1).Exemplary usage:
co_await just[for_each](v, 10, [](auto &elem) { elem = 0; });
This will set each element of
vto0in parallel using a chunk size of10.If the function or projection handed to
for_eachare async functions, then they will be invoked asynchronously, this allows you to launch further tasks recursively.Unlike
std::ranges::for_each, this function will make an implementation defined number of copies of the function objects and may invoke these copies concurrently.
Transformations with map¶
-
constexpr impl::map_overload lf::map = {}¶
A parallel variation of
std::transform.Effective call signature:
template <std::random_access_iterator I, std::sized_sentinel_for<I> S, std::random_access_iterator O typename Proj = std::identity, indirectly_unary_invocable<projected<I, Proj>> Fun > requires std::indirectly_copyable<projected<I, Proj, Fun>, O> void map(I head, S tail, O out, std::iter_difference_t<I> n, Fun fun, Proj proj = {});
Overloads exist for a random-access range (instead of
headandtail) andncan be omitted (which will setn = 1).Exemplary usage:
std::vector<int> out(v.size()); co_await just[map](v, out.begin(), 10, [](int const& elem) { return elem + 1; });
This will set each element of
outto one more than corresponding element invusing a chunk size of10.The input and output ranges must either be distinct (i.e. non-overlapping) or the same range (hence the transformation may be performed in-place).
If the function or projection handed to
mapare async functions, then they will be invoked asynchronously, this allows you to launch further tasks recursively.Unlike
std::transform, this function will make an implementation defined number of copies of the function objects and may invoke these copies concurrently.
Reductions with fold¶
-
constexpr impl::fold_overload lf::fold = {}¶
A parallel implementation of
std::ranges::fold_left_first.Effective call signature:
template <std::random_access_iterator I, std::sized_sentinel_for<I> S, typename Proj = std::identity, indirectly_foldable<projected<I, Proj>> Bop > auto fold(I head, S tail, std::iter_difference_t<I> n, Bop bop, Proj proj = {}) -> indirect_fold_acc_t<Bop, I, Proj>;
Overloads exist for a random-access range (instead of
headandtail) andncan be omitted (which will setn = 1).Exemplary usage:
co_await just[fold](v, 10, std::plus<>{}, [](auto &elem) -> std::size_t { return elem % 2 == 0; });
This counts the number of even elements in
vin parallel, using a chunk size of10.If the binary operator or projection handed to
foldare async functions, then they will be invoked asynchronously, this allows you to launch further tasks recursively.Unlike the
std::ranges::foldvariations, this function will make an implementation defined number of copies of the function objects and may invoke these copies concurrently.
Generalized prefix sums with scan¶
-
constexpr impl::scan_overload lf::scan = {}¶
A parallel implementation of
std::inclusive_scanthat accepts generalized ranges and projections.Effective call signature:
template <std::random_access_iterator I, std::sized_sentinel_for<I> S, std::random_access_iterator O, class Proj = std::identity, indirectly_scannable<O, projected<I, Proj>> Bop > void scan(I beg, S end, O out, std::iter_difference_t<I> n, Bop bop, Proj proj = {});
Overloads exist for a random-access range (instead of
headandtail), in place scans (omit the out iterator) and, the chunk size,n, can be omitted (which will setn = 1).Exemplary usage:
co_await just[scan](in, out.begin(), std::plus<>{});
This computes the cumulative sum of the input and stores it in the output-range e.g.
[1, 2, 2, 1] -> [1, 3, 5, 6].The input and output ranges must either be distinct (i.e. non-overlapping) or the same range.
If the binary operator or projection handed to
scanare async functions, then they will be invoked asynchronously, this allows you to launch further tasks recursively.Unlike the
std::variations, this function will make an implementation defined number of copies of the function objects and may invoke these copies concurrently.