Skip to content

Commit

Permalink
Conditional support for std::ranges::less and std::ranges::greater (i…
Browse files Browse the repository at this point in the history
…ssue #130)
  • Loading branch information
Morwenn committed Nov 9, 2020
1 parent 3428fb4 commit 25c361a
Show file tree
Hide file tree
Showing 9 changed files with 431 additions and 17 deletions.
9 changes: 7 additions & 2 deletions docs/Miscellaneous-utilities.md
Expand Up @@ -79,8 +79,8 @@ constexpr bool is_probably_branchless_comparison_v
```

This trait tells whether the comparison function `Compare` is likely to generate branchless code when comparing two instances of `T`. By default it considers that the following comparison functions are likely to be branchless:
* `std::less<>` and `std::less<T>` for any `T` which satisfies [`std::is_arithmetic`](http://en.cppreference.com/w/cpp/types/is_arithmetic)
* `std::greater<>` and `std::greater<T>` for any `T` which satisfies [`std::is_arithmetic`](http://en.cppreference.com/w/cpp/types/is_arithmetic)
* `std::less<>`, `std::ranges::less` and `std::less<T>` for any `T` which satisfies [`std::is_arithmetic`](http://en.cppreference.com/w/cpp/types/is_arithmetic)
* `std::greater<>`, `std::ranges::greater` and `std::greater<T>` for any `T` which satisfies [`std::is_arithmetic`](http://en.cppreference.com/w/cpp/types/is_arithmetic)

```cpp
template<typename Projection, typename T>
Expand All @@ -93,10 +93,15 @@ constexpr bool is_probably_branchless_projection_v

This trait tells whether the projection function `Projection` is likely to generate branchless code when called with an instance of `T`. By default it considers that the following projection functions are likely to be branchless:
* `cppsort::utility::identity` for any type
* `std::identity` for any type (when available)
* Any type that satisfies [`std::is_member_function_pointer`](http://en.cppreference.com/w/cpp/types/is_member_function_pointer) provided it is called with an instance of the appropriate class

These traits can be specialized for user-defined types. If one of the traits is specialized to consider that a user-defined type is likely to be branchless with a comparison/projection function, cv-qualified and reference-qualified versions of the same user-defined type will also be considered to produce branchless code when compared/projected with the same function.

*Changed in version 1.9.0:* conditional support for [`std::ranges::less`](https://en.cppreference.com/w/cpp/utility/functional/ranges/less) and [`std::ranges::greater`](https://en.cppreference.com/w/cpp/utility/functional/ranges/greater).

*Changed in version 1.9.0:* conditional support for [`std::identity`](https://en.cppreference.com/w/cpp/utility/functional/identity).

### Buffer providers

```cpp
Expand Down
4 changes: 4 additions & 0 deletions docs/Sorter-facade.md
Expand Up @@ -171,6 +171,10 @@ auto operator()(Iterator first, Iterator last,

When [`std::identity`](https://en.cppreference.com/w/cpp/utility/functional/identity) is available, special overloads are provided with the same behaviour as the `utility::identity` ones.

When [`std::ranges::less`](https://en.cppreference.com/w/cpp/utility/functional/ranges/less) is available, special overloads are provided with a behaviour similar to that of the `std::less<>` ones.

While it does not appear in this documentation, `sorter_facade` actually relies on an extensive amount of SFINAE tricks to ensure that only the `operator()` overloads that are needed and viable are generated. For example, the magic `std::less<>` overloads won't be generated if the wrapped *sorter implementation* already accepts a comparison function.

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

*Changed in version 1.9.0:* when `std::ranges::less` is available, special overloads are provided.
8 changes: 6 additions & 2 deletions docs/Sorters.md
Expand Up @@ -400,7 +400,7 @@ The following sorters are available but will only work for some specific types i
#include <cpp-sort/sorters/counting_sorter.h>
```

`counting_sorter` implements a simple [counting sort](https://en.wikipedia.org/wiki/Counting_sort). This sorter also supports reverse sorting with `std::greater<>`.
`counting_sorter` implements a simple [counting sort](https://en.wikipedia.org/wiki/Counting_sort). This sorter also supports reverse sorting with `std::greater<>` or `std::ranges::greater`.

| Best | Average | Worst | Memory | Stable | Iterators |
| ----------- | ----------- | ----------- | ----------- | ----------- | ------------- |
Expand All @@ -412,6 +412,8 @@ This sorter works with any type satisfying the trait `std::is_integral` (as well

*Changed in version 1.6.0:* support for `[un]signed __int128`.

*Changed in version 1.9.0:* conditional support for [`std::ranges::greater`](https://en.cppreference.com/w/cpp/utility/functional/ranges/greater).

### `ska_sorter`

```cpp
Expand Down Expand Up @@ -451,7 +453,7 @@ It comes into three main flavours (available individually if needed):

* `integer_spread_sorter` works with any type satisfying the trait `std::is_integral`.
* `float_spread_sorter` works with any type satisfying the trait `std::numeric_limits::is_iec559` whose size is the same as `std::uint32_t` or `std::uin64_t`.
* `string_spread_sorter` works with `std::string` and `std::wstring` (if `wchar_t` is 2 bytes). This sorter also supports reverse sorting with `std::greater<>`. In C++17 it also works with `std::string_view` and `std::wstring_view` (if `wchar_t` is 2 bytes).
* `string_spread_sorter` works with `std::string` and `std::wstring` (if `wchar_t` is 2 bytes). This sorter also supports reverse sorting with `std::greater<>` and `std::ranges::greater`. In C++17 it also works with `std::string_view` and `std::wstring_view` (if `wchar_t` is 2 bytes).

These sorters accept projections as long as their simplest form can handle the result of the projection. The three of them are aggregated into one main sorter the following way:

Expand All @@ -464,3 +466,5 @@ struct spread_sorter:
>
{};
```

*Changed in version 1.9.0:* conditional support for [`std::ranges::greater`](https://en.cppreference.com/w/cpp/utility/functional/ranges/greater).
58 changes: 58 additions & 0 deletions include/cpp-sort/detail/swap_if.h
Expand Up @@ -112,6 +112,64 @@ namespace detail
}
#endif

#ifdef __cpp_lib_ranges
template<typename Integer>
auto swap_if(Integer& x, Integer& y, std::ranges::less comp, utility::identity) noexcept
-> std::enable_if_t<std::is_integral<Integer>::value>
{
return swap_if(x, y, comp, utility::identity{});
}

template<typename Float>
auto swap_if(Float& x, Float& y, std::ranges::less comp, utility::identity) noexcept
-> std::enable_if_t<std::is_floating_point<Float>::value>
{
return swap_if(x, y, comp, utility::identity{});
}

template<typename Integer>
auto swap_if(Integer& x, Integer& y, std::ranges::greater comp, utility::identity) noexcept
-> std::enable_if_t<std::is_integral<Integer>::value>
{
return swap_if(x, y, comp, utility::identity{});
}

template<typename Float>
auto swap_if(Float& x, Float& y, std::ranges::greater comp, utility::identity) noexcept
-> std::enable_if_t<std::is_floating_point<Float>::value>
{
return swap_if(x, y, comp, utility::identity{});
}

template<typename Integer>
auto swap_if(Integer& x, Integer& y, std::ranges::less comp, std::identity) noexcept
-> std::enable_if_t<std::is_integral<Integer>::value>
{
return swap_if(x, y, comp, utility::identity{});
}

template<typename Float>
auto swap_if(Float& x, Float& y, std::ranges::less comp, std::identity) noexcept
-> std::enable_if_t<std::is_floating_point<Float>::value>
{
return swap_if(x, y, comp, utility::identity{});
}

template<typename Integer>
auto swap_if(Integer& x, Integer& y, std::ranges::greater comp, std::identity) noexcept
-> std::enable_if_t<std::is_integral<Integer>::value>
{
return swap_if(x, y, comp, utility::identity{});
}

template<typename Float>
auto swap_if(Float& x, Float& y, std::ranges::greater comp, std::identity) noexcept
-> std::enable_if_t<std::is_floating_point<Float>::value>
{
return swap_if(x, y, comp, utility::identity{});
}
#endif

////////////////////////////////////////////////////////////
// iter_swap_if

Expand Down
61 changes: 60 additions & 1 deletion include/cpp-sort/detail/three_way_compare.h
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2019 Morwenn
* Copyright (c) 2016-2020 Morwenn
* SPDX-License-Identifier: MIT
*/
#ifndef CPPSORT_DETAIL_THREE_WAY_COMPARE_H_
Expand Down Expand Up @@ -141,6 +141,35 @@ namespace detail
}
};

#ifdef __cpp_lib_ranges
template<>
struct three_way_compare<std::ranges::less, true>:
three_way_compare_base<three_way_compare<std::ranges::less>>
{
constexpr three_way_compare(std::ranges::less) {}

using three_way_compare_base<three_way_compare<std::ranges::less>>::operator();

template<
typename CharT,
typename Traits1, typename Alloc1,
typename Traits2, typename Alloc2
>
auto operator()(const std::basic_string<CharT, Traits1, Alloc1>& lhs,
const std::basic_string<CharT, Traits2, Alloc2>& rhs) const
-> int
{
return lhs.compare(0, lhs.size(), rhs.data(), rhs.size());
}

constexpr auto base() const noexcept
-> std::ranges::less
{
return {};
}
};
#endif

template<>
struct three_way_compare<std::greater<>, true>:
three_way_compare_base<three_way_compare<std::greater<>>>
Expand Down Expand Up @@ -168,6 +197,36 @@ namespace detail
return {};
}
};

#ifdef __cpp_lib_ranges
template<>
struct three_way_compare<std::ranges::greater, true>:
three_way_compare_base<three_way_compare<std::ranges::greater>>
{
constexpr three_way_compare(std::ranges::greater) {}

using three_way_compare_base<three_way_compare<std::ranges::greater>>::operator();

template<
typename CharT,
typename Traits1, typename Alloc1,
typename Traits2, typename Alloc2
>
auto operator()(const std::basic_string<CharT, Traits1, Alloc1>& lhs,
const std::basic_string<CharT, Traits2, Alloc2>& rhs) const
-> int
{
int res = lhs.compare(0, lhs.size(), rhs.data(), rhs.size());
return (res < 0) ? 1 : -res;
}

constexpr auto base() const noexcept
-> std::ranges::greater
{
return {};
}
};
#endif
}}

#endif // CPPSORT_DETAIL_THREE_WAY_COMPARE_H_

0 comments on commit 25c361a

Please sign in to comment.