Skip to content

Commit

Permalink
Basic constexpr support for user-defined sorters (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
Morwenn committed Mar 24, 2021
1 parent 145d1d2 commit d801e82
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 66 deletions.
9 changes: 9 additions & 0 deletions docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ When compiled with C++17, **cpp-sort** might gain a few additional features depe

This feature is available when the feature-testing macro `__cpp_nontype_template_parameter_auto` is defined.

* [[`sorter_facade`|Sorter facade]] range overloads can now be used in `constexpr` functions.

There is no specific feature macro available to test this, it starts working when `std::begin` and `std::end` are `constexpr`.

**Correctness improvements:**
* Some handy C++17 type traits such as `std::is_invocable` are manually reimplemented in C++14 mode while they are used as is in C++17 mode if available. It's likely that the C++17 implementation covers more corner cases and is thus more often correct than the manual C++14 implementation.

Expand All @@ -64,6 +68,10 @@ When compiled with C++20, **cpp-sort** might gain a few additional features depe

* When available, [`std::ranges::less`][std-ranges-less] and [`std::ranges::greater`][std-ranges-greater] benefit from dedicated support wherever [`std::less<>`][std-less-void] and [`std::greater<>`][std-greater-void] are supported, with equivalent semantics.

* [`utility::iter_swap`][utility-iter-move] can now be used in more `constexpr` functions thanks to [`std::swap`][std-swap] begin `constexpr`.

The feature-test macro `__cpp_lib_constexpr_algorithms` can be used to check whether `std::swap` is `constexpr`.

## Other features

**cpp-sort** tries to take advantage of more than just standard features when possible by using implementation-specific tweaks to improve the user experience. The following improvements might be available depending on the your standard implementation:
Expand Down Expand Up @@ -93,3 +101,4 @@ When compiled with C++20, **cpp-sort** might gain a few additional features depe
[std-ranges-greater]: https://en.cppreference.com/w/cpp/utility/functional/ranges/greater
[std-ranges-less]: https://en.cppreference.com/w/cpp/utility/functional/ranges/less
[std-string-view]: https://en.cppreference.com/w/cpp/string/basic_string_view)
[utility-iter-move]: https://github.com/Morwenn/cpp-sort/wiki/Miscellaneous-utilities#iter_move-and-iter_swap
12 changes: 8 additions & 4 deletions docs/Miscellaneous-utilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ The result of `as_projection` also inherits from `projection_base`, which makes

```cpp
template<typename Function>
auto as_projection(Function&& func)
constexpr auto as_projection(Function&& func)
-> /* implementation-defined */;

template<typename Function>
auto as_comparison(Function&& func)
constexpr auto as_comparison(Function&& func)
-> /* implementation-defined */;
```
Expand Down Expand Up @@ -196,14 +196,18 @@ The default implementation of `iter_move` simply move-returns the dereferenced i

```cpp
template<typename Iterator>
auto iter_move(Iterator it)
constexpr auto iter_move(Iterator it)
-> decltype(std::move(*it));

template<typename Iterator>
auto iter_swap(Iterator lhs, Iterator rhs)
constexpr auto iter_swap(Iterator lhs, Iterator rhs)
-> void;
```
*NOTE:* while both overloads are marked as `constexpr`, the generic version of `iter_swap` might use `std::swap`, which is not `constexpr` before C++20.
*Changed in version 1.10.0:* generic `iter_move` and `iter_swap` overloads are now marked as `constexpr`.
### `make_integer_range`
```cpp
Expand Down
28 changes: 19 additions & 9 deletions docs/Sorter-facade.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,20 @@ Note that the function pointer conversion syntax above is made up, but it allows

```cpp
template<typename Iterator>
auto operator()(Iterator first, Iterator last) const
constexpr auto operator()(Iterator first, Iterator last) const
-> /* implementation-defined */;

template<typename Iterator, typename Compare>
auto operator()(Iterator first, Iterator last, Compare compare) const
constexpr auto operator()(Iterator first, Iterator last, Compare compare) const
-> /* implementation-defined */;

template<typename Iterator, typename Projection>
auto operator()(Iterator first, Iterator last, Projection projection) const
constexpr auto operator()(Iterator first, Iterator last, Projection projection) const
-> /* implementation-defined */;

template<typename Iterator, typename Compare, typename Projection>
auto operator()(Iterator first, Iterator last,
Compare compare, Projection projection) const
constexpr auto operator()(Iterator first, Iterator last,
Compare compare, Projection projection) const
-> /* implementation-defined */;
```
Expand Down Expand Up @@ -99,32 +99,38 @@ struct selection_sorter:
{};
```

*Changed in version 1.10.0:* those overloads are now `constexpr`.

### `operator()` for ranges

`sorter_facade` provides the following overloads of `operator()` to handle ranges:

```cpp
template<typename Iterable>
auto operator()(Iterable&& iterable) const
constexpr auto operator()(Iterable&& iterable) const
-> /* implementation-defined */;

template<typename Iterable, typename Compare>
auto operator()(Iterable&& iterable, Compare compare) const
constexpr auto operator()(Iterable&& iterable, Compare compare) const
-> /* implementation-defined */;

template<typename Iterable, typename Projection>
auto operator()(Iterable&& iterable, Projection projection) const
constexpr auto operator()(Iterable&& iterable, Projection projection) const
-> /* implementation-defined */;

template<typename Iterable, typename Compare, typename Projection>
auto operator()(Iterable&& iterable, Compare compare, Projection projection) const
constexpr auto operator()(Iterable&& iterable, Compare compare, Projection projection) const
-> /* implementation-defined */;
```
These overloads will generally forward the parameters to the corresponding `operator()` overloads in the wrapped *sorter implementation* if they exist, or try to call an equivalent `operator()` taking a pair of iterators in the wrapped sorter by using `utility::begin` and `utility::end` on the iterable to sort. It also does some additional magic to forward `compare` and `projection` to the most suitable `operator()` overload in `sorter` and to complete the call with instances of [`std::less<>`][std-less-void] and/or [`utility::identity`][utility-identity] when additional parameters are needed. Basically, it ensures that everything can be done if `Sorter` has a single `operator()` taking a pair of iterators, a comparison function and a projection function.
It will always call the most suitable iterable `operator()` overload in the wrapped *sorter implementation* if there is one, and dispatch the call to an overload taking a pair of iterators when it cannot do otherwise.
*NOTE:* range overloads are marked as `constexpr` but rely on [`std::begin`][std-begin] and [`std::end`][std-end], which means that they can't actually be used in a `constexpr` context before C++17 (except for arrays).
*Changed in version 1.10.0:* those overloads are now `constexpr`.
### Projection support for comparison-only sorters
Some *sorter implementations* are able to handle custom comparison functions but don't have any dedicated support for projections. If such an implementation is wrapped by `sorter_facade` and is given a projection function, `sorter_facade` will bake the projection into the comparison function and give the result to the *sorter implementation* as a comparison function. Basically it means that a *sorter implementation* with a single `operator()` taking a pair of iterators and a comparison function can take any iterable, pair of iterators, comparison and/or projection function once it wrapped into `sorter_facade`.
Expand Down Expand Up @@ -181,8 +187,12 @@ While it does not appear in this documentation, `sorter_facade` actually relies

*Changed in version 1.9.0:* when `std::ranges::less` is available, special overloads are provided.

*Changed in version 1.10.0:* those overloads are now `constexpr`.


[selection-sort]: https://en.wikipedia.org/wiki/Selection_sort
[std-begin]: https://en.cppreference.com/w/cpp/iterator/begin
[std-end]: https://en.cppreference.com/w/cpp/iterator/end
[std-identity]: https://en.cppreference.com/w/cpp/utility/functional/identity
[std-less-void]: https://en.cppreference.com/w/cpp/utility/functional/less_void
[std-ranges-less]: https://en.cppreference.com/w/cpp/utility/functional/ranges/less
Expand Down

0 comments on commit d801e82

Please sign in to comment.