From 104a0a9ecdb18d65e4d9075d87e8860c6c9d8335 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 12 Sep 2023 23:54:00 +0200 Subject: Long live Q_(U)INT128_MIN/MAX! Since compilers don't provide such macros, do it ourselves. In order to test these macros, add ad-hoc specializations of QTest::toString() for qint128 and quint128 locally to the test. Turns out it's not too hard to write them, so we might move them to a public header, yet. Pick-to: 6.6 Change-Id: I1483f3af2ccec6038e1c780649f9ffe413bb59ef Reviewed-by: Thiago Macieira --- src/corelib/global/qtypes.cpp | 51 +++++++++++++++-- src/corelib/global/qtypes.h | 15 ++++- tests/auto/corelib/global/qglobal/qglobal.c | 6 ++ tests/auto/corelib/global/qglobal/tst_qglobal.cpp | 68 +++++++++++++++++++++++ 4 files changed, 135 insertions(+), 5 deletions(-) diff --git a/src/corelib/global/qtypes.cpp b/src/corelib/global/qtypes.cpp index 7d34f038f2..507fd6bbde 100644 --- a/src/corelib/global/qtypes.cpp +++ b/src/corelib/global/qtypes.cpp @@ -152,7 +152,7 @@ QT_BEGIN_NAMESPACE Typedef for \c{__int128} on platforms that support it (Qt defines the macro \l QT_SUPPORTS_INT128 if this is the case). - \sa quint128, QT_SUPPORTS_INT128 + \sa Q_INT128_MIN, Q_INT128_MAX, quint128, QT_SUPPORTS_INT128 */ /*! @@ -163,7 +163,7 @@ QT_BEGIN_NAMESPACE Typedef for \c{unsigned __int128} on platforms that support it (Qt defines the macro \l QT_SUPPORTS_INT128 if this is the case). - \sa qint128, QT_SUPPORTS_INT128 + \sa Q_UINT128_MAX, qint128, QT_SUPPORTS_INT128 */ /*! @@ -174,7 +174,7 @@ QT_BEGIN_NAMESPACE Qt defines this macro as well as the \l qint128 and \l quint128 types if the platform has support for 128-bit integer types. - \sa qint128, quint128 + \sa qint128, quint128, Q_INT128_MIN, Q_INT128_MAX, Q_UINT128_MAX */ /*! @@ -376,6 +376,48 @@ QT_BEGIN_NAMESPACE \sa quint64, Q_INT64_C() */ +/*! + \macro Q_UINT128_MAX + \relates + \since 6.6 + + This macro expands to a compile-time constant representing the + maximum value representable in a \l quint128. + + This macro is available in both C++ and C modes. + + The minimum of \l quint128 is 0 (zero), so a \c{Q_UINT128_MIN} is neither + needed nor provided. + + \sa Q_INT128_MAX, quint128, QT_SUPPORTS_INT128 +*/ + +/*! + \macro Q_INT128_MIN + \relates + \since 6.6 + + This macro expands to a compile-time constant representing the + minimum value representable in a \l qint128. + + This macro is available in both C++ and C modes. + + \sa Q_INT128_MAX, qint128, QT_SUPPORTS_INT128 +*/ + +/*! + \macro Q_INT128_MAX + \relates + \since 6.6 + + This macro expands to a compile-time constant representing the + maximum value representable in a \l qint128. + + This macro is available in both C++ and C modes. + + \sa Q_INT128_MIN, Q_UINT128_MAX, qint128, QT_SUPPORTS_INT128 +*/ + // Statically check assumptions about the environment we're running // in. The idea here is to error or warn if otherwise implicit Qt // assumptions are not fulfilled on new hardware or compilers @@ -439,9 +481,10 @@ static_assert(sizeof(qint128) == 16, "Internal error, qint128 is misdefined"); #ifdef QT_SUPPORTS_INT128 // check that numeric_limits works: // This fails here for GCC 9, but succeeds on Clang and GCC >= 11 +// However, all tests in tst_qglobal::int128Literals() pass for GCC 9, too, // so just suppress the check for older GCC: # if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 1100 -static_assert(std::numeric_limits::max() == qint128(-1)); +static_assert(std::numeric_limits::max() == Q_UINT128_MAX); # endif #endif diff --git a/src/corelib/global/qtypes.h b/src/corelib/global/qtypes.h index 6a4b6acf39..cca2263901 100644 --- a/src/corelib/global/qtypes.h +++ b/src/corelib/global/qtypes.h @@ -67,7 +67,20 @@ typedef quint64 qulonglong; #if defined(QT_SUPPORTS_INT128) __extension__ typedef __int128_t qint128; __extension__ typedef __uint128_t quint128; -#endif + +// limits: +# ifdef __cplusplus /* need to avoid c-style-casts in C++ mode */ +# define QT_C_STYLE_CAST(type, x) static_cast(x) +# else /* but C doesn't have constructor-style casts */ +# define QT_C_STYLE_CAST(type, x) ((type)x) +# endif +# ifndef Q_UINT128_MAX /* allow qcompilerdetection.h/user override */ +# define Q_UINT128_MAX QT_C_STYLE_CAST(quint128, -1) +# endif +# define Q_INT128_MAX QT_C_STYLE_CAST(qint128, (Q_UINT128_MAX / 2)) +# define Q_INT128_MIN (-Q_INT128_MAX - 1) + +#endif // QT_SUPPORTS_INT128 #ifndef __cplusplus // In C++ mode, we define below using QIntegerForSize template diff --git a/tests/auto/corelib/global/qglobal/qglobal.c b/tests/auto/corelib/global/qglobal/qglobal.c index c8dbbc318f..19f2e0f3b9 100644 --- a/tests/auto/corelib/global/qglobal/qglobal.c +++ b/tests/auto/corelib/global/qglobal/qglobal.c @@ -59,6 +59,12 @@ void tst_GlobalTypes() #endif /* QT_SUPPORTS_INT128 */ } +#if QT_SUPPORTS_INT128 +qint128 tst_qint128_min() { return Q_INT128_MIN + 0; } +qint128 tst_qint128_max() { return 0 + Q_INT128_MAX; } +quint128 tst_quint128_max() { return Q_UINT128_MAX - 1 + 1; } +#endif + /* Qt version */ int tst_QtVersion() { diff --git a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp index cf37b1ecb8..638c43f98a 100644 --- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp +++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp @@ -12,8 +12,49 @@ #include #include +#include #include +QT_BEGIN_NAMESPACE +namespace QTest { +#ifdef QT_SUPPORTS_INT128 +namespace detail { + char *i128ToStringHelper(std::array &buffer, quint128 n) + { + auto dst = buffer.data() + buffer.size(); + *--dst = '\0'; // NUL-terminate + if (n == 0) { + *--dst = '0'; // and done + } else { + while (n != 0) { + *--dst = "0123456789"[n % 10]; + n /= 10; + } + } + return dst; + } +} +template <> +char *toString(const qint128 &i) +{ + if (i == std::numeric_limits::min()) // -i is not representable, hardcode: + return qstrdup("-170141183460469231731687303715884105728"); + std::array buffer; + auto dst = detail::i128ToStringHelper(buffer, i < 0 ? -i : i); + if (i < 0) + *--dst = '-'; + return qstrdup(dst); +} +template <> +char *toString(const quint128 &i) +{ + std::array buffer; + return qstrdup(detail::i128ToStringHelper(buffer, i)); +} +#endif // QT_SUPPORTS_INT128 +} // namespace QTest +QT_END_NAMESPACE + class tst_QGlobal: public QObject { Q_OBJECT @@ -30,6 +71,7 @@ private slots: void qCoreAppStartupFunction(); void qCoreAppStartupFunctionRestart(); void integerForSize(); + void int128Literals(); void buildAbiEndianness(); void testqOverload(); void testqMinMax(); @@ -46,6 +88,12 @@ extern "C" { // functions in qglobal.c void tst_GlobalTypes(); int tst_QtVersion(); const char *tst_qVersion(); +#if QT_SUPPORTS_INT128 +qint128 tst_qint128_min(); +qint128 tst_qint128_max(); +quint128 tst_quint128_max(); +#endif + } void tst_QGlobal::cMode() @@ -437,6 +485,26 @@ void tst_QGlobal::integerForSize() #endif } +void tst_QGlobal::int128Literals() +{ +#ifdef QT_SUPPORTS_INT128 +#define COMPARE_EQ(lhs, rhs, Expected128) do { \ + constexpr auto lhs_ = lhs; \ + static_assert(std::is_same_v, Expected128>); \ + QCOMPARE_EQ(lhs_, rhs); \ + } while (0) + COMPARE_EQ(Q_INT128_MIN, std::numeric_limits::min(), qint128); + COMPARE_EQ(Q_INT128_MAX, std::numeric_limits::max(), qint128); + COMPARE_EQ(Q_UINT128_MAX, std::numeric_limits::max(), quint128); + QCOMPARE_EQ(tst_qint128_min(), Q_INT128_MIN); + QCOMPARE_EQ(tst_qint128_max(), Q_INT128_MAX); + QCOMPARE_EQ(tst_quint128_max(), Q_UINT128_MAX); +#undef COMPARE_EQ +#else + QSKIP("This test requires 128-bit integer support enabled in the compiler."); +#endif +} + typedef QPair stringpair; Q_DECLARE_METATYPE(stringpair) -- cgit v1.2.3