Implement compare helper macros

These macros should unwrap into a proper set of equality and ordering
operators, depending on the C++ standard being used.
For C++17, all 6 operators (==, !=, <, >, <=, >=) are overloaded, while
for C++20 only the overloads for opeartor==() and operator<=>() are
provided.

The macros are documented as internal for now.

The macros rely on two helper functions:
  bool comparesEqual(LeftType lhs, RightType rhs);
  ReturnType compareThreeWay(LeftType lhs, RightType rhs);

The comparesEqual() helper function is used to implement operator==()
and operator!=().
The compareThreeWay() helper function is used to implement the four
relational operators in C++17, or operator<=>() in C++20.
ReturnType must be one of Qt::{partial,weak,strong}_ordering.
When possible, the functions should also be declared constexpr and
noexcept.
It's the user's responsibility to provide the functions before
using the macros.

Implement a test case which applies the new macros to the dummy
classes, and uses the new helper function to verify the comparison
results.

The MSVC compiler before version 19.36 has a bug, where it fails
to correctly generate reverse opeerators in C++20 mode. Introduce
a new Q_COMPILER_LACKS_THREE_WAY_COMPARE_SYMMETRY definition for such
compiler versions, and use it to manually generate reversed
operators when needed.

Task-number: QTBUG-104113
Change-Id: Idc19d55df011fd616ff654f35a964e831b8ab93b
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Ivan Solovev 2023-05-02 12:33:26 +02:00 committed by Edward Welbourne
parent e616f8decb
commit fe12650e9d
9 changed files with 1170 additions and 4 deletions

View File

@ -50,6 +50,7 @@ qt_internal_add_module(Core
global/qassert.cpp global/qassert.h
global/qcompare_impl.h
global/qcompare.cpp global/qcompare.h
global/qcomparehelpers.h
global/qcompilerdetection.h
global/qconstructormacros.h
global/qcontainerinfo.h

View File

@ -122,6 +122,20 @@ CHECK(strong, equivalent);
implementing three-way comparison with C++17.
*/
/*!
\headerfile <QtCompare>
\inmodule QtCore
\title Classes and helpers for defining comparison operators
\keyword qtcompare
\brief The <QtCompare> header file defines \c {Qt::*_ordering} types and helper
macros for defining comparison operators.
This header introduces the \l Qt::partial_ordering, \l Qt::weak_ordering, and
\l Qt::strong_ordering types, which are Qt's C++17 backports of
\c {std::*_ordering} types.
*/
/*!
\class Qt::strong_ordering
\inmodule QtCore
@ -762,4 +776,326 @@ CHECK(strong, equivalent);
with respect to the right operand.
*/
/*!
\internal
\macro Q_DECLARE_EQUALITY_COMPARABLE(Type)
\macro Q_DECLARE_EQUALITY_COMPARABLE(LeftType, RightType)
\macro Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(Type)
\macro Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(LeftType, RightType)
\since 6.7
\relates <QtCompare>
These macros are used to generate \c {operator==()} and \c {operator!=()}.
In C++17 mode, the mixed-type overloads also generate the reversed
operators.
In C++20 mode, only \c {operator==()} is defined. \c {operator!=()},
as well as the reversed operators for mixed-type comparison, are synthesized
by the compiler.
The operators are implemented in terms of a helper function
\c {comparesEqual()}.
It's the user's responsibility to declare and define this function.
Consider the following example of a comparison operators declaration:
\code
class MyClass {
...
private:
friend bool comparesEqual(const MyClass &, const MyClass &) noexcept;
Q_DECLARE_EQUALITY_COMPARABLE(MyClass)
};
\endcode
When compiled with C++17, the macro will expand into the following code:
\code
friend bool operator==(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
friend bool operator!=(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
\endcode
When compiled with C++20, the macro will expand only into \c {operator==()}:
\code
friend bool operator==(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
\endcode
The \c {*_LITERAL_TYPE} versions of the macros are used to generate
\c constexpr operators. This means that the helper \c {comparesEqual()}
function must also be \c constexpr.
Consider the following example of a mixed-type \c constexpr comparison
operators declaration:
\code
class MyClass {
...
private:
friend constexpr bool comparesEqual(const MyClass &, int) noexcept;
Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(MyClass, int)
};
\endcode
When compiled with C++17, the macro will expand into the following code:
\code
friend constexpr bool operator==(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
friend constexpr bool operator!=(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
friend constexpr bool operator==(int lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
friend constexpr bool operator!=(int lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
\endcode
When compiled with C++20, the macro expands only into \c {operator==()}:
\code
friend constexpr bool operator==(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
\endcode
*/
/*!
\internal
\macro Q_DECLARE_PARTIALLY_ORDERED(Type)
\macro Q_DECLARE_PARTIALLY_ORDERED(LeftType, RightType)
\macro Q_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE(Type)
\macro Q_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE(LeftType, RightType)
\since 6.7
\relates <QtCompare>
These macros are used to generate all six relational operators.
The operators represent
\l {https://en.cppreference.com/w/cpp/utility/compare/partial_ordering}
{partial ordering}.
These macros use respective overloads of the
\l {Q_DECLARE_EQUALITY_COMPARABLE} macro to generate \c {operator==()} and
\c {operator!=()}, and also generate the four relational operators:
\c {operator<()}, \c {operator>()}, \c {operator<=()}, and \c {operator>()}.
In C++17 mode, the mixed-type overloads also generate the reversed
operators.
In C++20 mode, only \c {operator==()} and \c {operator<=>()} are defined.
Other operators, as well as the reversed operators for mixed-type
comparison, are synthesized by the compiler.
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 type
\l Qt::partial_ordering. It's the user's responsibility to declare and define
both helper functions.
Consider the following example of a comparison operators declaration:
\code
class MyClass {
...
private:
friend bool comparesEqual(const MyClass &, const MyClass &) noexcept;
friend Qt::partial_ordering compareThreeWay(const MyClass &, const MyClass &) noexcept;
Q_DECLARE_PARTIALLY_ORDERED(MyClass)
};
\endcode
When compiled with C++17, the macro will expand into the following code:
\code
// operator==() and operator!=() are generated from
// Q_DECLARE_EQUALITY_COMPARABLE
friend bool operator<(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend bool operator>(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend bool operator<=(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend bool operator>=(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
\endcode
When compiled with C++20, the macro will expand into \c {operator==()} and
\c {operator<=>()}:
\code
friend bool operator==(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
friend std::partial_ordering
operator<=>(const MyClass &lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
\endcode
The \c {*_LITERAL_TYPE} versions of the macros are used to generate
\c constexpr operators. This means that the helper \c {comparesEqual()} and
\c {compareThreeWay()} functions must also be \c constexpr.
Consider the following example of a mixed-type \c constexpr comparison
operators declaration:
\code
class MyClass {
...
private:
friend constexpr bool comparesEqual(const MyClass &, int) noexcept;
friend constexpr Qt::partial_ordering compareThreeWay(const MyClass &, int) noexcept;
Q_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE(MyClass, int)
};
\endcode
When compiled with C++17, the macro will expand into the following code:
\code
// operator==(), operator!=(), and their reversed versions are generated
// from Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE
friend constexpr bool operator<(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend constexpr bool operator>(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend constexpr bool operator<=(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend constexpr bool operator>=(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend constexpr bool operator<(int lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend constexpr bool operator>(int lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend constexpr bool operator<=(int lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
friend constexpr bool operator>=(int lhs, const MyClass &rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
\endcode
When compiled with C++20, the macro will expand into \c {operator==()} and
\c {operator<=>()}:
\code
friend constexpr bool operator==(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses comparesEqual()
}
friend constexpr std::partial_ordering
operator<=>(const MyClass &lhs, int rhs) noexcept
{
// inline implementation which uses compareThreeWay()
}
\endcode
\sa Q_DECLARE_EQUALITY_COMPARABLE, Q_DECLARE_WEAKLY_ORDERED,
Q_DECLARE_STRONGLY_ORDERED
*/
/*!
\internal
\macro Q_DECLARE_WEAKLY_ORDERED(Type)
\macro Q_DECLARE_WEAKLY_ORDERED(LeftType, RightType)
\macro Q_DECLARE_WEAKLY_ORDERED_LITERAL_TYPE(Type)
\macro Q_DECLARE_WEAKLY_ORDERED_LITERAL_TYPE(LeftType, RightType)
\since 6.7
\relates <QtCompare>
These macros behave similarly to the
\l {Q_DECLARE_PARTIALLY_ORDERED} overloads, but represent
\l {https://en.cppreference.com/w/cpp/utility/compare/weak_ordering}
{weak ordering}.
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 type
\l Qt::weak_ordering. 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.
\sa Q_DECLARE_PARTIALLY_ORDERED, Q_DECLARE_STRONGLY_ORDERED,
Q_DECLARE_EQUALITY_COMPARABLE
*/
/*!
\internal
\macro Q_DECLARE_STRONGLY_ORDERED(Type)
\macro Q_DECLARE_STRONGLY_ORDERED(LeftType, RightType)
\macro Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(Type)
\macro Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(LeftType, RightType)
\since 6.7
\relates <QtCompare>
These macros behave similarly to the
\l {Q_DECLARE_PARTIALLY_ORDERED} overloads, but represent
\l {https://en.cppreference.com/w/cpp/utility/compare/strong_ordering}
{strong ordering}.
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 type
\l Qt::strong_ordering. 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.
\sa Q_DECLARE_PARTIALLY_ORDERED, Q_DECLARE_WEAKLY_ORDERED,
Q_DECLARE_EQUALITY_COMPARABLE
*/
QT_END_NAMESPACE

View File

@ -684,4 +684,7 @@ inline constexpr strong_ordering strong_ordering::greater(QtPrivate::Ordering::G
QT_END_NAMESPACE
// This is intentionally included in the end of qcompare.h
#include <QtCore/qcomparehelpers.h>
#endif // QCOMPARE_H

View File

@ -0,0 +1,307 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCOMPARE_H
#error "Do not include qcomparehelpers.h directly. Use qcompare.h instead."
#endif
#ifndef QCOMPAREHELPERS_H
#define QCOMPAREHELPERS_H
#if 0
#pragma qt_no_master_include
#pragma qt_sync_skip_header_check
#pragma qt_sync_stop_processing
#endif
#include <QtCore/qoverload.h>
#ifdef __cpp_lib_three_way_comparison
#include <compare>
#endif
QT_BEGIN_NAMESPACE
/*
For all the macros these parameter names are used:
* LeftType - the type of the left operand of the comparison
* RightType - the type of the right operand of the comparison
* Constexpr - must be either constexpr or empty. Defines whether the
operator is constexpr or not
The macros require two helper functions. For operators to be constexpr,
these must be constexpr, too. Additionally, other attributes (like
Q_<Module>_EXPORT, Q_DECL_CONST_FUNCTION, etc) can be applied to them.
Aside from that, their declaration should match:
bool comparesEqual(LeftType, RightType) noexcept;
ReturnType compareThreeWay(LeftType, RightType) noexcept;
The ReturnType can be one of Qt::{partial,weak,strong}_ordering. The actual
type depends on the macro being used.
It makes sense to define the helper functions as hidden friends of the
class, so that they could be found via ADL, and don't participate in
unintended implicit conversions.
*/
// Seems that qdoc uses C++20 even when Qt is compiled in C++17 mode.
// Or at least it defines __cpp_lib_three_way_comparison.
// Let qdoc see only the C++17 operators for now, because that's what our docs
// currently describe.
#if defined(__cpp_lib_three_way_comparison) && !defined(Q_QDOC)
// C++20 - provide operator==() for equality, and operator<=>() for ordering
#define QT_DECLARE_EQUALITY_OPERATORS_HELPER(LeftType, RightType, Constexpr) \
friend Constexpr bool operator==(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(comparesEqual(lhs, rhs))) \
{ return comparesEqual(lhs, rhs); }
#define QT_DECLARE_3WAY_HELPER_STRONG(LeftType, RightType, Constexpr) \
friend Constexpr std::strong_ordering \
operator<=>(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
{ \
return compareThreeWay(lhs, rhs); \
}
#define QT_DECLARE_3WAY_HELPER_WEAK(LeftType, RightType, Constexpr) \
friend Constexpr std::weak_ordering \
operator<=>(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
{ \
return compareThreeWay(lhs, rhs); \
}
#define QT_DECLARE_3WAY_HELPER_PARTIAL(LeftType, RightType, Constexpr) \
friend Constexpr std::partial_ordering \
operator<=>(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
{ \
return compareThreeWay(lhs, rhs); \
}
#define QT_DECLARE_ORDERING_OPERATORS_HELPER(OrderingType, LeftType, RightType, Constexpr) \
QT_DECLARE_EQUALITY_OPERATORS_HELPER(LeftType, RightType, Constexpr) \
QT_DECLARE_3WAY_HELPER_ ## OrderingType (LeftType, RightType, Constexpr)
#ifdef Q_COMPILER_LACKS_THREE_WAY_COMPARE_SYMMETRY
// define reversed versions of the operators manually, because buggy MSVC versions do not do it
#define QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, Constexpr) \
friend Constexpr bool operator==(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(comparesEqual(rhs, lhs))) \
{ return comparesEqual(rhs, lhs); }
#define QT_DECLARE_REVERSED_3WAY_HELPER_STRONG(LeftType, RightType, Constexpr) \
friend Constexpr std::strong_ordering \
operator<=>(RightType const &lhs, LeftType const &rhs) \
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; \
return r; \
}
#define QT_DECLARE_REVERSED_3WAY_HELPER_WEAK(LeftType, RightType, Constexpr) \
friend Constexpr std::weak_ordering \
operator<=>(RightType const &lhs, LeftType const &rhs) \
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; \
return r; \
}
#define QT_DECLARE_REVERSED_3WAY_HELPER_PARTIAL(LeftType, RightType, Constexpr) \
friend Constexpr std::partial_ordering \
operator<=>(RightType const &lhs, LeftType const &rhs) \
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; \
return r; \
}
#define QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(OrderingString, LeftType, RightType, \
Constexpr) \
QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, Constexpr) \
QT_DECLARE_REVERSED_3WAY_HELPER_ ## OrderingString (LeftType, RightType, Constexpr)
#else
// dummy macros for C++17 compatibility, reversed operators are generated by the compiler
#define QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, Constexpr)
#define QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(OrderingString, LeftType, RightType, Constexpr)
#endif // Q_COMPILER_LACKS_THREE_WAY_COMPARE_SYMMETRY
#else
// C++17 - provide operator==() and operator!=() for equality,
// and all 4 comparison operators for ordering
#define QT_DECLARE_EQUALITY_OPERATORS_HELPER(LeftType, RightType, Constexpr) \
friend Constexpr bool operator==(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(comparesEqual(lhs, rhs))) \
{ return comparesEqual(lhs, rhs); } \
friend Constexpr bool operator!=(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(comparesEqual(lhs, rhs))) \
{ return !comparesEqual(lhs, rhs); }
// Helpers for reversed comparison, using the existing comparesEqual() function.
#define QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, Constexpr) \
friend Constexpr bool operator==(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(comparesEqual(rhs, lhs))) \
{ return comparesEqual(rhs, lhs); } \
friend Constexpr bool operator!=(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(comparesEqual(rhs, lhs))) \
{ return !comparesEqual(rhs, lhs); }
#define QT_DECLARE_ORDERING_HELPER_TEMPLATE(OrderingType, LeftType, RightType, Constexpr) \
friend Constexpr bool operator<(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
{ return compareThreeWay(lhs, rhs) < 0; } \
friend Constexpr bool operator>(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
{ return compareThreeWay(lhs, rhs) > 0; } \
friend Constexpr bool operator<=(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
{ return compareThreeWay(lhs, rhs) <= 0; } \
friend Constexpr bool operator>=(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
{ return compareThreeWay(lhs, rhs) >= 0; }
#define QT_DECLARE_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr) \
QT_DECLARE_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, Constexpr)
#define QT_DECLARE_ORDERING_HELPER_WEAK(LeftType, RightType, Constexpr) \
QT_DECLARE_ORDERING_HELPER_TEMPLATE(Qt::weak_ordering, LeftType, RightType, Constexpr)
#define QT_DECLARE_ORDERING_HELPER_STRONG(LeftType, RightType, Constexpr) \
QT_DECLARE_ORDERING_HELPER_TEMPLATE(Qt::strong_ordering, LeftType, RightType, Constexpr)
#define QT_DECLARE_ORDERING_OPERATORS_HELPER(OrderingString, LeftType, RightType, Constexpr) \
QT_DECLARE_EQUALITY_OPERATORS_HELPER(LeftType, RightType, Constexpr) \
QT_DECLARE_ORDERING_HELPER_ ## OrderingString (LeftType, RightType, Constexpr)
// Helpers for reversed ordering, using the existing compareThreeWay() function.
#define QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(OrderingType, LeftType, RightType, Constexpr) \
friend Constexpr bool operator<(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
{ return compareThreeWay(rhs, lhs) > 0; } \
friend Constexpr bool operator>(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
{ return compareThreeWay(rhs, lhs) < 0; } \
friend Constexpr bool operator<=(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
{ return compareThreeWay(rhs, lhs) >= 0; } \
friend Constexpr bool operator>=(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
{ return compareThreeWay(rhs, lhs) <= 0; }
#define QT_DECLARE_REVERSED_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr) \
QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, Constexpr)
#define QT_DECLARE_REVERSED_ORDERING_HELPER_WEAK(LeftType, RightType, Constexpr) \
QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(Qt::weak_ordering, LeftType, RightType, Constexpr)
#define QT_DECLARE_REVERSED_ORDERING_HELPER_STRONG(LeftType, RightType, Constexpr) \
QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(Qt::strong_ordering, LeftType, RightType, Constexpr)
#define QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(OrderingString, LeftType, RightType, \
Constexpr) \
QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, Constexpr) \
QT_DECLARE_REVERSED_ORDERING_HELPER_ ## OrderingString (LeftType, RightType, Constexpr)
#endif // __cpp_lib_three_way_comparison
/* Public API starts here */
// Equality operators
#define QT_DECLARE_EQUALITY_COMPARABLE_1(Type) \
QT_DECLARE_EQUALITY_OPERATORS_HELPER(Type, Type, /* non-constexpr */)
#define QT_DECLARE_EQUALITY_COMPARABLE_2(LeftType, RightType) \
QT_DECLARE_EQUALITY_OPERATORS_HELPER(LeftType, RightType, /* non-constexpr */) \
QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, /* non-constexpr */)
#define Q_DECLARE_EQUALITY_COMPARABLE(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_EQUALITY_COMPARABLE, __VA_ARGS__)
#define QT_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE_1(Type) \
QT_DECLARE_EQUALITY_OPERATORS_HELPER(Type, Type, constexpr)
#define QT_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE_2(LeftType, RightType) \
QT_DECLARE_EQUALITY_OPERATORS_HELPER(LeftType, RightType, constexpr) \
QT_DECLARE_EQUALITY_OPERATORS_REVERSED_HELPER(LeftType, RightType, constexpr)
#define Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE, __VA_ARGS__)
// Partial ordering operators
#define QT_DECLARE_PARTIALLY_ORDERED_1(Type) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(PARTIAL, Type, Type, /* non-constexpr */)
#define QT_DECLARE_PARTIALLY_ORDERED_2(LeftType, RightType) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(PARTIAL, LeftType, RightType, /* non-constexpr */) \
QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(PARTIAL, LeftType, RightType, /* non-constexpr */)
#define Q_DECLARE_PARTIALLY_ORDERED(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_PARTIALLY_ORDERED, __VA_ARGS__)
#define QT_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE_1(Type) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(PARTIAL, Type, Type, constexpr)
#define QT_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE_2(LeftType, RightType) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(PARTIAL, LeftType, RightType, constexpr) \
QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(PARTIAL, LeftType, RightType, constexpr)
#define Q_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_PARTIALLY_ORDERED_LITERAL_TYPE, __VA_ARGS__)
// Weak ordering operators
#define QT_DECLARE_WEAKLY_ORDERED_1(Type) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(WEAK, Type, Type, /* non-constexpr */)
#define QT_DECLARE_WEAKLY_ORDERED_2(LeftType, RightType) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(WEAK, LeftType, RightType, /* non-constexpr */) \
QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(WEAK, LeftType, RightType, /* non-constexpr */)
#define Q_DECLARE_WEAKLY_ORDERED(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_WEAKLY_ORDERED, __VA_ARGS__)
#define QT_DECLARE_WEAKLY_ORDERED_LITERAL_TYPE_1(Type) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(WEAK, Type, Type, constexpr)
#define QT_DECLARE_WEAKLY_ORDERED_LITERAL_TYPE_2(LeftType, RightType) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(WEAK, LeftType, RightType, constexpr) \
QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(WEAK, LeftType, RightType, constexpr)
#define Q_DECLARE_WEAKLY_ORDERED_LITERAL_TYPE(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_WEAKLY_ORDERED_LITERAL_TYPE, __VA_ARGS__)
// Strong ordering operators
#define QT_DECLARE_STRONGLY_ORDERED_1(Type) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(STRONG, Type, Type, /* non-constexpr */)
#define QT_DECLARE_STRONGLY_ORDERED_2(LeftType, RightType) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(STRONG, LeftType, RightType, /* non-constexpr */) \
QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(STRONG, LeftType, RightType, /* non-constexpr */)
#define Q_DECLARE_STRONGLY_ORDERED(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_STRONGLY_ORDERED, __VA_ARGS__)
#define QT_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE_1(Type) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(STRONG, Type, Type, constexpr)
#define QT_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE_2(LeftType, RightType) \
QT_DECLARE_ORDERING_OPERATORS_HELPER(STRONG, LeftType, RightType, constexpr) \
QT_DECLARE_ORDERING_OPERATORS_REVERSED_HELPER(STRONG, LeftType, RightType, constexpr)
#define Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE, __VA_ARGS__)
QT_END_NAMESPACE
#endif // QCOMPAREHELPERS_H

View File

@ -852,6 +852,13 @@
# if _MSC_VER >= 1910
# define Q_COMPILER_CONSTEXPR
# endif
// MSVC versions before 19.36 have a bug in C++20 comparison implementation.
// This leads to ambiguities when resolving comparison operator overloads in
// certain scenarios (the buggy MSVC versions were checked using our CI and
// compiler explorer).
# if _MSC_VER < 1936
# define Q_COMPILER_LACKS_THREE_WAY_COMPARE_SYMMETRY
# endif
# endif /* __cplusplus */
#endif // defined(Q_CC_MSVC) && !defined(Q_CC_CLANG)

View File

@ -127,6 +127,7 @@ void testAllComparisonOperatorsCompile()
Func(std::as_const(Left), std::as_const(Right), Op, Expected); \
/* END */
/*!
\internal
Basic testing of equality operators.
@ -166,8 +167,9 @@ void testEqualityOperators(LeftType lhs, RightType rhs, bool expectedEqual)
(==, !=, <, >, <=, >=) for the \a lhs operand of type \c {LeftType} and
the \a rhs operand of type \c {RightType}.
The \c OrderingType must be one of Qt::partial_ordering,
Qt::weak_ordering, or Qt::strong_ordering.
When compiled in C++17 mode, the \c OrderingType must be one of
Qt::partial_ordering, Qt::strong_ordering, or Qt::weak_ordering.
In C++20 mode, also the \c {std::*_ordering} types can be used.
The \a expectedOrdering parameter provides the expected
relation between \a lhs and \a rhs.
@ -189,9 +191,18 @@ void testAllComparisonOperators(LeftType lhs, RightType rhs, OrderingType expect
constexpr bool isQOrderingType = std::is_same_v<OrderingType, Qt::partial_ordering>
|| std::is_same_v<OrderingType, Qt::weak_ordering>
|| std::is_same_v<OrderingType, Qt::strong_ordering>;
static_assert(isQOrderingType,
#ifdef __cpp_lib_three_way_comparison
constexpr bool isStdOrderingType = std::is_same_v<OrderingType, std::partial_ordering>
|| std::is_same_v<OrderingType, std::weak_ordering>
|| std::is_same_v<OrderingType, std::strong_ordering>;
#else
constexpr bool isStdOrderingType = false;
#endif
static_assert(isQOrderingType || isStdOrderingType,
"Please provide, as the expectedOrdering parameter, a value "
"of one of the Qt::{partial,weak,strong_ordering types.");
"of one of the Qt::{partial,weak,strong}_ordering or "
"std::{partial,weak,strong}_ordering types.");
// We have all sorts of operator==() between Q*Ordering and std::*_ordering
// types, so we can just compare to Qt::partial_ordering.

View File

@ -4,6 +4,7 @@
if(NOT INTEGRITY)
add_subdirectory(qcompare)
endif()
add_subdirectory(qcomparehelpers)
add_subdirectory(qflags)
add_subdirectory(q_func_info)
add_subdirectory(qgetputenv)

View File

@ -0,0 +1,9 @@
# Copyright (C) 2023 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_test(tst_qcomparehelpers
SOURCES
tst_qcomparehelpers.cpp
LIBRARIES
Qt::TestPrivate
)

View File

@ -0,0 +1,491 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qcompare.h>
#include <QtTest/qtest.h>
#include <QtTest/private/qcomparisontesthelper_p.h>
class IntWrapper
{
public:
// implicit constructor and operator int() to simulate the case that
// triggers a bug on MSVC < 19.36.
IntWrapper(int val) : m_val(val) {}
operator int() const noexcept { return m_val; }
int value() const { return m_val; }
private:
friend bool comparesEqual(const IntWrapper &lhs, const IntWrapper &rhs) noexcept
{ return lhs.m_val == rhs.m_val; }
friend Qt::strong_ordering
compareThreeWay(const IntWrapper &lhs, const IntWrapper &rhs) noexcept
{
// ### Qt::compareThreeWay
if (lhs.m_val < rhs.m_val)
return Qt::strong_ordering::less;
else if (lhs.m_val > rhs.m_val)
return Qt::strong_ordering::greater;
else
return Qt::strong_ordering::equal;
}
friend bool comparesEqual(const IntWrapper &lhs, int rhs) noexcept
{ return lhs.m_val == rhs; }
friend Qt::strong_ordering compareThreeWay(const IntWrapper &lhs, int rhs) noexcept
{ return compareThreeWay(lhs, IntWrapper(rhs)); }
Q_DECLARE_STRONGLY_ORDERED(IntWrapper)
Q_DECLARE_STRONGLY_ORDERED(IntWrapper, int)
int m_val = 0;
};
class DoubleWrapper
{
public:
explicit DoubleWrapper(double val) : m_val(val) {}
double value() const { return m_val; }
private:
friend bool comparesEqual(const DoubleWrapper &lhs, const DoubleWrapper &rhs) noexcept
{ return lhs.m_val == rhs.m_val; }
friend Qt::partial_ordering
compareThreeWay(const DoubleWrapper &lhs, const DoubleWrapper &rhs) noexcept
{
// ### Qt::compareThreeWay
if (qIsNaN(lhs.m_val) || qIsNaN(rhs.m_val))
return Qt::partial_ordering::unordered;
if (lhs.m_val < rhs.m_val)
return Qt::partial_ordering::less;
else if (lhs.m_val > rhs.m_val)
return Qt::partial_ordering::greater;
else
return Qt::partial_ordering::equivalent;
}
friend bool comparesEqual(const DoubleWrapper &lhs, const IntWrapper &rhs) noexcept
{ return comparesEqual(lhs, DoubleWrapper(rhs.value())); }
friend Qt::partial_ordering
compareThreeWay(const DoubleWrapper &lhs, const IntWrapper &rhs) noexcept
{ return compareThreeWay(lhs, DoubleWrapper(rhs.value())); }
friend bool comparesEqual(const DoubleWrapper &lhs, double rhs) noexcept
{ return lhs.m_val == rhs; }
friend Qt::partial_ordering compareThreeWay(const DoubleWrapper &lhs, double rhs) noexcept
{
// ### Qt::compareThreeWay
if (qIsNaN(lhs.m_val) || qIsNaN(rhs))
return Qt::partial_ordering::unordered;
if (lhs.m_val < rhs)
return Qt::partial_ordering::less;
else if (lhs.m_val > rhs)
return Qt::partial_ordering::greater;
else
return Qt::partial_ordering::equivalent;
}
Q_DECLARE_PARTIALLY_ORDERED(DoubleWrapper)
Q_DECLARE_PARTIALLY_ORDERED(DoubleWrapper, IntWrapper)
Q_DECLARE_PARTIALLY_ORDERED(DoubleWrapper, double)
double m_val = 0.0;
};
template <typename String>
class StringWrapper
{
public:
explicit StringWrapper(String val) : m_val(val) {}
String value() const { return m_val; }
private:
// Some of the helper functions are intentionally NOT marked as noexcept
// to test the conditional noexcept in the macros.
template <typename T>
friend bool comparesEqual(const StringWrapper<T> &, const StringWrapper<T> &) noexcept;
template <typename T>
friend Qt::weak_ordering
compareThreeWay(const StringWrapper<T> &, const StringWrapper<T> &) noexcept;
template <typename T>
friend bool comparesEqual(const StringWrapper<T> &, QAnyStringView);
template <typename T>
friend Qt::weak_ordering compareThreeWay(const StringWrapper<T> &, QAnyStringView);
Q_DECLARE_WEAKLY_ORDERED(StringWrapper)
Q_DECLARE_WEAKLY_ORDERED(StringWrapper, QAnyStringView)
String m_val;
};
// StringWrapper comparison helper functions
bool equalsHelper(QAnyStringView lhs, QAnyStringView rhs) noexcept
{
return QAnyStringView::compare(lhs, rhs, Qt::CaseInsensitive) == 0;
}
template <typename T>
bool comparesEqual(const StringWrapper<T> &lhs, const StringWrapper<T> &rhs) noexcept
{
return equalsHelper(lhs.m_val, rhs.m_val);
}
Qt::weak_ordering compareHelper(QAnyStringView lhs, QAnyStringView rhs) noexcept
{
const int res = QAnyStringView::compare(lhs, rhs, Qt::CaseInsensitive);
if (res < 0)
return Qt::weak_ordering::less;
else if (res > 0)
return Qt::weak_ordering::greater;
else
return Qt::weak_ordering::equivalent;
}
template <typename T>
Qt::weak_ordering compareThreeWay(const StringWrapper<T> &lhs, const StringWrapper<T> &rhs) noexcept
{
return compareHelper(lhs.m_val, rhs.m_val);
}
template <typename T>
bool comparesEqual(const StringWrapper<T> &lhs, QAnyStringView rhs)
{
return equalsHelper(lhs.m_val, rhs);
}
template <typename T>
Qt::weak_ordering compareThreeWay(const StringWrapper<T> &lhs, QAnyStringView rhs)
{
return compareHelper(lhs.m_val, rhs);
}
// Test class
class tst_QCompareHelpers : public QObject
{
Q_OBJECT
private:
template <typename LeftType, typename RightType, typename OrderingType>
void compareImpl();
template <typename LeftType, typename RightType>
void compareIntData();
template <typename LeftType, typename RightType>
void compareFloatData();
template <typename LeftType, typename RightType>
void compareStringData();
private slots:
void comparisonCompiles();
void compare_IntWrapper_data() { compareIntData<IntWrapper, IntWrapper>(); }
void compare_IntWrapper() { compareImpl<IntWrapper, IntWrapper, Qt::strong_ordering>(); }
void compare_IntWrapper_int_data() { compareIntData<IntWrapper, int>(); }
void compare_IntWrapper_int() { compareImpl<IntWrapper, int, Qt::strong_ordering>(); }
void compare_DoubleWrapper_data() { compareFloatData<DoubleWrapper, DoubleWrapper>(); }
void compare_DoubleWrapper()
{ compareImpl<DoubleWrapper, DoubleWrapper, Qt::partial_ordering>(); }
void compare_DoubleWrapper_double_data() { compareFloatData<DoubleWrapper, double>(); }
void compare_DoubleWrapper_double()
{ compareImpl<DoubleWrapper, double, Qt::partial_ordering>(); }
void compare_IntWrapper_DoubleWrapper_data();
void compare_IntWrapper_DoubleWrapper()
{ compareImpl<IntWrapper, DoubleWrapper, Qt::partial_ordering>(); }
void compare_StringWrapper_data()
{ compareStringData<StringWrapper<QString>, StringWrapper<QString>>(); }
void compare_StringWrapper()
{ compareImpl<StringWrapper<QString>, StringWrapper<QString>, Qt::weak_ordering>(); }
void compare_StringWrapper_AnyStringView_data()
{ compareStringData<StringWrapper<QString>, QAnyStringView>(); }
void compare_StringWrapper_AnyStringView()
{ compareImpl<StringWrapper<QString>, QAnyStringView, Qt::weak_ordering>(); }
void generatedClasses();
};
template<typename LeftType, typename RightType, typename OrderingType>
void tst_QCompareHelpers::compareImpl()
{
QFETCH(LeftType, lhs);
QFETCH(RightType, rhs);
QFETCH(OrderingType, expectedOrdering);
QTestPrivate::testAllComparisonOperators(lhs, rhs, expectedOrdering);
if (QTest::currentTestFailed())
return;
#ifdef __cpp_lib_three_way_comparison
// Also check std types.
// if Ordering == Qt::strong_ordering -> std::strong_ordering
// else if Ordering == Qt::weak_ordering -> std::weak_ordering
// else std::partial_ordering
using StdType = std::conditional_t<
std::is_same_v<OrderingType, Qt::strong_ordering>,
std::strong_ordering,
std::conditional_t<std::is_same_v<OrderingType, Qt::weak_ordering>,
std::weak_ordering,
std::partial_ordering>>;
QTestPrivate::testAllComparisonOperators(lhs, rhs, static_cast<StdType>(expectedOrdering));
if (QTest::currentTestFailed())
return;
#endif // __cpp_lib_three_way_comparison
}
template<typename LeftType, typename RightType>
void tst_QCompareHelpers::compareIntData()
{
QTest::addColumn<LeftType>("lhs");
QTest::addColumn<RightType>("rhs");
QTest::addColumn<Qt::strong_ordering>("expectedOrdering");
auto createRow = [](auto lhs, auto rhs, Qt::strong_ordering ordering) {
QTest::addRow("%d vs %d", lhs, rhs) << LeftType(lhs) << RightType(rhs) << ordering;
};
createRow(0, 0, Qt::strong_ordering::equivalent);
createRow(-1, 0, Qt::strong_ordering::less);
createRow(1, 0, Qt::strong_ordering::greater);
constexpr int max = std::numeric_limits<int>::max();
constexpr int min = std::numeric_limits<int>::min();
createRow(max, max, Qt::strong_ordering::equivalent);
createRow(min, min, Qt::strong_ordering::equivalent);
createRow(max, min, Qt::strong_ordering::greater);
createRow(min, max, Qt::strong_ordering::less);
}
template<typename LeftType, typename RightType>
void tst_QCompareHelpers::compareFloatData()
{
QTest::addColumn<LeftType>("lhs");
QTest::addColumn<RightType>("rhs");
QTest::addColumn<Qt::partial_ordering>("expectedOrdering");
auto createRow = [](auto lhs, auto rhs, Qt::partial_ordering ordering) {
QTest::addRow("%f vs %f", lhs, rhs) << LeftType(lhs) << RightType(rhs) << ordering;
};
createRow(0.0, 0.0, Qt::partial_ordering::equivalent);
createRow(-0.000001, 0.0, Qt::partial_ordering::less);
createRow(0.000001, 0.0, Qt::partial_ordering::greater);
const double nan = qQNaN();
createRow(nan, 0.0, Qt::partial_ordering::unordered);
createRow(0.0, nan, Qt::partial_ordering::unordered);
createRow(nan, nan, Qt::partial_ordering::unordered);
const double inf = qInf();
createRow(inf, 0.0, Qt::partial_ordering::greater);
createRow(0.0, inf, Qt::partial_ordering::less);
createRow(-inf, 0.0, Qt::partial_ordering::less);
createRow(0.0, -inf, Qt::partial_ordering::greater);
createRow(inf, inf, Qt::partial_ordering::equivalent);
createRow(-inf, -inf, Qt::partial_ordering::equivalent);
createRow(-inf, inf, Qt::partial_ordering::less);
createRow(inf, -inf, Qt::partial_ordering::greater);
createRow(nan, inf, Qt::partial_ordering::unordered);
createRow(inf, nan, Qt::partial_ordering::unordered);
createRow(nan, -inf, Qt::partial_ordering::unordered);
createRow(-inf, nan, Qt::partial_ordering::unordered);
}
template<typename LeftType, typename RightType>
void tst_QCompareHelpers::compareStringData()
{
QTest::addColumn<LeftType>("lhs");
QTest::addColumn<RightType>("rhs");
QTest::addColumn<Qt::weak_ordering>("expectedOrdering");
auto createRow = [](auto lhs, auto rhs, Qt::weak_ordering ordering) {
QTest::addRow("'%s' vs '%s'", lhs, rhs) << LeftType(lhs) << RightType(rhs) << ordering;
};
createRow("", "", Qt::weak_ordering::equivalent);
createRow("Ab", "abc", Qt::weak_ordering::less);
createRow("aBc", "AB", Qt::weak_ordering::greater);
createRow("ab", "AB", Qt::weak_ordering::equivalent);
createRow("ABC", "abc", Qt::weak_ordering::equivalent);
}
void tst_QCompareHelpers::comparisonCompiles()
{
QTestPrivate::testAllComparisonOperatorsCompile<IntWrapper>();
if (QTest::currentTestFailed())
return;
QTestPrivate::testAllComparisonOperatorsCompile<IntWrapper, int>();
if (QTest::currentTestFailed())
return;
QTestPrivate::testAllComparisonOperatorsCompile<DoubleWrapper>();
if (QTest::currentTestFailed())
return;
QTestPrivate::testAllComparisonOperatorsCompile<DoubleWrapper, double>();
if (QTest::currentTestFailed())
return;
QTestPrivate::testAllComparisonOperatorsCompile<DoubleWrapper, IntWrapper>();
if (QTest::currentTestFailed())
return;
QTestPrivate::testAllComparisonOperatorsCompile<StringWrapper<QString>>();
if (QTest::currentTestFailed())
return;
QTestPrivate::testAllComparisonOperatorsCompile<StringWrapper<QString>, QAnyStringView>();
if (QTest::currentTestFailed())
return;
}
void tst_QCompareHelpers::compare_IntWrapper_DoubleWrapper_data()
{
QTest::addColumn<IntWrapper>("lhs");
QTest::addColumn<DoubleWrapper>("rhs");
QTest::addColumn<Qt::partial_ordering>("expectedOrdering");
auto createRow = [](auto lhs, auto rhs, Qt::partial_ordering ordering) {
QTest::addRow("%d vs %f", lhs, rhs) << IntWrapper(lhs) << DoubleWrapper(rhs) << ordering;
};
createRow(0, 0.0, Qt::partial_ordering::equivalent);
createRow(-1, 0.0, Qt::partial_ordering::less);
createRow(1, 0.0, Qt::partial_ordering::greater);
createRow(0, -0.000001, Qt::partial_ordering::greater);
createRow(0, 0.000001, Qt::partial_ordering::less);
constexpr int max = std::numeric_limits<int>::max();
constexpr int min = std::numeric_limits<int>::min();
const double nan = qQNaN();
createRow(0, nan, Qt::partial_ordering::unordered);
createRow(max, nan, Qt::partial_ordering::unordered);
createRow(min, nan, Qt::partial_ordering::unordered);
const double inf = qInf();
createRow(0, inf, Qt::partial_ordering::less);
createRow(0, -inf, Qt::partial_ordering::greater);
createRow(max, inf, Qt::partial_ordering::less);
createRow(max, -inf, Qt::partial_ordering::greater);
createRow(min, inf, Qt::partial_ordering::less);
createRow(min, -inf, Qt::partial_ordering::greater);
}
#define DECLARE_TYPE(Name, Type, Attrs, RetType, Constexpr, Suffix) \
class Dummy ## Name \
{ \
public: \
Constexpr Dummy ## Name () {} \
\
private: \
friend Attrs Constexpr bool \
comparesEqual(const Dummy ## Name &lhs, const Dummy ## Name &rhs) noexcept; \
friend Attrs Constexpr RetType \
compareThreeWay(const Dummy ## Name &lhs, const Dummy ## Name &rhs) noexcept; \
friend Attrs Constexpr bool \
comparesEqual(const Dummy ## Name &lhs, int rhs) noexcept; \
friend Attrs Constexpr RetType \
compareThreeWay(const Dummy ## Name &lhs, int rhs) noexcept; \
Q_DECLARE_ ## Type ##_ORDERED ## Suffix (Dummy ## Name) \
Q_DECLARE_ ## Type ##_ORDERED ## Suffix (Dummy ## Name, int) \
}; \
\
Attrs Constexpr bool comparesEqual(const Dummy ## Name &lhs, const Dummy ## Name &rhs) noexcept \
{ Q_UNUSED(lhs); Q_UNUSED(rhs); return true; } \
Attrs Constexpr RetType \
compareThreeWay(const Dummy ## Name &lhs, const Dummy ## Name &rhs) noexcept \
{ Q_UNUSED(lhs); Q_UNUSED(rhs); return RetType::equivalent; } \
Attrs Constexpr bool comparesEqual(const Dummy ## Name &lhs, int rhs) noexcept \
{ Q_UNUSED(lhs); Q_UNUSED(rhs); return true; } \
Attrs Constexpr RetType compareThreeWay(const Dummy ## Name &lhs, int rhs) noexcept \
{ Q_UNUSED(lhs); Q_UNUSED(rhs); return RetType::equivalent; }
DECLARE_TYPE(PartialConstAttr, PARTIALLY, Q_DECL_PURE_FUNCTION, Qt::partial_ordering, constexpr,
_LITERAL_TYPE)
DECLARE_TYPE(PartialConst, PARTIALLY, /* no attrs */, Qt::partial_ordering, constexpr, _LITERAL_TYPE)
DECLARE_TYPE(PartialAttr, PARTIALLY, Q_DECL_CONST_FUNCTION, Qt::partial_ordering, , )
DECLARE_TYPE(Partial, PARTIALLY, /* no attrs */, Qt::partial_ordering, , )
DECLARE_TYPE(WeakConstAttr, WEAKLY, Q_DECL_PURE_FUNCTION, Qt::weak_ordering, constexpr, _LITERAL_TYPE)
DECLARE_TYPE(WeakConst, WEAKLY, /* no attrs */, Qt::weak_ordering, constexpr, _LITERAL_TYPE)
DECLARE_TYPE(WeakAttr, WEAKLY, Q_DECL_CONST_FUNCTION, Qt::weak_ordering, , )
DECLARE_TYPE(Weak, WEAKLY, /* no attrs */, Qt::weak_ordering, , )
DECLARE_TYPE(StrongConstAttr, STRONGLY, Q_DECL_PURE_FUNCTION, Qt::strong_ordering, constexpr,
_LITERAL_TYPE)
DECLARE_TYPE(StrongConst, STRONGLY, /* no attrs */, Qt::strong_ordering, constexpr, _LITERAL_TYPE)
DECLARE_TYPE(StrongAttr, STRONGLY, Q_DECL_CONST_FUNCTION, Qt::strong_ordering, , )
DECLARE_TYPE(Strong, STRONGLY, /* no attrs */, Qt::strong_ordering, , )
#define DECLARE_EQUALITY_COMPARABLE(Name, Attrs, Constexpr, Suffix) \
class Dummy ## Name \
{ \
public: \
Constexpr Dummy ## Name (int) {} \
\
private: \
friend Attrs Constexpr bool \
comparesEqual(const Dummy ## Name &lhs, const Dummy ## Name &rhs) noexcept; \
friend Attrs Constexpr bool comparesEqual(const Dummy ## Name &lhs, int rhs) noexcept; \
Q_DECLARE_EQUALITY_COMPARABLE ## Suffix (Dummy ## Name) \
Q_DECLARE_EQUALITY_COMPARABLE ## Suffix (Dummy ## Name, int) \
}; \
\
Attrs Constexpr bool comparesEqual(const Dummy ## Name &lhs, const Dummy ## Name &rhs) noexcept \
{ Q_UNUSED(lhs); Q_UNUSED(rhs); return true; } \
Attrs Constexpr bool comparesEqual(const Dummy ## Name &lhs, int rhs) noexcept \
{ Q_UNUSED(lhs); Q_UNUSED(rhs); return true; } \
DECLARE_EQUALITY_COMPARABLE(ConstAttr, Q_DECL_PURE_FUNCTION, constexpr, _LITERAL_TYPE)
DECLARE_EQUALITY_COMPARABLE(Const, /* no attrs */, constexpr, _LITERAL_TYPE)
DECLARE_EQUALITY_COMPARABLE(Attr, Q_DECL_CONST_FUNCTION, , )
DECLARE_EQUALITY_COMPARABLE(None, /* no attrs */, , )
void tst_QCompareHelpers::generatedClasses()
{
#define COMPARE(ClassName) \
do { \
QTestPrivate::testAllComparisonOperatorsCompile<ClassName>(); \
QTestPrivate::testAllComparisonOperatorsCompile<ClassName, int>(); \
} while (0)
COMPARE(DummyPartialConstAttr);
COMPARE(DummyPartialConst);
COMPARE(DummyPartialAttr);
COMPARE(DummyPartial);
COMPARE(DummyWeakConstAttr);
COMPARE(DummyWeakConst);
COMPARE(DummyWeakAttr);
COMPARE(DummyWeak);
COMPARE(DummyStrongConstAttr);
COMPARE(DummyStrongConst);
COMPARE(DummyStrongAttr);
COMPARE(DummyStrong);
#undef COMPARE
QTestPrivate::testEqualityOperatorsCompile<DummyConstAttr>();
QTestPrivate::testEqualityOperatorsCompile<DummyConstAttr, int>();
QTestPrivate::testEqualityOperatorsCompile<DummyConst>();
QTestPrivate::testEqualityOperatorsCompile<DummyConst, int>();
QTestPrivate::testEqualityOperatorsCompile<DummyAttr>();
QTestPrivate::testEqualityOperatorsCompile<DummyAttr, int>();
QTestPrivate::testEqualityOperatorsCompile<DummyNone>();
QTestPrivate::testEqualityOperatorsCompile<DummyNone, int>();
}
QTEST_MAIN(tst_QCompareHelpers)
#include "tst_qcomparehelpers.moc"