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. Pick-to: 6.6 6.6.0 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>
This commit is contained in:
parent
9468ef2cfb
commit
16433a4a6e
@ -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
|
||||
|
@ -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
|
||||
|
@ -499,6 +499,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.");
|
||||
|
Loading…
Reference in New Issue
Block a user