diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2023-05-17 15:01:58 +0200 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2023-11-28 21:30:33 +0100 |
commit | 96f494bf92d246b741b6ae6261c93ef557ef392b (patch) | |
tree | df95a915535a9acc26166c45a21006a76f996192 /src/corelib/global/qcomparehelpers.h | |
parent | 5a28aacd850da6ba4a4e4cd3067f60d0f82dd8c0 (diff) |
Implement helper Qt::compareThreeWay() function for built-in types
The helper function
RetType compareThreeWay(const T &left, const T &right) noexcept;
is used for C++20-comparison macros. Normally it's the user's
responsibility to provide this function as a hidden friend of the class
which uses the comparison helper macros.
For built-in types we provide the implementation inside the Qt
namespace.
We have to use custom IsIntegralType trait because libstdc++ only
treats __{u}int128_t types as integral when compiling in -std=gnu++XX
mode, and we compile Qt in -std=c++XX mode.
This patch provides the implementations only for compareThreeWay()
overloads, because there is no need to implement comparesEqual() for
built-in types. It would just be equivalent to calling operator==(),
so the user can do it directly.
Task-number: QTBUG-104113
Change-Id: I7b3f395458e1ee4c64f442ad48bbf4fec4c19c52
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
Diffstat (limited to 'src/corelib/global/qcomparehelpers.h')
-rw-r--r-- | src/corelib/global/qcomparehelpers.h | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h index f445e29b00..d0991e91b3 100644 --- a/src/corelib/global/qcomparehelpers.h +++ b/src/corelib/global/qcomparehelpers.h @@ -15,11 +15,15 @@ #endif #include <QtCore/qoverload.h> +#include <QtCore/qttypetraits.h> +#include <QtCore/qtypes.h> #ifdef __cpp_lib_three_way_comparison #include <compare> #endif +#include <functional> // std::less + QT_BEGIN_NAMESPACE /* @@ -302,6 +306,134 @@ QT_BEGIN_NAMESPACE #define Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(...) \ QT_OVERLOADED_MACRO(QT_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE, __VA_ARGS__) +namespace QtPrivate { + +template <typename T> +constexpr bool IsIntegralType_v = std::numeric_limits<std::remove_const_t<T>>::is_specialized + && std::numeric_limits<std::remove_const_t<T>>::is_integer; + +} // namespace QtPrivate + +namespace Qt { + +template <typename T, typename U> +using if_integral = + std::enable_if_t<QtPrivate::IsIntegralType_v<std::remove_reference_t<T>> + && QtPrivate::IsIntegralType_v<std::remove_reference_t<U>>, + bool>; + +template <typename T, typename U> +using if_floating_point = + std::enable_if_t<std::conjunction_v<std::is_floating_point<std::remove_reference_t<T>>, + std::is_floating_point<std::remove_reference_t<U>>>, + bool>; + +template <typename T, typename U> +using if_integral_and_floating_point = + std::enable_if_t<QtPrivate::IsIntegralType_v<std::remove_reference_t<T>> + && std::is_floating_point_v<std::remove_reference_t<U>>, + bool>; + +template <typename T, typename U> +using if_compatible_pointers = + std::enable_if_t<std::disjunction_v<std::is_same<T, U>, + std::is_base_of<T, U>, + std::is_base_of<U, T>>, + bool>; + +template <typename Enum> +using if_enum = std::enable_if_t<std::is_enum_v<Enum>, bool>; + +template <typename LeftInt, typename RightInt, + if_integral<LeftInt, RightInt> = true> +constexpr Qt::strong_ordering compareThreeWay(LeftInt lhs, RightInt rhs) noexcept +{ + static_assert(std::is_signed_v<LeftInt> == std::is_signed_v<RightInt>, + "Qt::compareThreeWay() does not allow mixed-sign comparison."); + +#ifdef __cpp_lib_three_way_comparison + return lhs <=> rhs; +#else + if (lhs == rhs) + return Qt::strong_ordering::equivalent; + else if (lhs < rhs) + return Qt::strong_ordering::less; + else + return Qt::strong_ordering::greater; +#endif // __cpp_lib_three_way_comparison +} + +template <typename LeftFloat, typename RightFloat, + if_floating_point<LeftFloat, RightFloat> = true> +constexpr Qt::partial_ordering compareThreeWay(LeftFloat lhs, RightFloat rhs) noexcept +{ +QT_WARNING_PUSH +QT_WARNING_DISABLE_FLOAT_COMPARE +#ifdef __cpp_lib_three_way_comparison + return lhs <=> rhs; +#else + if (lhs < rhs) + return Qt::partial_ordering::less; + else if (lhs > rhs) + return Qt::partial_ordering::greater; + else if (lhs == rhs) + return Qt::partial_ordering::equivalent; + else + return Qt::partial_ordering::unordered; +#endif // __cpp_lib_three_way_comparison +QT_WARNING_POP +} + +template <typename IntType, typename FloatType, + if_integral_and_floating_point<IntType, FloatType> = true> +constexpr Qt::partial_ordering compareThreeWay(IntType lhs, FloatType rhs) noexcept +{ + return compareThreeWay(FloatType(lhs), rhs); +} + +template <typename FloatType, typename IntType, + if_integral_and_floating_point<IntType, FloatType> = true> +constexpr Qt::partial_ordering compareThreeWay(FloatType lhs, IntType rhs) noexcept +{ + return compareThreeWay(lhs, FloatType(rhs)); +} + +template <typename LeftType, typename RightType, + if_compatible_pointers<LeftType, RightType> = true> +constexpr Qt::strong_ordering compareThreeWay(const LeftType *lhs, const RightType *rhs) noexcept +{ +#ifdef __cpp_lib_three_way_comparison + return std::compare_three_way{}(lhs, rhs); +#else + if (lhs == rhs) + return Qt::strong_ordering::equivalent; + else if (std::less<>{}(lhs, rhs)) + return Qt::strong_ordering::less; + else + return Qt::strong_ordering::greater; +#endif // __cpp_lib_three_way_comparison +} + +template <typename T> +constexpr Qt::strong_ordering compareThreeWay(const T *lhs, std::nullptr_t rhs) noexcept +{ + return compareThreeWay(lhs, static_cast<const T *>(rhs)); +} + +template <typename T> +constexpr Qt::strong_ordering compareThreeWay(std::nullptr_t lhs, const T *rhs) noexcept +{ + return compareThreeWay(static_cast<const T *>(lhs), rhs); +} + +template <class Enum, if_enum<Enum> = true> +constexpr Qt::strong_ordering compareThreeWay(Enum lhs, Enum rhs) noexcept +{ + return compareThreeWay(qToUnderlying(lhs), qToUnderlying(rhs)); +} + +} // namespace Qt + QT_END_NAMESPACE #endif // QCOMPAREHELPERS_H |