Skip to content

Commit

Permalink
Add cmp::partial_min and partial_max functions
Browse files Browse the repository at this point in the history
These do the same as cmp::min/max, but accept arguments that are only partially_ordered, arbitrarily returning the first argument for min, or the second argument for max in the case where the arguments are unordered.
  • Loading branch information
tcbrindle committed Jan 31, 2024
1 parent 5114d5d commit 20c0d98
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 0 deletions.
50 changes: 50 additions & 0 deletions include/flux/core/functional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,12 +333,62 @@ struct max_fn {
}
};

struct partial_min_fn {
template <typename T, typename U>
requires same_decayed<T, U> &&
std::common_reference_with<T, U> &&
ordering_invocable<std::compare_three_way&, T&, U&>
[[nodiscard]]
constexpr auto operator()(T&& a, U&& b) const
-> std::common_reference_t<T, U>
{
return (b < a) ? FLUX_FWD(b) : FLUX_FWD(a);
}

template <typename T, typename U, typename Cmp>
requires same_decayed<T, U> &&
std::common_reference_with<T, U> &&
ordering_invocable<Cmp&, T&, U&>
[[nodiscard]]
constexpr auto operator()(T&& a, U&& b, Cmp cmp) const
-> std::common_reference_t<T, U>
{
return std::invoke(cmp, b, a) < 0 ? FLUX_FWD(b) : FLUX_FWD(a);
}
};

struct partial_max_fn {
template <typename T, typename U>
requires same_decayed<T, U> &&
std::common_reference_with<T, U> &&
ordering_invocable<std::compare_three_way&, T&, U&>
[[nodiscard]]
constexpr auto operator()(T&& a, U&& b) const
-> std::common_reference_t<T, U>
{
return !(b < a) ? FLUX_FWD(b) : FLUX_FWD(a);
}

template <typename T, typename U, typename Cmp>
requires same_decayed<T, U> &&
std::common_reference_with<T, U> &&
ordering_invocable<Cmp&, T&, U&>
[[nodiscard]]
constexpr auto operator()(T&& a, U&& b, Cmp cmp) const
-> std::common_reference_t<T, U>
{
return !(std::invoke(cmp, b, a) < 0) ? FLUX_FWD(b) : FLUX_FWD(a);
}
};

} // namespace detail

FLUX_EXPORT inline constexpr auto compare = std::compare_three_way{};
FLUX_EXPORT inline constexpr auto reverse_compare = flip(compare);
FLUX_EXPORT inline constexpr auto min = detail::min_fn{};
FLUX_EXPORT inline constexpr auto max = detail::max_fn{};
FLUX_EXPORT inline constexpr auto partial_min = detail::partial_min_fn{};
FLUX_EXPORT inline constexpr auto partial_max = detail::partial_max_fn{};

} // namespace cmp

Expand Down
56 changes: 56 additions & 0 deletions test/test_predicates.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,61 @@ constexpr bool test_comparisons()
}
static_assert(test_comparisons());

constexpr bool test_partial_min_max()
{
namespace cmp = flux::cmp;

struct Test {
bool operator==(Test const&) const = default;
constexpr auto operator<=>(Test const&) const {
return std::partial_ordering::unordered;
}
};

// partial_min works just like min for sensible types
{
int i = 100, j = 10;
int& r = cmp::partial_min(i, j);

STATIC_CHECK(&r == &j);
}

// for partially ordered types, partial_min returns the first element
// if the arguments are unordered
{
Test const t1, t2;

cmp::compare(t1, t2);

Test const& r = cmp::partial_min(t1, t2);

STATIC_CHECK(&r == &t1);
}

// partial_max works just like min for sensible types
{
int i = 100, j = 10;
int& r = cmp::partial_max(i, j);

STATIC_CHECK(&r == &i);
}

// for partially ordered types, partial_max returns the second element
// if the arguments are unordered
{
Test const t1, t2;

cmp::compare(t1, t2);

Test const& r = cmp::partial_max(t1, t2);

STATIC_CHECK(&r == &t2);
}

return true;
}
static_assert(test_partial_min_max());

}

TEST_CASE("predicates")
Expand All @@ -253,5 +308,6 @@ TEST_CASE("predicates")
TEST_CASE("comparators")
{
REQUIRE(test_comparisons());
REQUIRE(test_partial_min_max());
}

0 comments on commit 20c0d98

Please sign in to comment.