summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiuseppe D'Angelo <[email protected]>2024-08-01 11:17:03 +0200
committerGiuseppe D'Angelo <[email protected]>2024-08-18 11:14:00 +0200
commit7447ad503330ed176cf369792ffb33b7e00a58d3 (patch)
tree297b9873f95106cd8af4b1cfd60e5f3b5a64b701
parent47fd38be4bce0958fcfce8080d1580c4e3c2a15b (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.txt1
-rw-r--r--src/corelib/global/q26numeric.h69
-rw-r--r--src/corelib/global/qnumeric.cpp35
-rw-r--r--src/corelib/global/qnumeric_p.h39
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.cpp5
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib.cpp5
-rw-r--r--src/corelib/text/qbytearray.cpp3
-rw-r--r--src/corelib/text/qstringconverter.cpp12
-rw-r--r--src/testlib/qtestresult.cpp5
-rw-r--r--tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp37
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"