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 <ivan.solovev@qt.io>
bb10
Marc Mutz 2023-11-16 07:47:14 +01:00 committed by Ivan Solovev
parent c8cd9017f6
commit 4b6f757020
4 changed files with 98 additions and 10 deletions

View File

@ -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

View File

@ -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 <bit>
#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<Qt:: type ## _ordering>(std:: type ## _ordering:: flag) \
== Qt:: type ## _ordering :: flag); \
static_assert(std::bit_cast<std:: type ## _ordering>(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

View File

@ -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::CompareUnderlyingType>(QtPrivate::Ordering::Greater);
else if (stdorder == std::partial_ordering::unordered)
m_order = static_cast<QtPrivate::CompareUnderlyingType>(QtPrivate::Uncomparable::Unordered);
m_order = static_cast<QtPrivate::CompareUnderlyingType>(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<QtPrivate::Ordering>(m_order) == QtPrivate::Ordering::Greater)
return std::partial_ordering::greater;
else if (static_cast<QtPrivate::Uncomparable>(m_order) == QtPrivate::Uncomparable::Unordered)
else if (static_cast<QtPrivate::LegacyUncomparable>(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<QtPrivate::CompareUnderlyingType>(order))
{}
constexpr explicit QPartialOrdering(QtPrivate::Uncomparable order) noexcept
constexpr explicit QPartialOrdering(QtPrivate::LegacyUncomparable order) noexcept
: m_order(static_cast<QtPrivate::CompareUnderlyingType>(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::CompareUnderlyingType>(QtPrivate::Uncomparable::Unordered); }
{ return m_order != static_cast<QtPrivate::CompareUnderlyingType>(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 {

View File

@ -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<decltype(obj)>::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);