Implement spaceship operator <=> for No-STL case (#1604)

This was disabled explicitly in recent commit
232c948674cff1b4b72c2d6b3acfa7342d096dd1 to prevent using
the <compare> header from STL.

Now, re-enabling by implementing an ETL version of it in compare.h
This commit is contained in:
Roland Reichwein 2026-09-17 12:38:18 +02:00 committed by GitHub
parent eb793d2b82
commit 2e2c5d0199
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 667 additions and 48 deletions

View File

@ -7,23 +7,32 @@ title: "compare"
Since: `TBC`
{{< /callout >}}
A helper class that defines `<=`, `>`, `>=` in terms of `etl::less` or a user supplied compare type.
A helper for deriving comparison operations from a strict weak ordering.
`etl::compare<T, TLess>` exposes boolean predicates and a three-state comparison result.
`etl::three_way_compare` returns `-1`, `0`, or `1` using the same comparison policy.
```cpp
template <typename T, typename TLess = etl::less<T> >
struct compare
```
```cpp
template <typename T, typename TLess = etl::less<T> >
ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs)
```
## Types
```cpp
first_argument_type;
second_argument_type;
result_type;
cmp_result;
```
`cmp_result` has the values `Less`, `Equal`, and `Greater`.
## Members
```cpp
static result_type lt(first_argument_type lhs, second_argument_type rhs)
static bool lt(first_argument_type lhs, second_argument_type rhs)
```
**Return**
`true` if `lhs < rhs`, otherwise `false`.
@ -31,7 +40,7 @@ static result_type lt(first_argument_type lhs, second_argument_type rhs)
---
```cpp
static result_type lte(first_argument_type lhs, second_argument_type rhs)
static bool lte(first_argument_type lhs, second_argument_type rhs)
```
**Return**
`true` if `lhs <= rhs`, otherwise `false`.
@ -39,7 +48,7 @@ static result_type lte(first_argument_type lhs, second_argument_type rhs)
---
```cpp
static result_type gt(first_argument_type lhs, second_argument_type rhs)
static bool gt(first_argument_type lhs, second_argument_type rhs)
```
**Return**
`true` if `lhs > rhs`, otherwise `false`.
@ -47,14 +56,74 @@ static result_type gt(first_argument_type lhs, second_argument_type rhs)
---
```cpp
static result_type gte(first_argument_type lhs, second_argument_type rhs)
static bool gte(first_argument_type lhs, second_argument_type rhs)
```
**Return**
`true` if `lhs >= rhs`, otherwise `false`.
---
```cpp
static bool eq(first_argument_type lhs, second_argument_type rhs)
```
**Return**
`true` if `lhs == rhs`, otherwise `false`.
---
```cpp
static bool ne(first_argument_type lhs, second_argument_type rhs)
```
**Return**
`true` if `lhs != rhs`, otherwise `false`.
---
```cpp
static cmp_result cmp(first_argument_type lhs, second_argument_type rhs)
```
**Return**
`Less`, `Equal`, or `Greater`.
## Three-way comparison
`etl::three_way_compare(lhs, rhs)` returns the same ordering as `compare<T, TLess>::cmp(lhs, rhs)`,
but as an `int` with the values `-1`, `0`, or `1`.
## C++20 comparison categories
When compiling as C++20, `compare.h` makes the comparison categories available in the `etl`
namespace, so ETL and user code can name them regardless of the STL configuration.
```cpp
class strong_ordering;
class weak_ordering;
class partial_ordering;
```
* When the STL is available these are aliases for `std::strong_ordering`, `std::weak_ordering`
and `std::partial_ordering`.
* When the STL is not available, `compare.h` provides its own minimal implementations that
follow the standard semantics: the named values `less`, `equivalent`, `greater`
(plus `equal` for `strong_ordering` and `unordered` for `partial_ordering`), comparison
against the literal `0` only, and the implicit conversions
`strong_ordering` &rarr; `weak_ordering` &rarr; `partial_ordering`.
An `unordered` result compares `false` against `0` for all of `<`, `<=`, `==`, `>=` and `>`.
```cpp
ETL_CONSTEXPR strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT;
ETL_CONSTEXPR weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT;
ETL_CONSTEXPR partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT;
```
These helpers convert a three-way comparison result, such as the one returned by
`etl::three_way_compare`, into a comparison category. Negative values map to `less`, positive
values to `greater`, and zero to `equal`/`equivalent`. `unordered` cannot be expressed as an
integer result, so use `etl::partial_ordering::unordered` directly for that case.
## Example 1
```cpp
using std::less
struct Test
{
int a;
@ -84,6 +153,11 @@ bool operator >=(const Test& lhs, const Test& rhs)
{
return Compare::gte(lhs, rhs);
}
int operator <=>(const Test& lhs, const Test& rhs)
{
return etl::three_way_compare(lhs, rhs);
}
```
## Example 2
@ -124,6 +198,11 @@ bool operator >=(const Test& lhs, const Test& rhs)
{
return Compare::gte(lhs, rhs);
}
int operator <=>(const Test& lhs, const Test& rhs)
{
return etl::three_way_compare<Test, LessThan>(lhs, rhs);
}
```
## Example 3
@ -158,3 +237,13 @@ bool operator >=(const Test& lhs, const Test& rhs)
return Test::gte(lhs, rhs);
}
```
## Example 4
C++20, with or without the STL.
```cpp
etl::strong_ordering operator <=>(const Test& lhs, const Test& rhs)
{
return etl::make_strong_ordering(etl::three_way_compare(lhs, rhs));
}
```

View File

@ -37,6 +37,7 @@ SOFTWARE.
#if ETL_USING_CPP11
#include "compare.h"
#include "hash.h"
#include "integral_limits.h"
#include "type_traits.h"

View File

@ -35,6 +35,10 @@ SOFTWARE.
#include "functional.h"
#include "parameter_type.h"
#if ETL_USING_CPP20 && ETL_USING_STL
#include <compare>
#endif
//*****************************************************************************
///\defgroup compare compare
/// Comparisons only using less than operator
@ -42,6 +46,295 @@ SOFTWARE.
//*****************************************************************************
namespace etl
{
#if ETL_USING_CPP20
#if ETL_USING_STL
//***************************************************************************
/// The STL supplies the C++20 comparison categories, so simply expose them
/// through the etl namespace. This allows ETL code and user code to refer to
/// etl::strong_ordering (etc.) regardless of the STL configuration.
//***************************************************************************
using std::partial_ordering;
using std::strong_ordering;
using std::weak_ordering;
#else
namespace private_compare
{
//*************************************************************************
/// A type that can only be constructed from the literal 0.
/// Used to constrain the comparison-category operators to comparisons
/// against the literal 0, as required by the standard.
//*************************************************************************
struct only_literal_zero
{
ETL_CONSTEXPR only_literal_zero(only_literal_zero*) ETL_NOEXCEPT {}
};
typedef signed char ordering_value_type;
inline constexpr ordering_value_type ordering_less = -1;
inline constexpr ordering_value_type ordering_equivalent = 0;
inline constexpr ordering_value_type ordering_greater = 1;
inline constexpr ordering_value_type ordering_unordered = 2;
} // namespace private_compare
//***************************************************************************
/// Minimal partial-ordering category for C++20 three-way comparisons when the
/// STL is unavailable.
/// Unlike the other categories this one has an 'unordered' state, for which
/// every comparison against 0 is false.
//***************************************************************************
class partial_ordering
{
public:
static const partial_ordering less;
static const partial_ordering equivalent;
static const partial_ordering greater;
static const partial_ordering unordered;
friend ETL_CONSTEXPR bool operator==(partial_ordering lhs, partial_ordering rhs) ETL_NOEXCEPT = default;
friend ETL_CONSTEXPR bool operator==(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value == private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator<(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value == private_compare::ordering_less;
}
friend ETL_CONSTEXPR bool operator>(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value == private_compare::ordering_greater;
}
friend ETL_CONSTEXPR bool operator<=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return (v.value == private_compare::ordering_less) || (v.value == private_compare::ordering_equivalent);
}
friend ETL_CONSTEXPR bool operator>=(partial_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return (v.value == private_compare::ordering_greater) || (v.value == private_compare::ordering_equivalent);
}
friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
{
return v > 0;
}
friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
{
return v < 0;
}
friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
{
return v >= 0;
}
friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, partial_ordering v) ETL_NOEXCEPT
{
return v <= 0;
}
private:
explicit ETL_CONSTEXPR partial_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT
: value(value_)
{
}
friend class weak_ordering;
friend class strong_ordering;
private_compare::ordering_value_type value;
};
inline constexpr partial_ordering partial_ordering::less(private_compare::ordering_less);
inline constexpr partial_ordering partial_ordering::equivalent(private_compare::ordering_equivalent);
inline constexpr partial_ordering partial_ordering::greater(private_compare::ordering_greater);
inline constexpr partial_ordering partial_ordering::unordered(private_compare::ordering_unordered);
//***************************************************************************
/// Minimal weak-ordering category for C++20 three-way comparisons when the
/// STL is unavailable.
//***************************************************************************
class weak_ordering
{
public:
static const weak_ordering less;
static const weak_ordering equivalent;
static const weak_ordering greater;
ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT
{
return partial_ordering(value);
}
friend ETL_CONSTEXPR bool operator==(weak_ordering lhs, weak_ordering rhs) ETL_NOEXCEPT = default;
friend ETL_CONSTEXPR bool operator==(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value == private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator<(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value < private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator>(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value > private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator<=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value <= private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator>=(weak_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value >= private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
{
return v > 0;
}
friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
{
return v < 0;
}
friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
{
return v >= 0;
}
friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, weak_ordering v) ETL_NOEXCEPT
{
return v <= 0;
}
private:
explicit ETL_CONSTEXPR weak_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT
: value(value_)
{
}
friend class strong_ordering;
private_compare::ordering_value_type value;
};
inline constexpr weak_ordering weak_ordering::less(private_compare::ordering_less);
inline constexpr weak_ordering weak_ordering::equivalent(private_compare::ordering_equivalent);
inline constexpr weak_ordering weak_ordering::greater(private_compare::ordering_greater);
//***************************************************************************
/// Minimal strong-ordering category for C++20 three-way comparisons when the
/// STL is unavailable.
//***************************************************************************
class strong_ordering
{
public:
static const strong_ordering less;
static const strong_ordering equal;
static const strong_ordering equivalent;
static const strong_ordering greater;
ETL_CONSTEXPR operator partial_ordering() const ETL_NOEXCEPT
{
return partial_ordering(value);
}
ETL_CONSTEXPR operator weak_ordering() const ETL_NOEXCEPT
{
return weak_ordering(value);
}
friend ETL_CONSTEXPR bool operator==(strong_ordering lhs, strong_ordering rhs) ETL_NOEXCEPT = default;
friend ETL_CONSTEXPR bool operator==(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value == private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator<(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value < private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator>(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value > private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator<=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value <= private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator>=(strong_ordering v, private_compare::only_literal_zero) ETL_NOEXCEPT
{
return v.value >= private_compare::ordering_equivalent;
}
friend ETL_CONSTEXPR bool operator<(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
{
return v > 0;
}
friend ETL_CONSTEXPR bool operator>(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
{
return v < 0;
}
friend ETL_CONSTEXPR bool operator<=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
{
return v >= 0;
}
friend ETL_CONSTEXPR bool operator>=(private_compare::only_literal_zero, strong_ordering v) ETL_NOEXCEPT
{
return v <= 0;
}
private:
explicit ETL_CONSTEXPR strong_ordering(private_compare::ordering_value_type value_) ETL_NOEXCEPT
: value(value_)
{
}
private_compare::ordering_value_type value;
};
inline constexpr strong_ordering strong_ordering::less(private_compare::ordering_less);
inline constexpr strong_ordering strong_ordering::equal(private_compare::ordering_equivalent);
inline constexpr strong_ordering strong_ordering::equivalent(private_compare::ordering_equivalent);
inline constexpr strong_ordering strong_ordering::greater(private_compare::ordering_greater);
#endif
//***************************************************************************
/// Converts a three-way comparison result to an etl::strong_ordering.
/// Negative values map to 'less', positive values to 'greater' and zero to
/// 'equal'.
//***************************************************************************
ETL_NODISCARD ETL_CONSTEXPR inline etl::strong_ordering make_strong_ordering(int value) ETL_NOEXCEPT
{
return (value < 0) ? etl::strong_ordering::less : (value > 0) ? etl::strong_ordering::greater : etl::strong_ordering::equal;
}
//***************************************************************************
/// Converts a three-way comparison result to an etl::weak_ordering.
/// Negative values map to 'less', positive values to 'greater' and zero to
/// 'equivalent'.
//***************************************************************************
ETL_NODISCARD ETL_CONSTEXPR inline etl::weak_ordering make_weak_ordering(int value) ETL_NOEXCEPT
{
return (value < 0) ? etl::weak_ordering::less : (value > 0) ? etl::weak_ordering::greater : etl::weak_ordering::equivalent;
}
//***************************************************************************
/// Converts a three-way comparison result to an etl::partial_ordering.
/// Negative values map to 'less', positive values to 'greater' and zero to
/// 'equivalent'.
/// Note that 'unordered' cannot be expressed by an integer comparison result,
/// so use etl::partial_ordering::unordered directly for that case.
//***************************************************************************
ETL_NODISCARD ETL_CONSTEXPR inline etl::partial_ordering make_partial_ordering(int value) ETL_NOEXCEPT
{
return (value < 0) ? etl::partial_ordering::less : (value > 0) ? etl::partial_ordering::greater : etl::partial_ordering::equivalent;
}
#endif
//***************************************************************************
/// Defines <=, >, >=, ==, !=, <=> in terms of <
/// Default implementation of TLess is etl::less
@ -105,7 +398,7 @@ namespace etl
#endif
ETL_CONSTEXPR14 int three_way_compare(const T& lhs, const T& rhs)
{
return etl::compare<T>::cmp(lhs, rhs);
return etl::compare<T, TLess>::cmp(lhs, rhs);
}
} // namespace etl

View File

@ -211,11 +211,11 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::day& d1, const etl::chrono::day& d2) ETL_NOEXCEPT
{
return (static_cast<unsigned>(d1) <=> static_cast<unsigned>(d2));
return etl::make_strong_ordering(d1.compare(d2));
}
#endif

View File

@ -482,7 +482,7 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
template <typename TRep1, typename TPeriod1, typename TRep2, typename TPeriod2>
[[nodiscard]]
constexpr auto operator<=>(const etl::chrono::duration<TRep1, TPeriod1>& lhs, const etl::chrono::duration<TRep2, TPeriod2>& rhs) ETL_NOEXCEPT
@ -492,7 +492,37 @@ namespace etl
common_t l = etl::chrono::duration_cast<common_t>(lhs);
common_t r = etl::chrono::duration_cast<common_t>(rhs);
#if ETL_USING_STL
return (l.count() <=> r.count());
#else
// The STL returns the ordering category of the representation type.
// Mirror that: a floating point representation is only partially ordered.
if constexpr (etl::is_floating_point<typename common_t::rep>::value)
{
if (l.count() < r.count())
{
return etl::partial_ordering::less;
}
else if (r.count() < l.count())
{
return etl::partial_ordering::greater;
}
else if ((l.count() <= r.count()) && (r.count() <= l.count()))
{
// Equivalent. Expressed without '==' so that -Wfloat-equal is not triggered.
return etl::partial_ordering::equivalent;
}
else
{
// At least one of the values is NaN.
return etl::partial_ordering::unordered;
}
}
else
{
return etl::make_strong_ordering((l.count() < r.count()) ? -1 : (r.count() < l.count()) ? 1 : 0);
}
#endif
}
#endif

View File

@ -224,11 +224,11 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::month& d1, const etl::chrono::month& d2) ETL_NOEXCEPT
{
return (static_cast<unsigned>(d1) <=> static_cast<unsigned>(d2));
return etl::make_strong_ordering(d1.compare(d2));
}
#endif

View File

@ -178,7 +178,7 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::month_day& lhs, const etl::chrono::month_day& rhs) ETL_NOEXCEPT
{
@ -298,11 +298,11 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::month_day_last& mdl1, const etl::chrono::month_day_last& mdl2) ETL_NOEXCEPT
{
return (static_cast<unsigned>(mdl1.month()) <=> static_cast<unsigned>(mdl2.month()));
return mdl1.month() <=> mdl2.month();
}
#endif
} // namespace chrono

View File

@ -353,7 +353,7 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
template <typename TClock, typename TDuration1, typename TDuration2>
[[nodiscard]]
constexpr auto operator<=>(const etl::chrono::time_point<TClock, TDuration1>& lhs, const etl::chrono::time_point<TClock, TDuration2>& rhs)

View File

@ -240,11 +240,11 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::year& y1, const etl::chrono::year& y2) ETL_NOEXCEPT
{
return (static_cast<int>(y1) <=> static_cast<int>(y2));
return etl::make_strong_ordering(y1.compare(y2));
}
#endif

View File

@ -234,7 +234,7 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::year_month& lhs, const etl::chrono::year_month& rhs) ETL_NOEXCEPT
{

View File

@ -426,7 +426,7 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::year_month_day& lhs, const etl::chrono::year_month_day& rhs) ETL_NOEXCEPT
{
@ -722,7 +722,7 @@ namespace etl
//***********************************************************************
/// Spaceship operator
//***********************************************************************
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
[[nodiscard]]
inline constexpr auto operator<=>(const etl::chrono::year_month_day_last& lhs, const etl::chrono::year_month_day_last& rhs) ETL_NOEXCEPT
{

View File

@ -278,7 +278,7 @@ namespace
CHECK_TRUE(day10 >= day10);
CHECK_TRUE(day20 >= day10);
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
CHECK_TRUE((day10 <=> day10) == 0);
CHECK_TRUE((day10 <=> day20) < 0);
CHECK_TRUE((day20 <=> day10) > 0);

View File

@ -1045,16 +1045,41 @@ namespace
CHECK_EQUAL(1, s1.count()); // 10 seconds % (-3) seconds = 1 second
}
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
//*************************************************************************
TEST(test_duration_spaceship_operator)
{
Chrono::seconds s1(2); // 2 seconds
Chrono::milliseconds ms1(1500); // 1500 milliseconds
CHECK_TRUE(s1 <=> s1 == std::strong_ordering::equal);
CHECK_TRUE(ms1 <=> s1 == std::strong_ordering::less);
CHECK_TRUE(s1 <=> ms1 == std::strong_ordering::greater);
CHECK_TRUE(s1 <=> s1 == etl::strong_ordering::equal);
CHECK_TRUE(ms1 <=> s1 == etl::strong_ordering::less);
CHECK_TRUE(s1 <=> ms1 == etl::strong_ordering::greater);
static_assert(etl::is_same<decltype(s1 <=> ms1), etl::strong_ordering>::value, "Must return etl::strong_ordering");
}
//*************************************************************************
TEST(test_duration_spaceship_operator_floating_point_representation)
{
typedef Chrono::duration<double> fp_seconds;
fp_seconds fs1(1.5);
fp_seconds fs2(2.5);
// A floating point representation is only partially ordered.
static_assert(etl::is_same<decltype(fs1 <=> fs2), etl::partial_ordering>::value, "Must return etl::partial_ordering");
CHECK_TRUE((fs1 <=> fs1) == etl::partial_ordering::equivalent);
CHECK_TRUE((fs1 <=> fs2) == etl::partial_ordering::less);
CHECK_TRUE((fs2 <=> fs1) == etl::partial_ordering::greater);
fp_seconds nan_seconds(etl::numeric_limits<double>::quiet_NaN());
CHECK_TRUE((nan_seconds <=> fs1) == etl::partial_ordering::unordered);
CHECK_FALSE((nan_seconds <=> fs1) < 0);
CHECK_FALSE((nan_seconds <=> fs1) == 0);
CHECK_FALSE((nan_seconds <=> fs1) > 0);
}
#endif

View File

@ -303,7 +303,7 @@ namespace
CHECK_TRUE(month1 >= month1);
CHECK_TRUE(month2 >= month1);
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
CHECK_TRUE((month1 <=> month1) == 0);
CHECK_TRUE((month1 <=> month2) < 0);
CHECK_TRUE((month2 <=> month1) > 0);

View File

@ -89,7 +89,7 @@ namespace
CHECK_FALSE(md.ok()); // Invalid month_day
}
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
//*************************************************************************
TEST(test_month_day_spaceship_operator)
{
@ -97,9 +97,9 @@ namespace
Chrono::month_day md2{Chrono::February, Chrono::day{10}};
Chrono::month_day md3{Chrono::January, Chrono::day{15}};
CHECK_TRUE((md1 <=> md3) == std::strong_ordering::equal); // Same month and day
CHECK_TRUE((md1 <=> md2) == std::strong_ordering::less); // Different month and day
CHECK_TRUE((md2 <=> md1) == std::strong_ordering::greater); // Same month and day
CHECK_TRUE((md1 <=> md3) == etl::strong_ordering::equal); // Same month and day
CHECK_TRUE((md1 <=> md2) == etl::strong_ordering::less); // Different month and day
CHECK_TRUE((md2 <=> md1) == etl::strong_ordering::greater); // Same month and day
}
#endif

View File

@ -100,6 +100,20 @@ namespace
CHECK_TRUE(month_day_last1 != month_day_last3);
}
#if ETL_USING_CPP20
//*************************************************************************
TEST(test_month_day_last_spaceship_operator)
{
Chrono::month_day_last month_day_last1(Chrono::January);
Chrono::month_day_last month_day_last2(Chrono::February);
Chrono::month_day_last month_day_last3(Chrono::January);
CHECK_TRUE((month_day_last1 <=> month_day_last3) == etl::strong_ordering::equal);
CHECK_TRUE((month_day_last1 <=> month_day_last2) == etl::strong_ordering::less);
CHECK_TRUE((month_day_last2 <=> month_day_last1) == etl::strong_ordering::greater);
}
#endif
#if ETL_USING_ETL_CHRONO
//*************************************************************************
TEST(test_month_day_last_hashes_are_unique)

View File

@ -365,7 +365,7 @@ namespace
CHECK_FALSE(tp10d >= tp20h);
CHECK_FALSE(tp10h >= tp20d);
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
CHECK_TRUE((tp10h <=> tp10h) == 0);
CHECK_TRUE((tp10h <=> tp20h) < 0);
CHECK_TRUE((tp20h <=> tp10h) > 0);
@ -377,6 +377,8 @@ namespace
CHECK_TRUE((tp10d <=> tp10h) == 0);
CHECK_TRUE((tp10h <=> tp20d) < 0);
CHECK_TRUE((tp20d <=> tp10h) > 0);
static_assert(etl::is_same<decltype(tp10h <=> tp20d), etl::strong_ordering>::value, "Must return etl::strong_ordering");
#endif
}

View File

@ -284,7 +284,7 @@ namespace
CHECK_FALSE(year10 == year20);
CHECK_TRUE(year10 != year20);
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
CHECK_TRUE((year10 <=> year10) == 0);
CHECK_TRUE((year10 <=> year20) < 0);
CHECK_TRUE((year20 <=> year10) > 0);

View File

@ -89,7 +89,7 @@ namespace
CHECK_FALSE(ym.ok()); // Invalid year_month
}
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
//*************************************************************************
TEST(test_year_month_spaceship_operator)
{
@ -97,11 +97,11 @@ namespace
Chrono::year_month ym2{Chrono::year(2001), Chrono::January};
Chrono::year_month ym3{Chrono::year(2000), Chrono::February};
CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal);
CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less);
CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less);
CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal);
CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less);
CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less);
CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater);
}
#endif

View File

@ -98,7 +98,7 @@ namespace
CHECK_FALSE(ymd.ok()); // Invalid year_month_day
}
#if ETL_USING_CPP20 && ETL_USING_STL
#if ETL_USING_CPP20
//*************************************************************************
TEST(test_year_month_day_spaceship_operator)
{
@ -107,13 +107,13 @@ namespace
Chrono::year_month_day ym3{Chrono::year(2000), Chrono::February, Chrono::day(1)};
Chrono::year_month_day ym4{Chrono::year(2000), Chrono::January, Chrono::day(2)};
CHECK_TRUE((ym1 <=> ym1) == std::strong_ordering::equal);
CHECK_TRUE((ym1 <=> ym2) == std::strong_ordering::less);
CHECK_TRUE((ym2 <=> ym1) == std::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym3) == std::strong_ordering::less);
CHECK_TRUE((ym3 <=> ym1) == std::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym4) == std::strong_ordering::less);
CHECK_TRUE((ym4 <=> ym1) == std::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym1) == etl::strong_ordering::equal);
CHECK_TRUE((ym1 <=> ym2) == etl::strong_ordering::less);
CHECK_TRUE((ym2 <=> ym1) == etl::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym3) == etl::strong_ordering::less);
CHECK_TRUE((ym3 <=> ym1) == etl::strong_ordering::greater);
CHECK_TRUE((ym1 <=> ym4) == etl::strong_ordering::less);
CHECK_TRUE((ym4 <=> ym1) == etl::strong_ordering::greater);
}
#endif

View File

@ -160,6 +160,23 @@ namespace
CHECK_TRUE(year_month_day_last1 != year_month_day_last3);
}
#if ETL_USING_CPP20
//*************************************************************************
TEST(test_year_month_day_last_spaceship_operator)
{
Chrono::year_month_day_last year_month_day_last1(Chrono::year(2000), Chrono::month_day_last(Chrono::January));
Chrono::year_month_day_last year_month_day_last2(Chrono::year(2001), Chrono::month_day_last(Chrono::January));
Chrono::year_month_day_last year_month_day_last3(Chrono::year(2000), Chrono::month_day_last(Chrono::February));
Chrono::year_month_day_last year_month_day_last4(Chrono::year(2000), Chrono::month_day_last(Chrono::January));
CHECK_TRUE((year_month_day_last1 <=> year_month_day_last4) == etl::strong_ordering::equal);
CHECK_TRUE((year_month_day_last1 <=> year_month_day_last2) == etl::strong_ordering::less);
CHECK_TRUE((year_month_day_last2 <=> year_month_day_last1) == etl::strong_ordering::greater);
CHECK_TRUE((year_month_day_last1 <=> year_month_day_last3) == etl::strong_ordering::less);
CHECK_TRUE((year_month_day_last3 <=> year_month_day_last1) == etl::strong_ordering::greater);
}
#endif
#if ETL_USING_ETL_CHRONO
//*************************************************************************
TEST(test_year_month_day_last_hashes_are_unique)

View File

@ -91,6 +91,20 @@ namespace
}
};
struct ComparatorOnlyObject
{
int a;
int b;
};
struct LessBySecond
{
bool constexpr operator()(const ComparatorOnlyObject& lhs, const ComparatorOnlyObject& rhs) const
{
return lhs.b < rhs.b;
}
};
typedef etl::compare<Object, LessTest> CompareTest;
SUITE(test_compare)
@ -191,5 +205,139 @@ namespace
CHECK_EQUAL(CompareInt::Greater, cmp_test_greater);
CHECK_EQUAL(CompareInt::Equal, cmp_test_equal);
}
#if ETL_USING_CPP20
//*************************************************************************
TEST(test_strong_ordering)
{
constexpr etl::strong_ordering ordering_less(etl::strong_ordering::less);
constexpr etl::strong_ordering ordering_equal(etl::strong_ordering::equal);
constexpr etl::strong_ordering ordering_greater(etl::strong_ordering::greater);
CHECK(ordering_less < 0);
CHECK(ordering_less <= 0);
CHECK(!(ordering_less == 0));
CHECK(!(ordering_less > 0));
CHECK(ordering_equal == 0);
CHECK(ordering_equal <= 0);
CHECK(ordering_equal >= 0);
CHECK(ordering_greater > 0);
CHECK(ordering_greater >= 0);
CHECK(!(ordering_greater == 0));
// Reversed operands.
CHECK(0 > ordering_less);
CHECK(0 < ordering_greater);
static_assert(etl::strong_ordering::equivalent == etl::strong_ordering::equal, "Equivalent must alias equal");
static_assert(etl::is_same<etl::weak_ordering, decltype(etl::weak_ordering(etl::strong_ordering::less))>::value,
"Must convert to weak_ordering");
CHECK(etl::weak_ordering(etl::strong_ordering::less) < 0);
CHECK(etl::partial_ordering(etl::strong_ordering::greater) > 0);
}
//*************************************************************************
TEST(test_make_strong_ordering)
{
constexpr etl::strong_ordering ordering_less = etl::make_strong_ordering(-1);
constexpr etl::strong_ordering ordering_equal = etl::make_strong_ordering(0);
constexpr etl::strong_ordering ordering_greater = etl::make_strong_ordering(1);
CHECK(ordering_less < 0);
CHECK(ordering_equal == 0);
CHECK(ordering_greater > 0);
static_assert(etl::is_same<decltype(etl::make_strong_ordering(0)), etl::strong_ordering>::value, "Must return etl::strong_ordering");
}
//*************************************************************************
TEST(test_weak_ordering)
{
constexpr etl::weak_ordering ordering_less(etl::weak_ordering::less);
constexpr etl::weak_ordering ordering_equivalent(etl::weak_ordering::equivalent);
constexpr etl::weak_ordering ordering_greater(etl::weak_ordering::greater);
CHECK(ordering_less < 0);
CHECK(ordering_equivalent == 0);
CHECK(ordering_greater > 0);
CHECK(0 > ordering_less);
CHECK(0 < ordering_greater);
static_assert(etl::weak_ordering::equivalent == 0, "Equivalent must be zero");
CHECK(etl::partial_ordering(etl::weak_ordering::less) < 0);
}
//*************************************************************************
TEST(test_make_weak_ordering)
{
constexpr etl::weak_ordering ordering_less = etl::make_weak_ordering(-1);
constexpr etl::weak_ordering ordering_equivalent = etl::make_weak_ordering(0);
constexpr etl::weak_ordering ordering_greater = etl::make_weak_ordering(1);
CHECK(ordering_less < 0);
CHECK(ordering_equivalent == 0);
CHECK(ordering_greater > 0);
static_assert(etl::is_same<decltype(etl::make_weak_ordering(0)), etl::weak_ordering>::value, "Must return etl::weak_ordering");
}
//*************************************************************************
TEST(test_partial_ordering)
{
constexpr etl::partial_ordering ordering_less(etl::partial_ordering::less);
constexpr etl::partial_ordering ordering_equivalent(etl::partial_ordering::equivalent);
constexpr etl::partial_ordering ordering_greater(etl::partial_ordering::greater);
constexpr etl::partial_ordering ordering_unordered(etl::partial_ordering::unordered);
CHECK(ordering_less < 0);
CHECK(ordering_equivalent == 0);
CHECK(ordering_greater > 0);
// An unordered result compares false against zero in every direction.
CHECK(!(ordering_unordered < 0));
CHECK(!(ordering_unordered <= 0));
CHECK(!(ordering_unordered == 0));
CHECK(!(ordering_unordered >= 0));
CHECK(!(ordering_unordered > 0));
CHECK(ordering_unordered != 0);
static_assert(etl::partial_ordering::equivalent == 0, "Equivalent must be zero");
}
//*************************************************************************
TEST(test_make_partial_ordering)
{
constexpr etl::partial_ordering ordering_less = etl::make_partial_ordering(-1);
constexpr etl::partial_ordering ordering_equivalent = etl::make_partial_ordering(0);
constexpr etl::partial_ordering ordering_greater = etl::make_partial_ordering(1);
CHECK(ordering_less < 0);
CHECK(ordering_equivalent == 0);
CHECK(ordering_greater > 0);
static_assert(etl::is_same<decltype(etl::make_partial_ordering(0)), etl::partial_ordering>::value, "Must return etl::partial_ordering");
}
//*************************************************************************
TEST(test_three_way_compare_custom_less)
{
constexpr ComparatorOnlyObject lhs = {5, 1};
constexpr ComparatorOnlyObject rhs = {1, 2};
constexpr ComparatorOnlyObject eq = {9, 1};
constexpr int cmp_less = etl::three_way_compare<ComparatorOnlyObject, LessBySecond>(lhs, rhs);
constexpr int cmp_greater = etl::three_way_compare<ComparatorOnlyObject, LessBySecond>(rhs, lhs);
constexpr int cmp_equal = etl::three_way_compare<ComparatorOnlyObject, LessBySecond>(lhs, eq);
CHECK_EQUAL(-1, cmp_less);
CHECK_EQUAL(1, cmp_greater);
CHECK_EQUAL(0, cmp_equal);
CHECK(etl::make_strong_ordering(cmp_less) < 0);
CHECK(etl::make_strong_ordering(cmp_greater) > 0);
CHECK(etl::make_strong_ordering(cmp_equal) == 0);
}
#endif
}
} // namespace