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 <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2023-09-12 23:54:00 +02:00
parent 454636afec
commit 104a0a9ecd
4 changed files with 135 additions and 5 deletions

View File

@ -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 <QtTypes>
\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 <QtTypes>
\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 <QtTypes>
\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<quint128>::max() == qint128(-1));
static_assert(std::numeric_limits<quint128>::max() == Q_UINT128_MAX);
# endif
#endif

View File

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

View File

@ -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()
{

View File

@ -12,8 +12,49 @@
#include <QString>
#include <QtVersion>
#include <array>
#include <cmath>
QT_BEGIN_NAMESPACE
namespace QTest {
#ifdef QT_SUPPORTS_INT128
namespace detail {
char *i128ToStringHelper(std::array<char, 64> &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<qint128>::min()) // -i is not representable, hardcode:
return qstrdup("-170141183460469231731687303715884105728");
std::array<char, 64> 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<char, 64> 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<std::remove_cv_t<decltype(lhs_)>, Expected128>); \
QCOMPARE_EQ(lhs_, rhs); \
} while (0)
COMPARE_EQ(Q_INT128_MIN, std::numeric_limits<qint128>::min(), qint128);
COMPARE_EQ(Q_INT128_MAX, std::numeric_limits<qint128>::max(), qint128);
COMPARE_EQ(Q_UINT128_MAX, std::numeric_limits<quint128>::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<const char *, const char *> stringpair;
Q_DECLARE_METATYPE(stringpair)