Long live Q_DECLARE_ORDERED

This is a version of the comparison helper macro for the cases where
the ordering strength depends on the template arguments (think
std::pair<double, int> vs std::pair<int, int>).

This patch also adds the _LITERAL_TYPE and _NON_NOEXCEPT overloads of
the macros that generate the constexpr and noexpect(false) relational
operators respectively.

Done-with: Ivan Solovev <ivan.solovev@qt.io>
Task-number: QTBUG-127095
Change-Id: I368cf2ff385213be7d2c7477a8346e83ce841b91
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2024-07-29 18:41:39 +02:00 committed by Ivan Solovev
parent a8ca9c418f
commit 5d8f2b7cd2
4 changed files with 248 additions and 0 deletions

View File

@ -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 <QtCompare>
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 <typename LeftInt, typename RightInt, Qt::if_integral<LeftInt> = true, Qt::if_integral<RightInt> = true> auto Qt::compareThreeWay(LeftInt lhs, RightInt rhs)
\since 6.7

View File

@ -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 */, \

View File

@ -64,6 +64,8 @@ private Q_SLOTS:
void compareWithAttributes();
void totallyOrderedWrapperBasics();
void compareAutoReturnType();
};
#endif // TST_QCOMPAREHELPERS_H

View File

@ -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 <typename T>
@ -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 <typename T>
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<int>;
static_assert(std::is_same_v<decltype(compareThreeWay(std::declval<const StrongT &>(),
std::declval<const StrongT &>())),
Qt::strong_ordering>);
QTestPrivate::testAllComparisonOperatorsCompile<StrongT>();
}
// partial
{
using PartialT = AutoComparisonTester<float>;
static_assert(std::is_same_v<decltype(compareThreeWay(std::declval<const PartialT &>(),
std::declval<const PartialT &>())),
Qt::partial_ordering>);
QTestPrivate::testAllComparisonOperatorsCompile<PartialT>();
}
// weak
{
using WeakT = AutoComparisonTester<QDateTime>;
static_assert(std::is_same_v<decltype(compareThreeWay(std::declval<const WeakT &>(),
std::declval<const WeakT &>())),
Qt::weak_ordering>);
QTestPrivate::testAllComparisonOperatorsCompile<WeakT>();
}
}