Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CppNorth23 Example: Piping with variadic range adaptors #8

Open
lukasfro opened this issue Oct 18, 2023 · 0 comments
Open

CppNorth23 Example: Piping with variadic range adaptors #8

lukasfro opened this issue Oct 18, 2023 · 0 comments

Comments

@lukasfro
Copy link

Hi Conor,

I was watching your latest talk about the algorithms/range adaptors that C++23 introduces. I'm excited to see you advertising this "sequential programming" style! I'm already a big fan of ranges, but mostly use the range-v3 library at the moment.

In the talk, you mention at around 1:13h that you could not come up with a more elegant way of simplifying the Sushi-for-Two problem using range-v3. You mostly criticize the issue that one cannot pipe into variadiac templates (like zip_with and concat). I fully agree that the Circle compiler has a neat solution for the general pipeline operator. However, I still believe that you can make the example a bit more elegant with "plain" range-v3 (see a full example in compiler explorer). I'm eager to hear what you think about my approach 🙂

  1. Make a custom pairwise_transform adaptor

You have to introduce the temporary variables indices and deltas because you need to pipe them twice into the zip_with range adaptor. However, you could simply make a custom range adaptor, i.e., the pairwise_transform from C++23. Importantly, I make use of the make_pipeable helper that not too many people utilize. While this does not solve the underlying problem that one cannot pipe into variadic templates, in your example you could get rid of the temporary variables.

template <typename Rng, typename Fun>
auto pairwise_transform(Rng&& rng, Fun fun) {
    return rv::zip_with(fun, rng, rng | rv::drop(1));
}
template <typename Fun>
auto pairwise_transform(Fun fun) {
    return ranges::make_pipeable(
        [fun](auto&& rng) { return pairwise_transform(rng, fun); });
}
  1. Make custom prepend/append adaptors

These adaptors solve the "nonlinearity" issue with concat. I, personally, hated that concat could not be piped into and it really breaks the flow of reading the code. Hence, I made myself the following two neat helpers that solved most of my problems. Of course these can be extended to appending/prepending ranges instead of single elements.

// Adds an element to the beginning of a range.
template <typename Rng, typename T>
auto prepend(Rng&& rng, T t) {
    return rv::concat(rv::single(t), rng);
}
template <typename T>
auto prepend(T t) {
    return ranges::make_pipeable([t](auto&& rng) { return prepend(rng, t); });
}

// Adds an element to the end of a range.
template <typename Rng, typename T>
auto append(Rng&& rng, T t) {
    return rv::concat(rng, rv::single(t));
}
template <typename T>
auto append(T t) {
    return ranges::make_pipeable([t](auto&& rng) { return append(rng, t); });

Cheers and kind regards,
Lukas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant