rubenvb
rubenvb

Reputation: 76775

How do you use std::views::zip (and friends) with the pipe syntax?

C++23 gives us the long-missing std::views::zip (and friendly helpers like std::views::zip_transform) which gives a handy way to take values from multiple ranges and do something with those sets of elements.

I'm only recently dabbling in C++20 ranges, so I may be missing something obvious, but how (if at all possible) does one use the pipe syntax with the zip family of range adaptors?

Everything seems to be in place on the ranges side, but it seems operator| being binary, cannot be made to accept the multiple arguments needed in a decent way.

I know

some_range | std::views::transform(someFunc)

really is just syntactic sugar for

std::ranges::transform(someFunc, some_range)

(if I got the order of arguments right). So I'd expectwant the syntax to be some form of

(range1, range2, range3) | std::views::zip_transform(someFuncTakingThreeArgs)

But that obviously doesn't work.

If it can't be made to work, are there any proposals to allow this kind of visual zipping?

Upvotes: 4

Views: 144

Answers (1)

Caleth
Caleth

Reputation: 63142

You don't. std::views::zip et.al. don't have overloads that return a RangeAdaptorClosureObject. It would be pretty ambiguous if they did.

Contrast the "Call signature" sections in cppreference for e.g. std::views::transform:

template< ranges::viewable_range R, class F >
    requires /* see below */
constexpr ranges::view auto transform( R&& r, F&& fun );
template< class F >
constexpr /*range adaptor closure*/ transform( F&& fun );

vs std::views::zip:

template< ranges::viewable_range... Rs >
    requires /* see below */
constexpr ranges::view auto zip( Rs&&... rs );

There aren't any proposals, but you could write your own zip / zip_transform which can be the right hand side of operator | to a tuple-like of ranges. As a sketch

template <typename F>
struct my_zip_transform {
    F f;
    template <typename Self, /* tuple of ranges */ T>
    auto operator()( this Self&& self, T&& r ) {
        return std::apply(std::views::zip_transform, std::tuple_cat(std::forward_as_tuple(std::forward<Self>(self).f), std::forward<T>(t));
    }
    template </* tuple of ranges */ T>
    friend auto operator|( T&& r, my_zip_transform zt ) {
        return std::apply(std::views::zip_transform, std::tuple_cat(std::forward_as_tuple(zt.f), std::forward<T>(t));
    }
    template </* range adaptor */ A>
    friend auto operator|( my_zip_transform zt, A&& a ) {
        /* wrapper that will apply zt then a when applied to a tuple of ranges */
    }
}

That would then be used

std::tie(range1, range2, range3) | my_zip_transform(someFuncTakingThreeArgs) | some_range_adaptor | ...

Upvotes: 7

Related Questions