Skip to content

Commit ca43b8f

Browse files
committed
CRTP for projection_base (#212)
1 parent 4166164 commit ca43b8f

File tree

5 files changed

+44
-22
lines changed

5 files changed

+44
-22
lines changed

benchmarks/benchmarking-tools/distributions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015-2024 Morwenn
2+
* Copyright (c) 2015-2025 Morwenn
33
* SPDX-License-Identifier: MIT
44
*/
55
#include <algorithm>
@@ -472,7 +472,7 @@ namespace dist
472472
// Miscellaneous related tools
473473

474474
struct as_long_string:
475-
cppsort::utility::projection_base
475+
cppsort::utility::projection_base<as_long_string>
476476
{
477477
auto operator()(long long int value) const
478478
-> std::string

docs/Chainable-projections.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Sometimes one needs to apply several transformations to the elements of a collec
44

55
```cpp
66
struct my_negate:
7-
cppsort::utility::projection_base
7+
cppsort::utility::projection_base<my_negate>
88
{
99
int operator()(int value) const
1010
{
@@ -13,7 +13,7 @@ struct my_negate:
1313
};
1414
```
1515

16-
Making a function object inherit from `utility::projection_base` allows it to benefit from the `operator|` overload used to compose projections; the projection inheriting from that class can appear on any side of the operator, and the other argument can be any suitable [*Callable*][callable]. Here is an example of what is possible with the custom projection defined above:
16+
Making a function object `F` inherit from `utility::projection_base<F>` allows it to benefit from the `operator|` overload used to compose projections; the projection inheriting from that class can appear on any side of the operator, and the other argument can be any suitable [*Callable*][callable]. Here is an example of what is possible with the custom projection defined above:
1717

1818
```cpp
1919
// Create a vector of wrapper

docs/Miscellaneous-utilities.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ This buffer provider allocates on the heap a number of elements depending on a g
159159

160160
***WARNING:** `utility::identity` is removed in version 2.0.0, use `std::identity` instead.*
161161

162-
This header provides the class `projection_base` and the mechanism used to compose projections with `operator|`. See [Chainable projections][chainable-projections] for more information.
162+
This header provides the class template `projection_base` and the mechanism used to compose projections with `operator|`. See [Chainable projections][chainable-projections] for more information.
163163

164164
Also available in this header, the struct `identity` is a function object that can type any value of any movable type and return it as is. It is used as a default for every projection parameter in the library so that sorters view the values as they are by default, without a modification.
165165

@@ -181,7 +181,7 @@ Another simple yet very handy projection available in the header is `indirect`:
181181

182182
```cpp
183183
struct indirect:
184-
projection_base
184+
projection_base<indirect>
185185
{
186186
template<typename T>
187187
constexpr auto operator()(T&& indirect_value)

include/cpp-sort/utility/functional.h

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,26 @@ namespace cppsort::utility
2626
////////////////////////////////////////////////////////////
2727
// Base type to allow piping projections
2828

29-
// Literally just to check that other classes
30-
// actually inherit from it
31-
struct projection_base {};
29+
// CRTP base class for all projection objects: it is modelled after
30+
// std::ranges::range_adaptor_closure and thus uses CRTP for the same
31+
// reason, namely to avoid having function objects bigger than they
32+
// ought to (which happens when the same base class is inherited
33+
// from several times by the same class)
34+
35+
template<typename Derived>
36+
struct projection_base
37+
{
38+
static_assert(
39+
std::is_object_v<Derived> &&
40+
std::is_same_v<Derived, std::remove_cv_t<Derived>>
41+
);
42+
};
3243

3344
namespace detail
3445
{
3546
template<typename T, typename U>
3647
struct projection_base_pipe_result:
37-
projection_base,
48+
projection_base<projection_base_pipe_result<T, U>>,
3849
cppsort::detail::raw_check_is_transparent<T, U>
3950
{
4051
T lhs;
@@ -67,8 +78,13 @@ namespace cppsort::utility
6778
typename T,
6879
typename U,
6980
typename = cppsort::detail::enable_if_t<
70-
std::is_base_of_v<projection_base, cppsort::detail::remove_cvref_t<T>> ||
71-
std::is_base_of_v<projection_base, cppsort::detail::remove_cvref_t<U>>
81+
std::is_base_of_v<
82+
projection_base<std::remove_reference_t<T>>,
83+
cppsort::detail::remove_cvref_t<T>
84+
> || std::is_base_of_v<
85+
projection_base<std::remove_reference_t<U>>,
86+
cppsort::detail::remove_cvref_t<U>
87+
>
7288
>
7389
>
7490
constexpr auto operator|(T&& lhs, U&& rhs)
@@ -86,7 +102,10 @@ namespace cppsort::utility
86102
template<
87103
typename T,
88104
typename = cppsort::detail::enable_if_t<
89-
std::is_base_of_v<projection_base, cppsort::detail::remove_cvref_t<T>>
105+
std::is_base_of_v<
106+
projection_base<std::remove_reference_t<T>>,
107+
cppsort::detail::remove_cvref_t<T>
108+
>
90109
>
91110
>
92111
constexpr auto operator|(T&& lhs, std::identity)
@@ -98,7 +117,10 @@ namespace cppsort::utility
98117
template<
99118
typename T,
100119
typename = cppsort::detail::enable_if_t<
101-
std::is_base_of_v<projection_base, cppsort::detail::remove_cvref_t<T>>
120+
std::is_base_of_v<
121+
projection_base<std::remove_reference_t<T>>,
122+
cppsort::detail::remove_cvref_t<T>
123+
>
102124
>
103125
>
104126
constexpr auto operator|(std::identity, T&& rhs)
@@ -112,7 +134,7 @@ namespace cppsort::utility
112134
// Identity (mostly useful for projections)
113135

114136
struct identity:
115-
projection_base
137+
projection_base<identity>
116138
{
117139
template<typename T>
118140
constexpr auto operator()(T&& value) const noexcept
@@ -153,7 +175,7 @@ namespace cppsort::utility
153175
// indirect
154176

155177
struct indirect:
156-
projection_base
178+
projection_base<indirect>
157179
{
158180
template<typename T>
159181
constexpr auto operator()(T&& indirect_value)
@@ -177,7 +199,7 @@ namespace cppsort::utility
177199
{
178200
template<typename Function>
179201
struct as_projection_fn:
180-
projection_base,
202+
projection_base<as_projection_fn<Function>>,
181203
cppsort::detail::raw_check_is_transparent<Function>
182204
{
183205
private:
@@ -335,7 +357,7 @@ namespace cppsort::utility
335357
// Math functions (mostly useful for buffer providers)
336358

337359
struct half:
338-
projection_base
360+
projection_base<half>
339361
{
340362
template<typename T>
341363
constexpr auto operator()(T&& value) const
@@ -348,7 +370,7 @@ namespace cppsort::utility
348370
};
349371

350372
struct log:
351-
projection_base
373+
projection_base<log>
352374
{
353375
template<typename T>
354376
constexpr auto operator()(T&& value) const
@@ -362,7 +384,7 @@ namespace cppsort::utility
362384
};
363385

364386
struct sqrt:
365-
projection_base
387+
projection_base<sqrt>
366388
{
367389
template<typename T>
368390
constexpr auto operator()(T&& value) const

tests/utility/chainable_projections.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ using wrapper = generic_wrapper<int>;
1818
namespace
1919
{
2020
struct proj1:
21-
cppsort::utility::projection_base
21+
cppsort::utility::projection_base<proj1>
2222
{
2323
auto operator()(int value) const
2424
-> int
@@ -28,7 +28,7 @@ namespace
2828
};
2929

3030
struct proj2:
31-
cppsort::utility::projection_base
31+
cppsort::utility::projection_base<proj2>
3232
{
3333
auto operator()(int value)
3434
-> int

0 commit comments

Comments
 (0)