diff --git a/src/corelib/global/qcompare.cpp b/src/corelib/global/qcompare.cpp index 43d0d163f7f..f80a08fa1f1 100644 --- a/src/corelib/global/qcompare.cpp +++ b/src/corelib/global/qcompare.cpp @@ -1225,6 +1225,60 @@ CHECK(strong, equivalent); Starting from Qt 6.9, \c Attributes becomes a variable argument. */ +/*! + \internal + \macro Q_DECLARE_ORDERED(Type) + \macro Q_DECLARE_ORDERED(LeftType, RightType) + \macro Q_DECLARE_ORDERED(LeftType, RightType, Attributes...) + \macro Q_DECLARE_ORDERED_LITERAL_TYPE(Type) + \macro Q_DECLARE_ORDERED_LITERAL_TYPE(LeftType, RightType) + \macro Q_DECLARE_ORDERED_LITERAL_TYPE(LeftType, RightType, Attributes...) + \macro Q_DECLARE_ORDERED_NON_NOEXCEPT(Type) + \macro Q_DECLARE_ORDERED_NON_NOEXCEPT(LeftType, RightType) + \macro Q_DECLARE_ORDERED_NON_NOEXCEPT(LeftType, RightType, Attributes...) + \since 6.9 + \relates + + These macros behave similarly to the + \c {Q_DECLARE_(PARTIALLY,WEAKLY,STRONGLY)_ORDERED} overloads, but represent + any one of those three, using \c auto return type. + + This is what you typically would use for template classes where + the strength of the ordering depends on the template arguments. + For example, if one of the template arguments is a floating-point + type, the ordering would be \l {Qt::partial_ordering}, if they all + are integral - \l {Qt::strong_ordering}. + + \note It is better to use one of the explicit-strength macros in general, to + communicate intent. Use these macros only when the stength actually does vary + with template arguments. + + The (in)equality operators are implemented in terms of a helper function + \c {comparesEqual()}. The other relational operators are implemented in + terms of a helper function \c {compareThreeWay()}. + The \c {compareThreeWay()} function \e must return an object of an ordering + type. It's the user's responsibility to declare and define both helper + functions. + + The \c {*_LITERAL_TYPE} overloads are used to generate \c constexpr + operators. This means that the helper \c {comparesEqual()} and + \c {compareThreeWay()} functions must also be \c constexpr. + + See \l {Q_DECLARE_PARTIALLY_ORDERED} for usage examples. + + By default, the generated operators are \c {noexcept}. + Use the \c {*_NON_NOEXCEPT} overloads if the relational operators cannot be + \c {noexcept}. + + The three-argument versions of the macros allow specification of C++ + attributes to add before every generated relational operator. + See \l {Q_DECLARE_EQUALITY_COMPARABLE(LeftType, RightType, Attributes...)} + for more details and usage examples. + + \sa Q_DECLARE_PARTIALLY_ORDERED, Q_DECLARE_WEAKLY_ORDERED, + Q_DECLARE_STRONGLY_ORDERED, Q_DECLARE_EQUALITY_COMPARABLE +*/ + /*! \fn template = true, Qt::if_integral = true> auto Qt::compareThreeWay(LeftInt lhs, RightInt rhs) \since 6.7 diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h index 2874f2eefd3..3c5d1e0ef22 100644 --- a/src/corelib/global/qcomparehelpers.h +++ b/src/corelib/global/qcomparehelpers.h @@ -208,6 +208,15 @@ orderingFlagsFor(T t) noexcept return compareThreeWay(lhs, rhs); \ } +#define QT_DECLARE_3WAY_HELPER_AUTO(LeftType, RightType, Constexpr, Noexcept, ...) \ + __VA_ARGS__ \ + friend Constexpr auto \ + operator<=>(LeftType const &lhs, RightType const &rhs) Noexcept \ + { \ + QT_COMPARISON_NOEXCEPT_CHECK(Noexcept, compareThreeWay);\ + return QtOrderingPrivate::to_std(compareThreeWay(lhs, rhs)); \ + } + #define QT_DECLARE_ORDERING_OPERATORS_HELPER(OrderingType, LeftType, RightType, Constexpr, \ Noexcept, ...) \ QT_DECLARE_EQUALITY_OPERATORS_HELPER(LeftType, RightType, Constexpr, Noexcept, __VA_ARGS__) \ @@ -253,6 +262,15 @@ orderingFlagsFor(T t) noexcept return QtOrderingPrivate::reversed(r); \ } +#define QT_DECLARE_REVERSED_3WAY_HELPER_AUTO(LeftType, RightType, Constexpr, Noexcept, ...) \ + __VA_ARGS__ \ + friend Constexpr auto \ + operator<=>(RightType const &lhs, LeftType const &rhs) Noexcept \ + { \ + const auto r = compareThreeWay(rhs, lhs); \ + return QtOrderingPrivate::to_std(QtOrderingPrivate::reversed(r)); \ + } + #define QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(OrderingString, LeftType, RightType, \ Constexpr, Noexcept, ...) \ QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, Constexpr, \ @@ -314,6 +332,10 @@ orderingFlagsFor(T t) noexcept friend Constexpr bool operator>=(LeftType const &lhs, RightType const &rhs) Noexcept \ { return is_gteq(compareThreeWay(lhs, rhs)); } +#define QT_DECLARE_ORDERING_HELPER_AUTO(LeftType, RightType, Constexpr, Noexcept, ...) \ + QT_DECLARE_ORDERING_HELPER_TEMPLATE(auto, LeftType, RightType, Constexpr, Noexcept, \ + __VA_ARGS__) + #define QT_DECLARE_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr, Noexcept, ...) \ QT_DECLARE_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, Constexpr, \ Noexcept, __VA_ARGS__) @@ -348,6 +370,10 @@ orderingFlagsFor(T t) noexcept friend Constexpr bool operator>=(RightType const &lhs, LeftType const &rhs) Noexcept \ { return is_lteq(compareThreeWay(rhs, lhs)); } +#define QT_DECLARE_REVERSED_ORDERING_HELPER_AUTO(LeftType, RightType, Constexpr, Noexcept, ...) \ + QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(auto, LeftType, RightType, Constexpr, Noexcept, \ + __VA_ARGS__) + #define QT_DECLARE_REVERSED_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr, Noexcept, ...) \ QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, \ Constexpr, Noexcept, __VA_ARGS__) @@ -468,6 +494,96 @@ orderingFlagsFor(T t) noexcept #define Q_DECLARE_EQUALITY_COMPARABLE_NON_NOEXCEPT(...) \ QT_OVERLOADED_MACRO(QT_DECLARE_EQUALITY_COMPARABLE_NON_NOEXCEPT, __VA_ARGS__) +// Ordering operators that automatically deduce the strength: +#define QT_DECLARE_ORDERED_1(Type) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, Type, Type, /* non-constexpr */, noexcept(true), \ + /* no attributes */) + +#define QT_DECLARE_ORDERED_2(LeftType, RightType) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(true), /* no attributes */) \ + QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(true), /* no attributes */) + +#define QT_DECLARE_ORDERED_3(LeftType, RightType, ...) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(true), __VA_ARGS__) \ + QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(true), __VA_ARGS__) + +#define QT_DECLARE_ORDERED_4(...) QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_5(...) QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_6(...) QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_7(...) QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_8(...) QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_9(...) QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_3(__VA_ARGS__)) + +#define Q_DECLARE_ORDERED(...) QT_OVERLOADED_MACRO(QT_DECLARE_ORDERED, __VA_ARGS__) + +#define QT_DECLARE_ORDERED_LITERAL_TYPE_1(Type) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, Type, Type, constexpr, noexcept(true), \ + /* no attributes */) + +#define QT_DECLARE_ORDERED_LITERAL_TYPE_2(LeftType, RightType) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, LeftType, RightType, constexpr, \ + noexcept(true), /* no attributes */) \ + QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(AUTO, LeftType, RightType, constexpr, \ + noexcept(true), /* no attributes */) + +#define QT_DECLARE_ORDERED_LITERAL_TYPE_3(LeftType, RightType, ...) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, LeftType, RightType, constexpr, \ + noexcept(true), __VA_ARGS__) \ + QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(AUTO, LeftType, RightType, constexpr, \ + noexcept(true), __VA_ARGS__) + +#define QT_DECLARE_ORDERED_LITERAL_TYPE_4(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_LITERAL_TYPE_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_LITERAL_TYPE_5(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_LITERAL_TYPE_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_LITERAL_TYPE_6(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_LITERAL_TYPE_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_LITERAL_TYPE_7(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_LITERAL_TYPE_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_LITERAL_TYPE_8(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_LITERAL_TYPE_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_LITERAL_TYPE_9(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_LITERAL_TYPE_3(__VA_ARGS__)) + +#define Q_DECLARE_ORDERED_LITERAL_TYPE(...) \ + QT_OVERLOADED_MACRO(QT_DECLARE_ORDERED_LITERAL_TYPE, __VA_ARGS__) + +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_1(Type) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, Type, Type, /* non-constexpr */, noexcept(false), \ + /* no attributes */) + +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_2(LeftType, RightType) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(false), /* no attributes */) \ + QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(false), /* no attributes */) + +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_3(LeftType, RightType, ...) \ + QT_DECLARE_ORDERING_OPERATORS_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(false), __VA_ARGS__) \ + QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(AUTO, LeftType, RightType, /* non-constexpr */, \ + noexcept(false), __VA_ARGS__) + +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_4(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_NON_NOEXCEPT_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_5(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_NON_NOEXCEPT_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_6(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_NON_NOEXCEPT_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_7(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_NON_NOEXCEPT_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_8(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_NON_NOEXCEPT_3(__VA_ARGS__)) +#define QT_DECLARE_ORDERED_NON_NOEXCEPT_9(...) \ + QT_VA_ARGS_EXPAND(QT_DECLARE_ORDERED_NON_NOEXCEPT_3(__VA_ARGS__)) + +#define Q_DECLARE_ORDERED_NON_NOEXCEPT(...) \ + QT_OVERLOADED_MACRO(QT_DECLARE_ORDERED_NON_NOEXCEPT, __VA_ARGS__) + // Partial ordering operators #define QT_DECLARE_PARTIALLY_ORDERED_1(Type) \ QT_DECLARE_ORDERING_OPERATORS_HELPER(PARTIAL, Type, Type, /* non-constexpr */, \ diff --git a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.h b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.h index 89d8a269475..aac786ba43d 100644 --- a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.h +++ b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.h @@ -64,6 +64,8 @@ private Q_SLOTS: void compareWithAttributes(); void totallyOrderedWrapperBasics(); + + void compareAutoReturnType(); }; #endif // TST_QCOMPAREHELPERS_H diff --git a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp index e0226e1bfb8..118b61df482 100644 --- a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp +++ b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers1.cpp @@ -22,6 +22,28 @@ private: \ }; \ /* END */ +#define DECLARE_AUTO_TYPE(Name, Type, Constexpr, Noex, Suffix, ...) \ +class TemplatedAuto ## Name \ +{ \ +public: \ + Constexpr TemplatedAuto ## Name () {} \ + Constexpr TemplatedAuto ## Name (Type v) : val(v) {} \ +\ +private: \ + __VA_ARGS__ \ + friend Constexpr bool \ + comparesEqual(const TemplatedAuto ## Name &lhs, X rhs) noexcept(Noex) \ + { return lhs.val == rhs; } \ + __VA_ARGS__ \ + friend Constexpr auto \ + compareThreeWay(const TemplatedAuto ## Name &lhs, X rhs) noexcept(Noex) \ + { using Qt::compareThreeWay; return compareThreeWay(lhs.val, rhs); } \ + Q_DECLARE_ORDERED ## Suffix (TemplatedAuto ## Name, X, __VA_ARGS__) \ +\ + Type val = {}; \ +}; \ +/* END */ + #define DECLARE_TYPES_FOR_N_ATTRS(N, ...) \ DECLARE_TYPE(PartialConst ## N, PARTIALLY, Qt::partial_ordering, constexpr, \ true, _LITERAL_TYPE, __VA_ARGS__) \ @@ -38,6 +60,9 @@ DECLARE_TYPE(StrongConst ## N, STRONGLY, Qt::strong_ordering, constexpr, true, \ DECLARE_TYPE(Strong ## N, STRONGLY, Qt::strong_ordering, , true, , __VA_ARGS__) \ DECLARE_TYPE(StrongNonNoex ## N, STRONGLY, Qt::strong_ordering, , false, \ _NON_NOEXCEPT, __VA_ARGS__) \ +DECLARE_AUTO_TYPE(Def ## N, int, , true, , __VA_ARGS__) \ +DECLARE_AUTO_TYPE(Const ## N, int, constexpr, true, _LITERAL_TYPE, __VA_ARGS__) \ +DECLARE_AUTO_TYPE(NonNoex ## N, int, , false, _NON_NOEXCEPT, __VA_ARGS__) \ /* END */ template @@ -91,6 +116,9 @@ void tst_QCompareHelpers::compareWithAttributes() COMPARE(TemplatedStrongConst ## N); \ COMPARE(TemplatedStrong ## N); \ COMPARE(TemplatedStrongNonNoex ## N); \ + COMPARE(TemplatedAutoDef ## N); \ + COMPARE(TemplatedAutoConst ## N); \ + COMPARE(TemplatedAutoNonNoex ## N); \ /* END */ COMPARE_SET(1) @@ -122,3 +150,51 @@ void tst_QCompareHelpers::totallyOrderedWrapperBasics() QCOMPARE_EQ(*intWrp, 20); QCOMPARE_EQ(val, 20); } + +template +class AutoComparisonTester +{ +public: + AutoComparisonTester(const T &v) : val(v) {} + +private: + friend bool + comparesEqual(const AutoComparisonTester &lhs, const AutoComparisonTester &rhs) noexcept + { return lhs.val == rhs.val; } + + friend auto + compareThreeWay(const AutoComparisonTester &lhs, const AutoComparisonTester &rhs) noexcept + { using Qt::compareThreeWay; return compareThreeWay(lhs.val, rhs.val); } + + Q_DECLARE_ORDERED(AutoComparisonTester) + + T val; +}; + +void tst_QCompareHelpers::compareAutoReturnType() +{ + // strong + { + using StrongT = AutoComparisonTester; + static_assert(std::is_same_v(), + std::declval())), + Qt::strong_ordering>); + QTestPrivate::testAllComparisonOperatorsCompile(); + } + // partial + { + using PartialT = AutoComparisonTester; + static_assert(std::is_same_v(), + std::declval())), + Qt::partial_ordering>); + QTestPrivate::testAllComparisonOperatorsCompile(); + } + // weak + { + using WeakT = AutoComparisonTester; + static_assert(std::is_same_v(), + std::declval())), + Qt::weak_ordering>); + QTestPrivate::testAllComparisonOperatorsCompile(); + } +}