diff options
Diffstat (limited to 'src/corelib/global/qcomparehelpers.h')
-rw-r--r-- | src/corelib/global/qcomparehelpers.h | 222 |
1 files changed, 206 insertions, 16 deletions
diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h index 0e43ac296b..fce751be09 100644 --- a/src/corelib/global/qcomparehelpers.h +++ b/src/corelib/global/qcomparehelpers.h @@ -16,6 +16,7 @@ #include <QtCore/qoverload.h> #include <QtCore/qttypetraits.h> +#include <QtCore/qtypeinfo.h> #include <QtCore/qtypes.h> #ifdef __cpp_lib_three_way_comparison @@ -23,7 +24,7 @@ #endif #include <QtCore/q20type_traits.h> -#include <functional> // std::less +#include <functional> // std::less, std::hash QT_BEGIN_NAMESPACE @@ -145,8 +146,8 @@ template <typename In> constexpr auto to_Qt(In in) noexcept noexcept(noexcept(compareThreeWay(rhs, lhs))) \ { \ const auto r = compareThreeWay(rhs, lhs); \ - if (r > 0) return std::strong_ordering::less; \ - if (r < 0) return std::strong_ordering::greater; \ + if (is_gt(r)) return std::strong_ordering::less; \ + if (is_lt(r)) return std::strong_ordering::greater; \ return r; \ } @@ -157,8 +158,8 @@ template <typename In> constexpr auto to_Qt(In in) noexcept noexcept(noexcept(compareThreeWay(rhs, lhs))) \ { \ const auto r = compareThreeWay(rhs, lhs); \ - if (r > 0) return std::weak_ordering::less; \ - if (r < 0) return std::weak_ordering::greater; \ + if (is_gt(r)) return std::weak_ordering::less; \ + if (is_lt(r)) return std::weak_ordering::greater; \ return r; \ } @@ -169,8 +170,8 @@ template <typename In> constexpr auto to_Qt(In in) noexcept noexcept(noexcept(compareThreeWay(rhs, lhs))) \ { \ const auto r = compareThreeWay(rhs, lhs); \ - if (r > 0) return std::partial_ordering::less; \ - if (r < 0) return std::partial_ordering::greater; \ + if (is_gt(r)) return std::partial_ordering::less; \ + if (is_lt(r)) return std::partial_ordering::greater; \ return r; \ } @@ -218,19 +219,19 @@ template <typename In> constexpr auto to_Qt(In in) noexcept Attributes \ friend Constexpr bool operator<(LeftType const &lhs, RightType const &rhs) \ noexcept(noexcept(compareThreeWay(lhs, rhs))) \ - { return compareThreeWay(lhs, rhs) < 0; } \ + { return is_lt(compareThreeWay(lhs, rhs)); } \ Attributes \ friend Constexpr bool operator>(LeftType const &lhs, RightType const &rhs) \ noexcept(noexcept(compareThreeWay(lhs, rhs))) \ - { return compareThreeWay(lhs, rhs) > 0; } \ + { return is_gt(compareThreeWay(lhs, rhs)); } \ Attributes \ friend Constexpr bool operator<=(LeftType const &lhs, RightType const &rhs) \ noexcept(noexcept(compareThreeWay(lhs, rhs))) \ - { return compareThreeWay(lhs, rhs) <= 0; } \ + { return is_lteq(compareThreeWay(lhs, rhs)); } \ Attributes \ friend Constexpr bool operator>=(LeftType const &lhs, RightType const &rhs) \ noexcept(noexcept(compareThreeWay(lhs, rhs))) \ - { return compareThreeWay(lhs, rhs) >= 0; } + { return is_gteq(compareThreeWay(lhs, rhs)); } #define QT_DECLARE_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr, Attributes) \ QT_DECLARE_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, Constexpr, \ @@ -255,19 +256,19 @@ template <typename In> constexpr auto to_Qt(In in) noexcept Attributes \ friend Constexpr bool operator<(RightType const &lhs, LeftType const &rhs) \ noexcept(noexcept(compareThreeWay(rhs, lhs))) \ - { return compareThreeWay(rhs, lhs) > 0; } \ + { return is_gt(compareThreeWay(rhs, lhs)); } \ Attributes \ friend Constexpr bool operator>(RightType const &lhs, LeftType const &rhs) \ noexcept(noexcept(compareThreeWay(rhs, lhs))) \ - { return compareThreeWay(rhs, lhs) < 0; } \ + { return is_lt(compareThreeWay(rhs, lhs)); } \ Attributes \ friend Constexpr bool operator<=(RightType const &lhs, LeftType const &rhs) \ noexcept(noexcept(compareThreeWay(rhs, lhs))) \ - { return compareThreeWay(rhs, lhs) >= 0; } \ + { return is_gteq(compareThreeWay(rhs, lhs)); } \ Attributes \ friend Constexpr bool operator>=(RightType const &lhs, LeftType const &rhs) \ noexcept(noexcept(compareThreeWay(rhs, lhs))) \ - { return compareThreeWay(rhs, lhs) <= 0; } + { return is_lteq(compareThreeWay(rhs, lhs)); } #define QT_DECLARE_REVERSED_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr, Attributes) \ QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, \ @@ -450,6 +451,26 @@ constexpr bool IsFloatType_v<QtPrivate::NativeFloat16Type> = true; } // namespace QtPrivate +namespace QtOrderingPrivate { + +template <typename T, typename U> +constexpr Qt::strong_ordering +strongOrderingCompareDefaultImpl(T lhs, U rhs) noexcept +{ +#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 +} + +} // namespace QtOrderingPrivate + namespace Qt { template <typename T> @@ -526,8 +547,11 @@ constexpr Qt::partial_ordering compareThreeWay(FloatType lhs, IntType rhs) noexc return compareThreeWay(lhs, FloatType(rhs)); } +#if QT_DEPRECATED_SINCE(6, 8) + template <typename LeftType, typename RightType, if_compatible_pointers<LeftType, RightType> = true> +QT_DEPRECATED_VERSION_X_6_8("Wrap the pointers into Qt::totally_ordered_wrapper and use the respective overload instead.") constexpr Qt::strong_ordering compareThreeWay(const LeftType *lhs, const RightType *rhs) noexcept { #ifdef __cpp_lib_three_way_comparison @@ -543,25 +567,191 @@ constexpr Qt::strong_ordering compareThreeWay(const LeftType *lhs, const RightTy } template <typename T> +QT_DEPRECATED_VERSION_X_6_8("Wrap the pointer into Qt::totally_ordered_wrapper and use the respective overload instead.") constexpr Qt::strong_ordering compareThreeWay(const T *lhs, std::nullptr_t rhs) noexcept { return compareThreeWay(lhs, static_cast<const T *>(rhs)); } template <typename T> +QT_DEPRECATED_VERSION_X_6_8("Wrap the pointer into Qt::totally_ordered_wrapper and use the respective overload instead.") constexpr Qt::strong_ordering compareThreeWay(std::nullptr_t lhs, const T *rhs) noexcept { return compareThreeWay(static_cast<const T *>(lhs), rhs); } +#endif // QT_DEPRECATED_SINCE(6, 8) + 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 +namespace QtOrderingPrivate { + +template <typename Head, typename...Tail, std::size_t...Is> +constexpr std::tuple<Tail...> qt_tuple_pop_front_impl(const std::tuple<Head, Tail...> &t, + std::index_sequence<Is...>) noexcept +{ + return std::tuple<Tail...>(std::get<Is + 1>(t)...); +} + +template <typename Head, typename...Tail> +constexpr std::tuple<Tail...> qt_tuple_pop_front(const std::tuple<Head, Tail...> &t) noexcept +{ + return qt_tuple_pop_front_impl(t, std::index_sequence_for<Tail...>{}); +} + +template <typename LhsHead, typename...LhsTail, typename RhsHead, typename...RhsTail> +constexpr auto compareThreeWayMulti(const std::tuple<LhsHead, LhsTail...> &lhs, // ie. not empty + const std::tuple<RhsHead, RhsTail...> &rhs) noexcept +{ + static_assert(sizeof...(LhsTail) == sizeof...(RhsTail), + // expanded together below, but provide a nicer error message: + "The tuple arguments have to have the same size."); + + using Qt::compareThreeWay; + using R = std::common_type_t< + decltype(compareThreeWay(std::declval<LhsHead>(), std::declval<RhsHead>())), + decltype(compareThreeWay(std::declval<LhsTail>(), std::declval<RhsTail>()))... + >; + + const auto &l = std::get<0>(lhs); + const auto &r = std::get<0>(rhs); + static_assert(noexcept(compareThreeWay(l, r)), + "This function requires all relational operators to be noexcept."); + const auto res = compareThreeWay(l, r); + if constexpr (sizeof...(LhsTail) > 0) { + if (is_eq(res)) + return R{compareThreeWayMulti(qt_tuple_pop_front(lhs), qt_tuple_pop_front(rhs))}; + } + return R{res}; +} + +} //QtOrderingPrivate + +namespace Qt { +// A wrapper class that adapts the wrappee to use the strongly-ordered +// <functional> function objects for implementing the relational operators. +// Mostly useful to avoid UB on pointers (which it currently mandates P to be), +// because all the comparison helpers (incl. std::compare_three_way on +// std::tuple<T*>!) will use the language-level operators. +// +template <typename P> +class totally_ordered_wrapper +{ + static_assert(std::is_pointer_v<P>); + using T = std::remove_pointer_t<P>; + + P ptr; +public: + totally_ordered_wrapper() noexcept = default; + explicit constexpr totally_ordered_wrapper(P p) noexcept : ptr(p) {} + + constexpr P get() const noexcept { return ptr; } + constexpr void reset(P p) noexcept { ptr = p; } + constexpr P operator->() const noexcept { return get(); } + constexpr T& operator*() const noexcept { return *get(); } + + explicit constexpr operator bool() const noexcept { return get(); } + +private: + // TODO: Replace the constraints with std::common_type_t<P, U> when + // a bug in VxWorks is fixed! + template <typename T, typename U> + using if_compatible_types = + std::enable_if_t<std::conjunction_v<std::is_pointer<T>, + std::is_pointer<U>, + std::disjunction<std::is_convertible<T, U>, + std::is_convertible<U, T>>>, + bool>; + +#define MAKE_RELOP(Ret, op, Op) \ + template <typename U = P, if_compatible_types<P, U> = true> \ + friend constexpr Ret operator op (const totally_ordered_wrapper<P> &lhs, const totally_ordered_wrapper<U> &rhs) noexcept \ + { return std:: Op {}(lhs.ptr, rhs.get()); } \ + template <typename U = P, if_compatible_types<P, U> = true> \ + friend constexpr Ret operator op (const totally_ordered_wrapper<P> &lhs, const U &rhs) noexcept \ + { return std:: Op {}(lhs.ptr, rhs ); } \ + template <typename U = P, if_compatible_types<P, U> = true> \ + friend constexpr Ret operator op (const U &lhs, const totally_ordered_wrapper<P> &rhs) noexcept \ + { return std:: Op {}(lhs, rhs.ptr); } \ + friend constexpr Ret operator op (const totally_ordered_wrapper &lhs, std::nullptr_t) noexcept \ + { return std:: Op {}(lhs.ptr, P(nullptr)); } \ + friend constexpr Ret operator op (std::nullptr_t, const totally_ordered_wrapper &rhs) noexcept \ + { return std:: Op {}(P(nullptr), rhs.ptr); } \ + /* end */ + MAKE_RELOP(bool, ==, equal_to<>) + MAKE_RELOP(bool, !=, not_equal_to<>) + MAKE_RELOP(bool, < , less<>) + MAKE_RELOP(bool, <=, less_equal<>) + MAKE_RELOP(bool, > , greater<>) + MAKE_RELOP(bool, >=, greater_equal<>) +#ifdef __cpp_lib_three_way_comparison + MAKE_RELOP(auto, <=>, compare_three_way) +#endif +#undef MAKE_RELOP + friend void qt_ptr_swap(totally_ordered_wrapper &lhs, totally_ordered_wrapper &rhs) noexcept + { qt_ptr_swap(lhs.ptr, rhs.ptr); } + friend void swap(totally_ordered_wrapper &lhs, totally_ordered_wrapper &rhs) noexcept + { qt_ptr_swap(lhs, rhs); } + friend size_t qHash(totally_ordered_wrapper key, size_t seed = 0) noexcept + { return qHash(key.ptr, seed); } +}; + +template <typename T, typename U, if_compatible_pointers<T, U> = true> +constexpr Qt::strong_ordering +compareThreeWay(Qt::totally_ordered_wrapper<T*> lhs, Qt::totally_ordered_wrapper<U*> rhs) noexcept +{ + return QtOrderingPrivate::strongOrderingCompareDefaultImpl(lhs, rhs); +} + +template <typename T, typename U, if_compatible_pointers<T, U> = true> +constexpr Qt::strong_ordering +compareThreeWay(Qt::totally_ordered_wrapper<T*> lhs, U *rhs) noexcept +{ + return QtOrderingPrivate::strongOrderingCompareDefaultImpl(lhs, rhs); +} + +template <typename T, typename U, if_compatible_pointers<T, U> = true> +constexpr Qt::strong_ordering +compareThreeWay(U *lhs, Qt::totally_ordered_wrapper<T*> rhs) noexcept +{ + return QtOrderingPrivate::strongOrderingCompareDefaultImpl(lhs, rhs); +} + +template <typename T> +constexpr Qt::strong_ordering +compareThreeWay(Qt::totally_ordered_wrapper<T*> lhs, std::nullptr_t rhs) noexcept +{ + return QtOrderingPrivate::strongOrderingCompareDefaultImpl(lhs, rhs); +} + +template <typename T> +constexpr Qt::strong_ordering +compareThreeWay(std::nullptr_t lhs, Qt::totally_ordered_wrapper<T*> rhs) noexcept +{ + return QtOrderingPrivate::strongOrderingCompareDefaultImpl(lhs, rhs); +} + +} //Qt + +template <typename P> +class QTypeInfo<Qt::totally_ordered_wrapper<P>> : public QTypeInfo<P> {}; + QT_END_NAMESPACE +namespace std { + template <typename P> + struct hash<QT_PREPEND_NAMESPACE(Qt::totally_ordered_wrapper)<P>> + { + using argument_type = QT_PREPEND_NAMESPACE(Qt::totally_ordered_wrapper)<P>; + using result_type = size_t; + constexpr result_type operator()(argument_type w) const noexcept + { return std::hash<P>{}(w.get()); } + }; +} + #endif // QCOMPAREHELPERS_H |