Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do C++ ranges support projections in views?

I know algorithms (e.g. sort) in ranges support projection, but it seems to me that there is no way to get that functionality for views... Am I right?

As an example consider following working code:

#include <algorithm>
#include <ranges>
#include <vector>
#include <iostream>

enum Color {
   Red,
   Green,
   Blue
}; 
struct Cat {
   int age;
   Color color;
};

int main() {
    std::vector<Cat> cats{{.age = 10,.color=Color::Red}, {.age = 20,.color=Color::Blue}, {.age = 30,.color=Color::Green}};
    auto is_red = [](const auto& cat) {return cat.color == Color::Red;};
    for (const auto& cat: cats | std::views::filter(is_red)) {
        std::cout << cat.age << std::endl; 
    }
}

Is there a way to remove the lambda and do something like:

for (const auto& cat: cats | std::views::filter(&Cat::color, Color::Red) {

note: my question is for member variable projection, but obviously in real code member function calls would be also needed.

like image 738
NoSenseEtAl Avatar asked May 20 '21 09:05

NoSenseEtAl


Video Answer


1 Answers

Do C++ ranges support projections in views?

No (although range-v3 did).

Is there a way to remove the lambda and do something like:

std::views::filter(&Cat::color, Color::Red)

That wouldn't really be how this would work with projections anyway. It would have been:

filter([](Color c){ return c == Color::Red; }, &Cat::color)

Which you can reduce this if you have an equals that returns a predicate:

filter(equals(Color::Red), &Cat::color)

But adding projections to algorithms is kind of unnecessary. You can always provide a projection manually. Using Boost.Hof's appropriately-named proj function adapter, which satisfies proj(p, f)(xs...) == f(p(xs)...) (i.e. we're applying p on each argument before passing them into f):

filter(proj(&Cat::color, [](Color c){ return c == Color::Red; }))

or, shorter:

filter(proj(&Cat::color, _ == Color::Red))

Demo.


Even in the range-v3 implementation, it's not that remove_if_view had explicit support for projections. It's that the overload that took a projection manually composed the predicate for you as compose(pred, proj). In range-v3, compose(f, g)(xs...) can mean either f(g(xs...)) or f(g(xs)...) depending on how g is invocable. So in this case, it's a projection rather than function composition. In Boost.Hof, there is are distinct compose and proj adaptors for the two cases.

like image 174
Barry Avatar answered Sep 30 '22 16:09

Barry