Skip to content

Commit

Permalink
std::identity support when available (#130)
Browse files Browse the repository at this point in the history
Wherever the library implements special handling of utility::identity,
provide the same level of support for std::identity when it is
available.
  • Loading branch information
Morwenn committed Nov 7, 2020
1 parent 65d43bc commit 6b00048
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 2 deletions.
6 changes: 5 additions & 1 deletion docs/Miscellaneous-utilities.md
Expand Up @@ -125,6 +125,8 @@ This buffer provider allocates on the heap a number of elements depending on a g
#include <cpp-sort/utility/functional.h>
```

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

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

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.
Expand All @@ -141,7 +143,7 @@ struct identity:
};
```

It is equivalent to the proposed `std::identity` from the [Ranges TS](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4560.pdf) and will probably be replaced by the standard function object the day it makes its way into the standard.
It is equivalent to the C++20 [`std::identity`](https://en.cppreference.com/w/cpp/utility/functional/identity). Wherever the documentation mentions special handling of `utility::identity`, the same support is provided for `std::identity` when it is available.

This header also provides additional function objects implementing basic unary operations. These functions objects are designed to be used as *size policies* with `dynamic_buffer` and similar classes. The following function objects are available:
* `half`: returns the passed value divided by 2.
Expand Down Expand Up @@ -175,6 +177,8 @@ This utility is modeled after [`std::integral_constant`](http://en.cppreference.
*New in version 1.7.0:* `projection_base` and chainable projections.
*Changed in version 1.9.0:* `std::identity` is now also supported wherever the library has special behavior for `utility::identity`.
### `iter_move` and `iter_swap`
```cpp
Expand Down
4 changes: 4 additions & 0 deletions docs/Sorter-facade.md
Expand Up @@ -169,4 +169,8 @@ auto operator()(Iterator first, Iterator last,
-> /* implementation-defined */;
```

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.

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.
23 changes: 23 additions & 0 deletions include/cpp-sort/detail/config.h
Expand Up @@ -29,6 +29,29 @@
# define CPPSORT_CONSTEXPR_AFTER_CXX14
#endif

////////////////////////////////////////////////////////////
// Check for C++20 features

// There is no feature-test macro for std::identity that can
// be used reliably, so we have to fall back to checking
// compiler and standard versions

#if defined(__GNUC__)
# if __GNUC__ > 3 && __cplusplus > 201703L
# define CPPSORT_STD_IDENTITY_AVAILABLE 1
# else
# define CPPSORT_STD_IDENTITY_AVAILABLE 0
# endif
#elif defined(__clang__)
# define CPPSORT_STD_IDENTITY_AVAILABLE 0
#else
# if defined(__cpp_lib_ranges)
# CPPSORT_STD_IDENTITY_AVAILABLE 1
# else
# CPPSORT_STD_IDENTITY_AVAILABLE 0
# endif
#endif

////////////////////////////////////////////////////////////
// CPPSORT_ASSUME

Expand Down
33 changes: 32 additions & 1 deletion include/cpp-sort/detail/swap_if.h
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015-2018 Morwenn
* Copyright (c) 2015-2020 Morwenn
* SPDX-License-Identifier: MIT
*/
#ifndef CPPSORT_DETAIL_SWAP_IF_H_
Expand All @@ -15,6 +15,7 @@
#include <cpp-sort/utility/as_function.h>
#include <cpp-sort/utility/functional.h>
#include <cpp-sort/utility/iter_move.h>
#include "config.h"
#include "type_traits.h"

namespace cppsort
Expand Down Expand Up @@ -81,6 +82,36 @@ namespace detail
y = std::min(dx, y);
}

#if CPPSORT_STD_IDENTITY_AVAILABLE
template<typename Integer>
auto swap_if(Integer& x, Integer& y, std::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::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::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::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
70 changes: 70 additions & 0 deletions include/cpp-sort/sorter_facade.h
Expand Up @@ -499,6 +499,44 @@ namespace cppsort
return operator()(std::forward<Iterable>(iterable));
}

#if CPPSORT_STD_IDENTITY_AVAILABLE
template<typename Iterator>
auto operator()(Iterator first, Iterator last, std::identity) const
-> std::enable_if_t<
not detail::has_projection_sort_iterator<Sorter, Iterator, std::identity>::value &&
not detail::has_comparison_projection_sort_iterator<
Sorter,
Iterator,
std::less<>,
std::identity
>::value,
decltype(Sorter::operator()(std::move(first), std::move(last)))
>
{
return Sorter::operator()(std::move(first), std::move(last));
}

template<typename Iterable>
auto operator()(Iterable&& iterable, std::identity) const
-> std::enable_if_t<
not detail::has_projection_sort_iterator<
Sorter,
decltype(std::begin(iterable)),
std::identity
>::value &&
not detail::has_comparison_projection_sort_iterator<
Sorter,
decltype(std::begin(iterable)),
std::less<>,
std::identity
>::value,
decltype(operator()(std::forward<Iterable>(iterable)))
>
{
return operator()(std::forward<Iterable>(iterable));
}
#endif

////////////////////////////////////////////////////////////
// Fused comparison-projection overloads

Expand Down Expand Up @@ -599,6 +637,38 @@ namespace cppsort
return operator()(std::forward<Iterable>(iterable));
}

#if CPPSORT_STD_IDENTITY_AVAILABLE
template<typename Iterator>
auto operator()(Iterator first, Iterator last, std::less<>, std::identity) const
-> std::enable_if_t<
not detail::has_comparison_projection_sort_iterator<
Sorter,
Iterator,
std::less<>,
std::identity
>::value,
decltype(Sorter::operator()(std::move(first), std::move(last)))
>
{
return Sorter::operator()(std::move(first), std::move(last));
}

template<typename Iterable>
auto operator()(Iterable&& iterable, std::less<>, std::identity) const
-> std::enable_if_t<
not detail::has_comparison_projection_sort_iterator<
Sorter,
decltype(std::begin(iterable)),
std::less<>,
std::identity
>::value,
decltype(operator()(std::forward<Iterable>(iterable)))
>
{
return operator()(std::forward<Iterable>(iterable));
}
#endif

template<typename Iterator, typename Projection>
auto operator()(Iterator first, Iterator last, std::less<>, Projection projection) const
-> std::enable_if_t<
Expand Down

0 comments on commit 6b00048

Please sign in to comment.