Skip to content

Comparator adapters

GitHub Action edited this page Apr 12, 2022 · 1 revision

The comparator adapters below are classes mainly designed to wrap binary predicates and should themselves satisfy the BinaryPredicate named requirement.

All adapters below are composed of two elements:

  • A class template that wraps a comparator and is itself a comparator (ex: not_fn_t, flip_t).
  • A function template that simplifies the construction and sometimes implements optimizations (ex: not_fn, flip).

The optimizations performed by the function templates are of the "unwrapping" kind, with a goal to reduce the nesting of templates in the library and to eventually reduce the overall number of instantiated templates.

auto cmp = std::less{};
auto cmp2 = cppsort::flip(cmp); // cppsort::flip_t<std::less<>>
auto cmp3 = cppsort::flip(cmp2); // std::less<>

Those unwrappings are meant to be simple and only intended to work with "well-formed" functions that don't go against simple logic rules. If the passed comparators are too tricky and absolutely need the extra wrapping, then it is advised to use the adapter template classes directly instead of the construction functions.

flip

#include <cpp-sort/comparators/flip.h>

The class template flip_t is a function object which, when called, passes the arguments in reversed order to the Callable it holds with and returns the result. It is named after the flip function from Haskell's Prelude module.

flip_t<F> has the following member functions:

// Construction
flip_t() = default;
explicit constexpr flip_t(const F& func);
explicit constexpr flip_t(F&& func);

// Call with flipped arguments
template<typename T1, typename T2>
constexpr auto operator()(T1&& x, T2&& y) &/const&/&&/const&&
    noexcept(noexcept(std::invoke(base(), std::forward<T2>(y), std::forward<T1>(x))))
    -> decltype(std::invoke(base(), std::forward<T2>(y), std::forward<T1>(x)));

// Retrieve the passed callable
constexpr auto base() const
    -> F;

cppsort::flip takes a Callable f of type F and returns an instance of flip_t<std::decay_t<F>> except in the following cases:

  • When given a flip_t<F>, it returns a F.
  • When given a not_fn_t<flip_t<F>>, it returns a not_fn_t<F>.
  • When given a projection_compare it returns make_projection_compare(flip(f.comparison()), f.projection()).

flip_t<F> is transparent when F is transparent.

flip_t<F> is considered branchless when F is considered branchless.

New in version 1.13.0

not_fn

#include <cpp-sort/comparators/not_fn.h>

The class template not_fn_t is roughly equivalent to the one returned by the C++17 std::not_fn, a function object which, when called, returns the negation of the Callable it holds.

not_fn_t<F> has the following member functions:

// Construction
not_fn_t() = default;
explicit constexpr not_fn_t(const F& func);
explicit constexpr not_fn_t(F&& func);

// Call and negate
template<typename... Args>
constexpr auto operator()(Args&&... args) &/const&/&&/const&&
    noexcept(noexcept(not std::invoke(base(), std::forward<Args>(args)...)))
    -> decltype(not std::invoke(base(), std::forward<Args>(args)...));

// Retrieve the passed callable
constexpr auto base() const
    -> F;

cppsort::not_fn takes a Callable f of type F and returns an instance of not_fn_t<std::decay_t<F>> except in the following cases:

  • When given a not_fn_t<F>, it returns a F.
  • When given a flip_t<not_fn_t<F>>, it returns a flip_t<F>.
  • When given a projection_compare it returns make_projection_compare(not_fn(f.comparison()), f.projection()).

not_fn_t<F> is transparent when F is transparent.

New in version 1.13.0

projection_compare

#include <cpp-sort/comparators/projection_compare.h>

The class template projection_compare can be used to embed a comparison and a projection in a single comparison object, allowing to provide projection support to algorithms that only support comparisons, such as standard library algorithms prior to C++20. Both the passed comparison and projection functions can be Callable.

It is accompanied by a make_projection_compare function template to avoid having to pass the template parameters by hand.

Example:

// Sort a family from older to younger member
std::vector<Person> family = { /* ... */ };
std::sort(family.begin(), family.end(), cppsort::make_projection_compare(std::greater<>{}, &Person::age));

projection_compare<C, P> has the following member functions:

// Construction
projection_compare() = default;
projection_compare(C comp, P proj);

// Call
template<typename T1, typename T2>
constexpr auto operator()(T1&& x, T2&& y) &/const&/&&/const&&
    noexcept(noexcept(std::invoke(comparison(),
                                  std::invoke(projection(), std::forward<T1>(x)),
                                  std::invoke(projection(), std::forward<T1>(y)))))
    -> decltype(std::invoke(comparison(),
                            std::invoke(projection(), std::forward<T1>(x)),
                            std::invoke(projection(), std::forward<T1>(y))));

// Retrieve the passed comparison
constexpr auto comparison() const
    -> C;

// Retrieve the passed projection
constexpr auto projection() const
    -> P;

cppsort::make_projection_compare takes two Callable of types C and P and returns an instance of projection_compare<std::decay_t<C>, std::decay_t<P>> except in the following cases:

projection_compare is transparent when the passed comparison and projection are both transparent.

projection_compare is considered branchless when the projection it wraps is considered branchless and the comparison it wraps is considered branchless when called with the result of the projection.

New in version 1.9.0

Changed in version 1.13.0: projection_compare is now conditionally default-constructible.

Changed in version 1.13.0: make_projection_compare now returns the comparison directly when the passed projection is an identity function object.