0%

Projection in C++

雖然之前有看過,不過看過即忘,還是得寫下來

What’s projection

從一個寫到爛的範例開始

1
2
3
4
5
6
7
8
struct Person {
std::string name;
int age;
};
std::vector<Person> persons;
std::sort(begin(persons), end(persons), [](const auto& p1, const auto& p2) {
return p1.name < p2.name;
});

相信這樣的程式碼已經寫到吐了
如果用C++20 Ranges寫的話可以這樣寫

1
std::ranges::sort(persons, std::ranges::less{}, &Person::name);

可以知道我們要比的就是name,而這樣的寫法就叫做Projection

Backport to C++17

其實要backport到更之前的版本也行, 只要有第三方或是自己寫的invoke
然後寫一個projecting_fn functor,compose以下的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template <typename Function, typename Projection>
class projecting_fn {
public:
projecting_fn(Function function, Projection projection)
: m_function{ std::move(function) }
, m_projection{ std::move(projection) }
{
}

template <typename... Args>
decltype(auto) operator() (Args&&... args) const
{
return std::invoke(
m_function,
std::invoke(m_projection, std::forward<decltype(args)>(args))...);
}

private:
Function m_function;
Projection m_projection;
};
std::sort(begin(persons), end(persons),
projecting_fn{ std::less{}, &Person::name });

Projection and view filter

像這樣的Source Code是無法通過編譯的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template<typename T>
struct LessThan
{
bool operator()(const T& x){
return x < value;
}
T value;
};

struct Apple
{
int weight;
int size;
};

int main()
{
auto apples = std::vector<Apple>{{1,2}, {2,3}, {3,4}};

auto smallApples = apples | views::filter(LessThan{3}, &Apple::size);
}

解決方式有兩種

不用Projection也是一種解決方法

1
apples | views::filter([] (Apple& a) {return a.size < 3;})

不過這方式就與本文無關了

Boost HOF

1
apples | views::filter(hof::proj(&Apple::size, LessThan{3}));

這方法就類似上面C++17的projecting_fn

Reference