diff options
author | Giuseppe D'Angelo <[email protected]> | 2024-08-01 11:17:03 +0200 |
---|---|---|
committer | Giuseppe D'Angelo <[email protected]> | 2024-08-18 11:14:00 +0200 |
commit | 7447ad503330ed176cf369792ffb33b7e00a58d3 (patch) | |
tree | 297b9873f95106cd8af4b1cfd60e5f3b5a64b701 | |
parent | 47fd38be4bce0958fcfce8080d1580c4e3c2a15b (diff) |
Rename qt_saturate to q26::saturate_cast
C++26 adds std::saturate_cast, so follow the established pattern of
other similar "backported" APIs. The old name is left around while
we port other submodules.
While at it, move qt_saturate's tests to the qnumeric test.
Pick-to: 6.8
Change-Id: I653a2e3d936081378298a9c8e51e7c1a2d438d83
Reviewed-by: Thiago Macieira <[email protected]>
-rw-r--r-- | src/corelib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/corelib/global/q26numeric.h | 69 | ||||
-rw-r--r-- | src/corelib/global/qnumeric.cpp | 35 | ||||
-rw-r--r-- | src/corelib/global/qnumeric_p.h | 39 | ||||
-rw-r--r-- | src/corelib/kernel/qabstracteventdispatcher.cpp | 5 | ||||
-rw-r--r-- | src/corelib/kernel/qeventdispatcher_glib.cpp | 5 | ||||
-rw-r--r-- | src/corelib/text/qbytearray.cpp | 3 | ||||
-rw-r--r-- | src/corelib/text/qstringconverter.cpp | 12 | ||||
-rw-r--r-- | src/testlib/qtestresult.cpp | 5 | ||||
-rw-r--r-- | tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp | 37 |
10 files changed, 125 insertions, 86 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index cc615f91144..304cd365631 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -102,6 +102,7 @@ qt_internal_add_module(Core global/q23utility.cpp # remove once we have a user that tests this global/q23type_traits.h global/q23utility.h + global/q26numeric.h global/qxpfunctional.h global/qxptype_traits.h global/qversiontagging.h diff --git a/src/corelib/global/q26numeric.h b/src/corelib/global/q26numeric.h new file mode 100644 index 00000000000..72a4d499481 --- /dev/null +++ b/src/corelib/global/q26numeric.h @@ -0,0 +1,69 @@ +// Copyright (C) 2024 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 Q26NUMERIC_H +#define Q26NUMERIC_H + +#include <QtCore/qglobal.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. Types and functions defined in this +// file can reliably be replaced by their std counterparts, once available. +// You may use these definitions in your own code, but be aware that we +// will remove them once Qt depends on the C++ version that supports +// them in namespace std. There will be NO deprecation warning, the +// definitions will JUST go away. +// +// If you can't agree to these terms, don't use these definitions! +// +// We mean it. +// + +#include <numeric> +#include <limits> +#include <type_traits> + +QT_BEGIN_NAMESPACE + +namespace q26 { + +// Like std::saturate_cast +#ifdef __cpp_lib_saturation_arithmetic +using std::saturate_cast; +#else +template <typename To, typename From> +constexpr auto saturate_cast(From x) +{ + static_assert(std::is_integral_v<To>); + static_assert(std::is_integral_v<From>); + + [[maybe_unused]] + constexpr auto Lo = (std::numeric_limits<To>::min)(); + constexpr auto Hi = (std::numeric_limits<To>::max)(); + + if constexpr (std::is_signed_v<From> == std::is_signed_v<To>) { + // same signedness, we can accept regular integer conversion rules + return x < Lo ? Lo : + x > Hi ? Hi : + /*else*/ To(x); + } else { + if constexpr (std::is_signed_v<From>) { // ie. !is_signed_v<To> + if (x < From{0}) + return To{0}; + } + + // from here on, x >= 0 + using FromU = std::make_unsigned_t<From>; + using ToU = std::make_unsigned_t<To>; + return FromU(x) > ToU(Hi) ? Hi : To(x); // assumes Hi >= 0 + } +} +#endif // __cpp_lib_saturation_arithmetic + +} // namespace q26 + +QT_END_NAMESPACE + +#endif /* Q26NUMERIC_H */ diff --git a/src/corelib/global/qnumeric.cpp b/src/corelib/global/qnumeric.cpp index a46039c5da5..16b2b7ca408 100644 --- a/src/corelib/global/qnumeric.cpp +++ b/src/corelib/global/qnumeric.cpp @@ -458,39 +458,4 @@ Q_CORE_EXPORT quint64 qFloatDistance(double a, double b) Returns true if the absolute value of \a f is within 0.00001f of 0.0. */ -namespace QtNumericTests { - -template <typename T> static constexpr T max = std::numeric_limits<T>::max(); -template <typename T> static constexpr T min = std::numeric_limits<T>::min(); - -static_assert(qt_saturate<short>(max<unsigned>) == max<short>); -static_assert(qt_saturate<int>(max<unsigned>) == max<int>); -static_assert(qt_saturate<qint64>(max<unsigned>) == qint64(max<unsigned>)); - -static_assert(qt_saturate<short>(max<int>) == max<short>); -static_assert(qt_saturate<unsigned>(max<int>) == unsigned(max<int>)); -static_assert(qt_saturate<qint64>(max<int>) == qint64(max<int>)); - -static_assert(qt_saturate<short>(max<qint64>) == max<short>); -static_assert(qt_saturate<int>(max<qint64>) == max<int>); -static_assert(qt_saturate<unsigned>(max<qint64>) == max<unsigned>); -static_assert(qt_saturate<quint64>(max<qint64>) == quint64(max<qint64>)); - -static_assert(qt_saturate<short>(max<quint64>) == max<short>); -static_assert(qt_saturate<int>(max<quint64>) == max<int>); -static_assert(qt_saturate<unsigned>(max<quint64>) == max<unsigned>); -static_assert(qt_saturate<qint64>(max<quint64>) == max<qint64>); - -static_assert(qt_saturate<short>(min<int>) == min<short>); -static_assert(qt_saturate<qint64>(min<int>) == qint64(min<int>)); -static_assert(qt_saturate<unsigned>(min<int>) == 0); -static_assert(qt_saturate<quint64>(min<int>) == 0); - -static_assert(qt_saturate<short>(min<qint64>) == min<short>); -static_assert(qt_saturate<int>(min<qint64>) == min<int>); -static_assert(qt_saturate<unsigned>(min<qint64>) == 0); -static_assert(qt_saturate<quint64>(min<qint64>) == 0); - -} // namespace QtNumericTests - QT_END_NAMESPACE diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h index 1b88c28974b..8414a681fc7 100644 --- a/src/corelib/global/qnumeric_p.h +++ b/src/corelib/global/qnumeric_p.h @@ -23,9 +23,7 @@ #include <limits> #include <type_traits> -#ifdef __cpp_lib_saturation_arithmetic -# include <numeric> // for saturate_cast -#endif +#include <QtCore/q26numeric.h> // temporarily, for saturate_cast #ifndef __has_extension # define __has_extension(X) 0 @@ -435,43 +433,10 @@ template <auto V2, typename T> bool mul_overflow(T v1, T *r) } #endif // Q_QDOC -/* - Safely narrows \a x to \c{To}. Let \c L be - \c{std::numeric_limit<To>::min()} and \c H be \c{std::numeric_limit<To>::max()}. - - If \a x is less than L, returns L. If \a x is greater than H, - returns H. Otherwise, returns \c{To(x)}. -*/ template <typename To, typename From> static constexpr auto qt_saturate(From x) { -#ifdef __cpp_lib_saturation_arithmetic - return std::saturate_cast<To>(x); -#else - static_assert(std::is_integral_v<To>); - static_assert(std::is_integral_v<From>); - - [[maybe_unused]] - constexpr auto Lo = (std::numeric_limits<To>::min)(); - constexpr auto Hi = (std::numeric_limits<To>::max)(); - - if constexpr (std::is_signed_v<From> == std::is_signed_v<To>) { - // same signedness, we can accept regular integer conversion rules - return x < Lo ? Lo : - x > Hi ? Hi : - /*else*/ To(x); - } else { - if constexpr (std::is_signed_v<From>) { // ie. !is_signed_v<To> - if (x < From{0}) - return To{0}; - } - - // from here on, x >= 0 - using FromU = std::make_unsigned_t<From>; - using ToU = std::make_unsigned_t<To>; - return FromU(x) > ToU(Hi) ? Hi : To(x); // assumes Hi >= 0 - } -#endif // __cpp_lib_saturation_arithmetic + return q26::saturate_cast<To>(x); } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp index b6619d3c4d2..f022b3b463b 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.cpp +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -9,7 +9,8 @@ #include <private/qthread_p.h> #include <private/qcoreapplication_p.h> #include <private/qfreelist_p.h> -#include <private/qnumeric_p.h> + +#include <QtCore/q26numeric.h> QT_BEGIN_NAMESPACE @@ -59,7 +60,7 @@ template <typename T> static T fromDuration(std::chrono::nanoseconds interval) { using namespace std::chrono; qint64 value = ceil<milliseconds>(interval).count(); - return qt_saturate<T>(value); + return q26::saturate_cast<T>(value); } #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp index 2573cdc2c8f..0bfafacf219 100644 --- a/src/corelib/kernel/qeventdispatcher_glib.cpp +++ b/src/corelib/kernel/qeventdispatcher_glib.cpp @@ -4,7 +4,6 @@ #include "qeventdispatcher_glib_p.h" #include "qeventdispatcher_unix_p.h" -#include <private/qnumeric_p.h> #include <private/qthread_p.h> #include "qcoreapplication.h" @@ -12,6 +11,8 @@ #include <QtCore/qlist.h> +#include <QtCore/q26numeric.h> + #include <glib.h> using namespace std::chrono; @@ -104,7 +105,7 @@ static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout) } auto remaining = src->timerList.timerWait().value_or(-1ms); - *timeout = qt_saturate<gint>(ceil<milliseconds>(remaining).count()); + *timeout = q26::saturate_cast<gint>(ceil<milliseconds>(remaining).count()); return (*timeout == 0); } diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index d756fe2773e..8ab1dc139fd 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -32,6 +32,7 @@ #include <stdlib.h> #include <algorithm> +#include <QtCore/q26numeric.h> #ifdef Q_OS_WIN # if !defined(QT_BOOTSTRAPPED) && (defined(QT_NO_CAST_FROM_ASCII) || defined(QT_NO_CAST_FROM_BYTEARRAY)) @@ -728,7 +729,7 @@ QByteArray qCompress(const uchar* data, qsizetype nbytes, int compressionLevel) if (out.data() == nullptr) // allocation failed return tooMuchData(ZLibOp::Compression); - qToBigEndian(qt_saturate<CompressSizeHint_t>(nbytes), out.data()); + qToBigEndian(q26::saturate_cast<CompressSizeHint_t>(nbytes), out.data()); out.size = HeaderSize; return xxflate(ZLibOp::Compression, std::move(out), {data, nbytes}, diff --git a/src/corelib/text/qstringconverter.cpp b/src/corelib/text/qstringconverter.cpp index 57e0e30d9c8..e411990a456 100644 --- a/src/corelib/text/qstringconverter.cpp +++ b/src/corelib/text/qstringconverter.cpp @@ -25,7 +25,7 @@ #ifndef QT_BOOTSTRAPPED #include <QtCore/qvarlengtharray.h> #include <QtCore/q20iterator.h> -#include <QtCore/private/qnumeric_p.h> +#include <QtCore/q26numeric.h> #endif // !QT_BOOTSTRAPPED #endif @@ -1385,12 +1385,12 @@ QString QLocal8Bit::convertToUnicode_sys(QByteArrayView in, quint32 codePage, // Need it in this scope, since we try to decrease our window size if we // encounter an error - int nextIn = qt_saturate<int>(mblen); + int nextIn = q26::saturate_cast<int>(mblen); while (mblen > 0) { std::tie(out, outlen) = growOut(1); // Need space for at least one character if (!out) return {}; - const int nextOut = qt_saturate<int>(outlen); + const int nextOut = q26::saturate_cast<int>(outlen); int len = MultiByteToWideChar(codePage, MB_ERR_INVALID_CHARS, mb, nextIn, out, nextOut); if (len) { mb += nextIn; @@ -1450,7 +1450,7 @@ QString QLocal8Bit::convertToUnicode_sys(QByteArrayView in, quint32 codePage, break; } } - nextIn = qt_saturate<int>(mblen); + nextIn = q26::saturate_cast<int>(mblen); } if (sp.isEmpty()) { @@ -1572,7 +1572,7 @@ QByteArray QLocal8Bit::convertFromUnicode_sys(QStringView in, quint32 codePage, }; const auto getNextWindowSize = [&]() { - int nextIn = qt_saturate<int>(uclen); + int nextIn = q26::saturate_cast<int>(uclen); // The Windows API has some issues if the current window ends in the // middle of a surrogate pair, so we avoid that: if (nextIn > 1 && QChar::isHighSurrogate(ch[nextIn - 1])) @@ -1586,7 +1586,7 @@ QByteArray QLocal8Bit::convertFromUnicode_sys(QStringView in, quint32 codePage, std::tie(out, outlen) = growOut(1); // We need at least one byte if (!out) return {}; - const int nextOut = qt_saturate<int>(outlen); + const int nextOut = q26::saturate_cast<int>(outlen); len = WideCharToMultiByte(codePage, 0, ch, nextIn, out, nextOut, nullptr, nullptr); if (len > 0) { ch += nextIn; diff --git a/src/testlib/qtestresult.cpp b/src/testlib/qtestresult.cpp index fd4a9fd2f75..a9f42cbf5e2 100644 --- a/src/testlib/qtestresult.cpp +++ b/src/testlib/qtestresult.cpp @@ -12,10 +12,9 @@ #include <QtTest/qtestassert.h> #include <QtTest/qtesteventloop.h> -#include <QtCore/private/qnumeric_p.h> - #include <climits> #include <cwchar> +#include <QtCore/q26numeric.h> #include <stdlib.h> #include <stdio.h> @@ -338,7 +337,7 @@ static int approx_wide_len(const char *s) auto r = std::mbsrtowcs(nullptr, &s, INT_MAX, &state); if (r == size_t(-1)) // encoding error, fall back to strlen() r = strlen(s); // `s` was not advanced since `dst == nullptr` - return qt_saturate<int>(r); + return q26::saturate_cast<int>(r); } // Overload to format failures for "const char *" - no need to strdup(). diff --git a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp index d21fabd74e4..d7a2ded57a6 100644 --- a/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp +++ b/tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp @@ -10,6 +10,8 @@ #include <math.h> #include <float.h> +#include <QtCore/q26numeric.h> + namespace { template <typename F> struct Fuzzy {}; /* Data taken from qglobal.h's implementation of qFuzzyCompare: @@ -750,5 +752,40 @@ void tst_QNumeric::signedOverflow() QCOMPARE(qMulOverflow(maxInt, maxInt, &r), true); } +namespace SaturateCastTest { + +template <typename T> static constexpr T max = std::numeric_limits<T>::max(); +template <typename T> static constexpr T min = std::numeric_limits<T>::min(); + +static_assert(q26::saturate_cast<short>(max<unsigned>) == max<short>); +static_assert(q26::saturate_cast<int>(max<unsigned>) == max<int>); +static_assert(q26::saturate_cast<qint64>(max<unsigned>) == qint64(max<unsigned>)); + +static_assert(q26::saturate_cast<short>(max<int>) == max<short>); +static_assert(q26::saturate_cast<unsigned>(max<int>) == unsigned(max<int>)); +static_assert(q26::saturate_cast<qint64>(max<int>) == qint64(max<int>)); + +static_assert(q26::saturate_cast<short>(max<qint64>) == max<short>); +static_assert(q26::saturate_cast<int>(max<qint64>) == max<int>); +static_assert(q26::saturate_cast<unsigned>(max<qint64>) == max<unsigned>); +static_assert(q26::saturate_cast<quint64>(max<qint64>) == quint64(max<qint64>)); + +static_assert(q26::saturate_cast<short>(max<quint64>) == max<short>); +static_assert(q26::saturate_cast<int>(max<quint64>) == max<int>); +static_assert(q26::saturate_cast<unsigned>(max<quint64>) == max<unsigned>); +static_assert(q26::saturate_cast<qint64>(max<quint64>) == max<qint64>); + +static_assert(q26::saturate_cast<short>(min<int>) == min<short>); +static_assert(q26::saturate_cast<qint64>(min<int>) == qint64(min<int>)); +static_assert(q26::saturate_cast<unsigned>(min<int>) == 0); +static_assert(q26::saturate_cast<quint64>(min<int>) == 0); + +static_assert(q26::saturate_cast<short>(min<qint64>) == min<short>); +static_assert(q26::saturate_cast<int>(min<qint64>) == min<int>); +static_assert(q26::saturate_cast<unsigned>(min<qint64>) == 0); +static_assert(q26::saturate_cast<quint64>(min<qint64>) == 0); + +} // namespace SaturateCastTest + QTEST_APPLESS_MAIN(tst_QNumeric) #include "tst_qnumeric.moc" |