summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2023-09-11 08:55:38 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-09-15 09:44:00 +0000
commite8514f771d5b79298419f4b78cc5d3b0dc0af48c (patch)
treebcfe8d46c163453c291e692fe43bca427d5f27cf
parenta1a5e12ef908e900a0ec600cd1904e797fcab9c4 (diff)
Long live Q_(U)INT128_C()!
Compilers that support 128-bit integer types usually don't have support for 128-bit literals, so provide Q_(U)INT128_C macros and back them with UDLs. This, of course, only works in C++, so until compilers provide built-in literals that support C, too, that's all we get. [ChangeLog][QtCore] Added Q_INT128_C() and Q_UINT128_C() macros to create qint128 and quint128 literals in a platform-independent way. Fixes: QTBUG-116822 Change-Id: I4be645baf2e007ee1aa1a27f9b5166671806dc49 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> (cherry picked from commit 16433a4a6ed22750adfdb9633149c3bd485c4656) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> (cherry picked from commit 321dcd1d1d0fdd627160e882c471529baa6cafa7)
-rw-r--r--src/corelib/global/qtypes.cpp50
-rw-r--r--src/corelib/global/qtypes.h72
-rw-r--r--tests/auto/corelib/global/qglobal/tst_qglobal.cpp129
3 files changed, 243 insertions, 8 deletions
diff --git a/src/corelib/global/qtypes.cpp b/src/corelib/global/qtypes.cpp
index 507fd6bbde..19a7541ed1 100644
--- a/src/corelib/global/qtypes.cpp
+++ b/src/corelib/global/qtypes.cpp
@@ -152,7 +152,9 @@ 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 Q_INT128_MIN, Q_INT128_MAX, quint128, QT_SUPPORTS_INT128
+ Literals of this type can be created using the Q_INT128_C() macro.
+
+ \sa Q_INT128_C(), Q_INT128_MIN, Q_INT128_MAX, quint128, QT_SUPPORTS_INT128
*/
/*!
@@ -163,7 +165,9 @@ 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 Q_UINT128_MAX, qint128, QT_SUPPORTS_INT128
+ Literals of this type can be created using the Q_UINT128_C() macro.
+
+ \sa Q_UINT128_C(), Q_UINT128_MAX, qint128, QT_SUPPORTS_INT128
*/
/*!
@@ -174,7 +178,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, Q_INT128_MIN, Q_INT128_MAX, Q_UINT128_MAX
+ \sa qint128, quint128, Q_INT128_C(), Q_UINT128_C(), Q_INT128_MIN, Q_INT128_MAX, Q_UINT128_MAX
*/
/*!
@@ -360,7 +364,7 @@ QT_BEGIN_NAMESPACE
\snippet code/src_corelib_global_qglobal.cpp 8
- \sa qint64, Q_UINT64_C()
+ \sa qint64, Q_UINT64_C(), Q_INT128_C()
*/
/*! \macro quint64 Q_UINT64_C(literal)
@@ -373,7 +377,37 @@ QT_BEGIN_NAMESPACE
\snippet code/src_corelib_global_qglobal.cpp 9
- \sa quint64, Q_INT64_C()
+ \sa quint64, Q_INT64_C(), Q_UINT128_C()
+*/
+
+/*!
+ \macro qint128 Q_INT128_C(literal)
+ \relates <QtTypes>
+ \since 6.6
+
+ Wraps the signed 128-bit integer \a literal in a
+ platform-independent way.
+
+ \note Unlike Q_INT64_C(), this macro is only available in C++, not in C.
+ This is because compilers do not provide these literals as built-ins and C
+ does not have support for user-defined literals.
+
+ \sa qint128, Q_UINT128_C(), Q_INT128_MIN, Q_INT128_MAX, Q_INT64_C(), QT_SUPPORTS_INT128
+*/
+
+/*!
+ \macro quint128 Q_UINT128_C(literal)
+ \relates <QtTypes>
+ \since 6.6
+
+ Wraps the unsigned 128-bit integer \a literal in a
+ platform-independent way.
+
+ \note Unlike Q_UINT64_C(), this macro is only available in C++, not in C.
+ This is because compilers do not provide these literals as built-ins and C
+ does not have support for user-defined literals.
+
+ \sa quint128, Q_INT128_C(), Q_UINT128_MAX, Q_UINT64_C(), QT_SUPPORTS_INT128
*/
/*!
@@ -389,7 +423,7 @@ QT_BEGIN_NAMESPACE
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
+ \sa Q_INT128_MAX, quint128, Q_UINT128_C, QT_SUPPORTS_INT128
*/
/*!
@@ -402,7 +436,7 @@ QT_BEGIN_NAMESPACE
This macro is available in both C++ and C modes.
- \sa Q_INT128_MAX, qint128, QT_SUPPORTS_INT128
+ \sa Q_INT128_MAX, qint128, Q_INT128_C, QT_SUPPORTS_INT128
*/
/*!
@@ -415,7 +449,7 @@ QT_BEGIN_NAMESPACE
This macro is available in both C++ and C modes.
- \sa Q_INT128_MIN, Q_UINT128_MAX, qint128, QT_SUPPORTS_INT128
+ \sa Q_INT128_MIN, Q_UINT128_MAX, qint128, Q_INT128_C, QT_SUPPORTS_INT128
*/
// Statically check assumptions about the environment we're running
diff --git a/src/corelib/global/qtypes.h b/src/corelib/global/qtypes.h
index cca2263901..41e6025735 100644
--- a/src/corelib/global/qtypes.h
+++ b/src/corelib/global/qtypes.h
@@ -7,6 +7,7 @@
#include <QtCore/qprocessordetection.h>
#include <QtCore/qtconfigmacros.h>
+#include <QtCore/qassert.h>
#ifdef __cplusplus
# include <cstddef>
@@ -80,6 +81,77 @@ __extension__ typedef __uint128_t quint128;
# define Q_INT128_MAX QT_C_STYLE_CAST(qint128, (Q_UINT128_MAX / 2))
# define Q_INT128_MIN (-Q_INT128_MAX - 1)
+# ifdef __cplusplus
+ namespace QtPrivate::NumberLiterals {
+ namespace detail {
+ template <quint128 accu, int base>
+ constexpr quint128 construct() { return accu; }
+
+ template <quint128 accu, int base, char C, char...Cs>
+ constexpr quint128 construct()
+ {
+ if constexpr (C != '\'') { // ignore digit separators
+ const int digitValue = '0' <= C && C <= '9' ? C - '0' :
+ 'a' <= C && C <= 'z' ? C - 'a' + 10 :
+ 'A' <= C && C <= 'Z' ? C - 'A' + 10 :
+ /* else */ -1 ;
+ static_assert(digitValue >= 0 && digitValue < base,
+ "Invalid character");
+ // accu * base + digitValue <= MAX, but without overflow:
+ static_assert(accu <= (Q_UINT128_MAX - digitValue) / base,
+ "Overflow occurred");
+ return construct<accu * base + digitValue, base, Cs...>();
+ } else {
+ return construct<accu, base, Cs...>();
+ }
+ }
+
+ template <char C, char...Cs>
+ constexpr quint128 parse0xb()
+ {
+ constexpr quint128 accu = 0;
+ if constexpr (C == 'x' || C == 'X')
+ return construct<accu, 16, Cs...>(); // base 16, skip 'x'
+ else if constexpr (C == 'b' || C == 'B')
+ return construct<accu, 2, Cs...>(); // base 2, skip 'b'
+ else
+ return construct<accu, 8, C, Cs...>(); // base 8, include C
+ }
+
+ template <char...Cs>
+ constexpr quint128 parse0()
+ {
+ if constexpr (sizeof...(Cs) == 0) // this was just a literal 0
+ return 0;
+ else
+ return parse0xb<Cs...>();
+ }
+
+ template <char C, char...Cs>
+ constexpr quint128 parse()
+ {
+ if constexpr (C == '0')
+ return parse0<Cs...>(); // base 2, 8, or 16 (or just a literal 0), skip '0'
+ else
+ return construct<0, 10, C, Cs...>(); // initial accu 0, base 10, include C
+ }
+ } // namespace detail
+ template <char...Cs>
+ constexpr quint128 operator""_quint128() noexcept
+ { return QtPrivate::NumberLiterals::detail::parse<Cs...>(); }
+ template <char...Cs>
+ constexpr qint128 operator""_qint128() noexcept
+ { return qint128(QtPrivate::NumberLiterals::detail::parse<Cs...>()); }
+
+ #ifndef Q_UINT128_C // allow qcompilerdetection.h/user override
+ # define Q_UINT128_C(c) ([]{ using namespace QtPrivate::NumberLiterals; return c ## _quint128; }())
+ #endif
+ #ifndef Q_INT128_C // allow qcompilerdetection.h/user override
+ # define Q_INT128_C(c) ([]{ using namespace QtPrivate::NumberLiterals; return c ## _qint128; }())
+ #endif
+
+ } // namespace QtPrivate::NumberLiterals
+# endif // __cplusplus
#endif // QT_SUPPORTS_INT128
#ifndef __cplusplus
diff --git a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
index edfa59ebf1..bc96c0d9f0 100644
--- a/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
+++ b/tests/auto/corelib/global/qglobal/tst_qglobal.cpp
@@ -497,6 +497,135 @@ void tst_QGlobal::int128Literals()
QCOMPARE_EQ(tst_qint128_min(), Q_INT128_MIN);
QCOMPARE_EQ(tst_qint128_max(), Q_INT128_MAX);
QCOMPARE_EQ(tst_quint128_max(), Q_UINT128_MAX);
+ {
+ #define CHECK_S(x) COMPARE_EQ(Q_INT128_C(x), Q_INT64_C(x), qint128)
+ #define CHECK_U(x) COMPARE_EQ(Q_UINT128_C(x), Q_UINT64_C(x), quint128);
+ #define CHECK(x) do { CHECK_S(x); CHECK_U(x); } while (0)
+ // basics:
+ CHECK(0);
+ CHECK(1);
+ CHECK_S(-1);
+ QCOMPARE_EQ(Q_INT64_C(9223372036854775807), std::numeric_limits<qint64>::max());
+ CHECK(9223372036854775807); // LLONG_MAX
+ // Q_INT64_C(-9223372036854775808) gives -Wimplicitly-unsigned-literal on GCC, so use numeric_limits:
+ {
+ constexpr auto i = Q_INT128_C(-9223372036854775808); // LLONG_MIN
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ QCOMPARE_EQ(i, std::numeric_limits<qint64>::min());
+ }
+ // actual 128-bit numbers
+ {
+ constexpr auto i = Q_INT128_C( 9223372036854775808); // LLONG_MAX + 1
+ constexpr auto u = Q_UINT128_C(9223372036854775808); // LLONG_MAX + 1
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ static_assert(std::is_same_v<decltype(u), const quint128>);
+ QCOMPARE_EQ(i, qint128{ std::numeric_limits<qint64>::max()} + 1);
+ QCOMPARE_EQ(u, quint128{std::numeric_limits<qint64>::max()} + 1);
+ }
+ {
+ constexpr auto i = Q_INT128_C(-9223372036854775809); // LLONG_MIN - 1
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ QCOMPARE_EQ(i, qint128{std::numeric_limits<qint64>::min()} - 1);
+ }
+ {
+ constexpr auto i = Q_INT128_C( 18446744073709551616); // ULLONG_MAX + 1
+ constexpr auto u = Q_UINT128_C(18446744073709551616);
+ constexpr auto expected = qint128{1} << 64;
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ static_assert(std::is_same_v<decltype(expected), const qint128>);
+ static_assert(std::is_same_v<decltype(u), const quint128>);
+ QCOMPARE_EQ(i, expected);
+ QCOMPARE_EQ(u, quint128{expected});
+ }
+ {
+ // compilers don't let one write signed _MIN literals, so use MIN + 1:
+ // Q_INT128_C(-170141183460469231731687303715884105728) gives
+ // ERROR: ~~~ outside range of representable values of type qint128
+ // This is because the unary minus is technically speaking not part of
+ // the literal, but called on the result of the literal.
+ constexpr auto i = Q_INT128_C(-170141183460469231731687303715884105727); // 128-bit MIN + 1
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ QCOMPARE_EQ(i, std::numeric_limits<qint128>::min() + 1);
+ }
+ {
+ constexpr auto i = Q_INT128_C( 170141183460469231731687303715884105727); // MAX
+ constexpr auto u = Q_UINT128_C(340282366920938463463374607431768211455); // UMAX
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ static_assert(std::is_same_v<decltype(u), const quint128>);
+ QCOMPARE_EQ(i, std::numeric_limits<qint128>::max());
+ QCOMPARE_EQ(u, std::numeric_limits<quint128>::max());
+ QCOMPARE_EQ(u, Q_UINT128_C(-1));
+ }
+
+ // binary literals:
+ CHECK(0b0);
+ CHECK(0b1);
+ CHECK_S(-0b1);
+ CHECK(0b01);
+ CHECK(0b10);
+ CHECK(0b1'1); // with digit separator
+ CHECK(0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
+ //bytes |---1---| |---2---| |---3---| |---4---| |---5---| |---6---| |---7---| |---8---|
+ {
+ // bytes: |---1---| |---2---| |---3---| |---4---| |---5---| |---6---| |---7---| |---8---| |---9---| |--10---| |--11---| |--12---| |--13---| |--14---| |--15---| |--16---|
+ constexpr auto i = Q_INT128_C( 0b0111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
+ constexpr auto u = Q_UINT128_C(0b1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111'1111);
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ static_assert(std::is_same_v<decltype(u), const quint128>);
+ QCOMPARE_EQ(i, std::numeric_limits<qint128>::max());
+ QCOMPARE_EQ(u, std::numeric_limits<quint128>::max());
+ QCOMPARE_EQ(u, Q_UINT128_C(-0b1));
+ }
+
+ // octal literals:
+ CHECK(00);
+ CHECK(01);
+ CHECK(02);
+ CHECK(03);
+ CHECK(04);
+ CHECK(05);
+ CHECK(06);
+ CHECK(07);
+ CHECK_S(-01);
+ CHECK(010);
+ CHECK_S(-01'0); // with digit separator
+ CHECK(07'7777'7777'7777'7777'7777); // LLONG_MAX
+ {
+ // bits: 120| 108| 96| 84| 72| 60| 48| 36| 24| 12| 0|
+ constexpr auto i = Q_INT128_C( 0177'7777'7777'7777'7777'7777'7777'7777'7777'7777'7777);
+ constexpr auto u = Q_UINT128_C(0377'7777'7777'7777'7777'7777'7777'7777'7777'7777'7777);
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ static_assert(std::is_same_v<decltype(u), const quint128>);
+ QCOMPARE_EQ(i, std::numeric_limits<qint128>::max());
+ QCOMPARE_EQ(u, std::numeric_limits<quint128>::max());
+ QCOMPARE_EQ(u, Q_UINT128_C(-01));
+ }
+
+ // hex literals:
+ CHECK(0x0);
+ CHECK(0x1);
+ CHECK(0x9);
+ CHECK(0xA);
+ CHECK(0xB);
+ CHECK(0xC);
+ CHECK(0xD);
+ CHECK(0xE);
+ CHECK(0x0F);
+ CHECK(0x10);
+ CHECK_S(-0x1);
+ CHECK_S(-0x1'0); // with digit separator
+ CHECK(0x7FFF'FFFF'FFFF'FFFF);
+ {
+ constexpr auto i = Q_INT128_C( 0x7FFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFF);
+ constexpr auto u = Q_UINT128_C(0xFFFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFF'FFFF);
+ static_assert(std::is_same_v<decltype(i), const qint128>);
+ static_assert(std::is_same_v<decltype(u), const quint128>);
+ QCOMPARE_EQ(i, std::numeric_limits<qint128>::max());
+ QCOMPARE_EQ(u, std::numeric_limits<quint128>::max());
+ QCOMPARE_EQ(Q_UINT128_C(-1), u);
+ }
+ #undef CHECK
+ }
#undef COMPARE_EQ
#else
QSKIP("This test requires 128-bit integer support enabled in the compiler.");