From 4b6f757020382ed157bf6beb572549f05e881359 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 16 Nov 2023 07:47:14 +0100 Subject: [PATCH] Make Qt::partial_ordering binary-compatible to std::partial_ordering In particular, match the value of ::unordered to each std library implementation. Legal disclaimer: The values were provided to this author in a comment on QTBUG-118913. This author hereby confirms he didn't look at any of the implementations himself. The stdlib detection macros are taken from existing code in qcompilerdetection.h. I didn't succeed in googling a corresponding marker for MSSTL, and I didn't look at the implementation or Boost.Config to find one, so this patch just assumes MSSTL as a fall-back, which is probably wrong, since we may still have Dinkumware and RougeWave STLs to deal with on embedded platforms. Add tests to ensure the values are the same on all platforms. To maximize coverage, rename qcompare.qdoc to qcompare.cpp and add a bunch of compile-time tests there. These depend in part on bit_cast, which we cannot depend on, so tst_qcompare contains the same tests using memcpy. Fixes: QTBUG-118913 Change-Id: I46c922c8e3ea37d7c01a71361c7a689340f9047d Reviewed-by: Ivan Solovev --- src/corelib/CMakeLists.txt | 2 +- .../global/{qcompare.qdoc => qcompare.cpp} | 43 +++++++++++++++++-- src/corelib/global/qcompare.h | 24 ++++++++--- .../corelib/global/qcompare/tst_qcompare.cpp | 39 +++++++++++++++++ 4 files changed, 98 insertions(+), 10 deletions(-) rename src/corelib/global/{qcompare.qdoc => qcompare.cpp} (95%) diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index ec0e802d7c..827680cc4f 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -49,7 +49,7 @@ qt_internal_add_module(Core global/archdetect.cpp global/qassert.cpp global/qassert.h global/qcompare_impl.h - global/qcompare.h + global/qcompare.cpp global/qcompare.h global/qcompilerdetection.h global/qconstructormacros.h global/qcontainerinfo.h diff --git a/src/corelib/global/qcompare.qdoc b/src/corelib/global/qcompare.cpp similarity index 95% rename from src/corelib/global/qcompare.qdoc rename to src/corelib/global/qcompare.cpp index 48c2f05803..e02c78b6ae 100644 --- a/src/corelib/global/qcompare.qdoc +++ b/src/corelib/global/qcompare.cpp @@ -2,6 +2,41 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only +#include "qcompare.h" + +#ifdef __cpp_lib_bit_cast +#include +#endif + +QT_BEGIN_NAMESPACE + +#ifdef __cpp_lib_three_way_comparison +static_assert(sizeof(std::partial_ordering) == sizeof(Qt::partial_ordering)); +static_assert(sizeof(std::weak_ordering) == sizeof(Qt::weak_ordering)); +static_assert(sizeof(std::strong_ordering) == sizeof(Qt::strong_ordering)); +#ifdef __cpp_lib_bit_cast +#define CHECK(type, flag) \ + static_assert(std::bit_cast(std:: type ## _ordering:: flag) \ + == Qt:: type ## _ordering :: flag); \ + static_assert(std::bit_cast(Qt:: type ## _ordering:: flag) \ + == std:: type ## _ordering :: flag) \ + /* end */ +CHECK(partial, unordered); +CHECK(partial, less); +CHECK(partial, greater); +CHECK(partial, equivalent); +CHECK(weak, less); +CHECK(weak, greater); +CHECK(weak, equivalent); +CHECK(strong, less); +CHECK(strong, greater); +CHECK(strong, equal); +CHECK(strong, equivalent); +#undef CHECK +#endif // __cpp_lib_bit_cast +#endif //__cpp_lib_three_way_comparison + + /*! \page comparison-types.html overview \title Comparison types overview @@ -398,7 +433,7 @@ \fn Qt::weak_ordering::is_gt (Qt::weak_ordering o) \fn Qt::weak_ordering::is_gteq(Qt::weak_ordering o) - \include qcompare.qdoc is_eq_table + \include qcompare.cpp is_eq_table These functions are provided for compatibility with \c{std::weak_ordering}. */ @@ -544,7 +579,7 @@ \fn Qt::partial_ordering::is_gt (Qt::partial_ordering o) \fn Qt::partial_ordering::is_gteq(Qt::partial_ordering o) - \include qcompare.qdoc is_eq_table + \include qcompare.cpp is_eq_table These functions are provided for compatibility with \c{std::partial_ordering}. */ @@ -697,7 +732,7 @@ \fn QPartialOrdering::is_gt (QPartialOrdering o) \fn QPartialOrdering::is_gteq(QPartialOrdering o) - \include qcompare.qdoc is_eq_table + \include qcompare.cpp is_eq_table These functions are provided for compatibility with \c{std::partial_ordering}. */ @@ -729,3 +764,5 @@ Represents the result of a comparison where the left operand is not ordered with respect to the right operand. */ + +QT_END_NAMESPACE diff --git a/src/corelib/global/qcompare.h b/src/corelib/global/qcompare.h index 1a9d53bc73..90341270d7 100644 --- a/src/corelib/global/qcompare.h +++ b/src/corelib/global/qcompare.h @@ -30,11 +30,23 @@ enum class Ordering : CompareUnderlyingType Greater = 1 }; -enum class Uncomparable : CompareUnderlyingType +enum class LegacyUncomparable : CompareUnderlyingType { Unordered = -127 }; +enum class Uncomparable : CompareUnderlyingType +{ + Unordered = + #if defined(_LIBCPP_VERSION) // libc++ + -127 + #elif defined(__GLIBCXX__) // libstd++ + 2 + #else // assume MSSTL + -128 + #endif +}; + } // namespace QtPrivate // [cmp.partialord] @@ -112,7 +124,7 @@ public: else if (stdorder == std::partial_ordering::greater) m_order = static_cast(QtPrivate::Ordering::Greater); else if (stdorder == std::partial_ordering::unordered) - m_order = static_cast(QtPrivate::Uncomparable::Unordered); + m_order = static_cast(QtPrivate::LegacyUncomparable::Unordered); } constexpr Q_IMPLICIT operator std::partial_ordering() const noexcept @@ -123,7 +135,7 @@ public: return std::partial_ordering::equivalent; else if (static_cast(m_order) == QtPrivate::Ordering::Greater) return std::partial_ordering::greater; - else if (static_cast(m_order) == QtPrivate::Uncomparable::Unordered) + else if (static_cast(m_order) == QtPrivate::LegacyUncomparable::Unordered) return std::partial_ordering::unordered; return std::partial_ordering::unordered; } @@ -145,7 +157,7 @@ private: constexpr explicit QPartialOrdering(QtPrivate::Ordering order) noexcept : m_order(static_cast(order)) {} - constexpr explicit QPartialOrdering(QtPrivate::Uncomparable order) noexcept + constexpr explicit QPartialOrdering(QtPrivate::LegacyUncomparable order) noexcept : m_order(static_cast(order)) {} @@ -163,7 +175,7 @@ private: // instead of the exposition only is_ordered member in [cmp.partialord], // use a private function constexpr bool isOrdered() const noexcept - { return m_order != static_cast(QtPrivate::Uncomparable::Unordered); } + { return m_order != static_cast(QtPrivate::LegacyUncomparable::Unordered); } QtPrivate::CompareUnderlyingType m_order; }; @@ -171,7 +183,7 @@ private: inline constexpr QPartialOrdering QPartialOrdering::Less(QtPrivate::Ordering::Less); inline constexpr QPartialOrdering QPartialOrdering::Equivalent(QtPrivate::Ordering::Equivalent); inline constexpr QPartialOrdering QPartialOrdering::Greater(QtPrivate::Ordering::Greater); -inline constexpr QPartialOrdering QPartialOrdering::Unordered(QtPrivate::Uncomparable::Unordered); +inline constexpr QPartialOrdering QPartialOrdering::Unordered(QtPrivate::LegacyUncomparable::Unordered); namespace Qt { diff --git a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp index f0bba8b3ce..7561fbdc38 100644 --- a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp +++ b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp @@ -13,6 +13,7 @@ class tst_QCompare: public QObject Q_OBJECT private slots: void legacyPartialOrdering(); + void stdQtBinaryCompatibility(); void partialOrdering(); void weakOrdering(); void strongOrdering(); @@ -130,6 +131,44 @@ void tst_QCompare::legacyPartialOrdering() static_assert(!(0 >= QPartialOrdering::Greater)); } +void tst_QCompare::stdQtBinaryCompatibility() +{ +#ifndef __cpp_lib_three_way_comparison + QSKIP("This test requires C++20 three-way-comparison support enabled in the stdlib."); +#else + QCOMPARE_EQ(sizeof(std::partial_ordering), 1U); + QCOMPARE_EQ(sizeof( Qt::partial_ordering), 1U); + QCOMPARE_EQ(sizeof(std:: weak_ordering), 1U); + QCOMPARE_EQ(sizeof( Qt:: weak_ordering), 1U); + QCOMPARE_EQ(sizeof(std:: strong_ordering), 1U); + QCOMPARE_EQ(sizeof( Qt:: strong_ordering), 1U); + + auto valueOf = [](auto obj) { + typename QIntegerForSizeof::Unsigned value; + memcpy(&value, &obj, sizeof(obj)); + return value; + }; +#define CHECK(type, flag) \ + QCOMPARE_EQ(valueOf( Qt:: type ## _ordering :: flag), \ + valueOf(std:: type ## _ordering :: flag)) \ + /* end */ + CHECK(partial, unordered); + CHECK(partial, less); + CHECK(partial, greater); + CHECK(partial, equivalent); + + CHECK(weak, less); + CHECK(weak, greater); + CHECK(weak, equivalent); + + CHECK(strong, less); + CHECK(strong, greater); + CHECK(strong, equivalent); + CHECK(strong, equal); +#undef CHECK +#endif //__cpp_lib_three_way_comparison +} + void tst_QCompare::partialOrdering() { static_assert(Qt::partial_ordering::unordered == Qt::partial_ordering::unordered);