summaryrefslogtreecommitdiffstats
path: root/src/corelib/global/qcomparehelpers.h
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2023-05-02 12:33:26 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2023-11-28 20:30:19 +0000
commitfe12650e9d85ea0ed4a73f85cdbef0ddf3b67ae3 (patch)
tree465c96111fc234c422b7d37fa34797e00c081163 /src/corelib/global/qcomparehelpers.h
parente616f8decb998f68753300c8282b48d05bd6fd01 (diff)
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>
Diffstat (limited to 'src/corelib/global/qcomparehelpers.h')
-rw-r--r--src/corelib/global/qcomparehelpers.h307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h
new file mode 100644
index 0000000000..f445e29b00
--- /dev/null
+++ b/src/corelib/global/qcomparehelpers.h
@@ -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