QDataStream: allow streaming of enums backed by long and unsigned long

On Unix 64-bit platforms (LP64), compilers will use long and unsigned
long as the underlying type of 64-bit enumerations without explicit
type, such as:
  enum E { V = 0x1'0000'0000 };

User code may also explicitly choose std::int64_t or std::uint64_t,
which on those platforms map to long and unsigned long, respectively.
Likewise std::ptrdiff_t and std::size_t, which are the same.

This means we do allow streaming of enums with explicit long and
unsigned long types, such as:
  enum ELong : long { A, B, C };
which change sizes between platforms.

[ChangeLog][QtCore][QDataStream] QDataStream now supports streaming
enumerations whose underlying type is long or unsigned long (like
std::int64_t, std::uint64_t, ptrdiff_t, size_t on 64-bit Unix
platforms).

Fixes: QTBUG-86424
Change-Id: I34ef9422a252ce3df468fffd4ba074cddea20005
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2024-08-12 18:26:09 -07:00
parent b387537d3e
commit 0c010cd9c7
2 changed files with 46 additions and 2 deletions

View File

@ -525,12 +525,21 @@ inline QDataStream &operator>>(QDataStream &s, QFlags<Enum> &e)
template <typename T>
typename std::enable_if_t<std::is_enum<T>::value, QDataStream &>
operator<<(QDataStream &s, const T &t)
{ return s << static_cast<typename std::underlying_type<T>::type>(t); }
{
// std::underlying_type_t<T> may be long or ulong, for which QDataStream
// provides no streaming operators. For those, cast to qint64 or quint64.
return s << typename QIntegerForSizeof<T>::Unsigned(t);
}
template <typename T>
typename std::enable_if_t<std::is_enum<T>::value, QDataStream &>
operator>>(QDataStream &s, T &t)
{ return s >> reinterpret_cast<typename std::underlying_type<T>::type &>(t); }
{
typename QIntegerForSizeof<T>::Unsigned i;
s >> i;
t = T(i);
return s;
}
#ifndef Q_QDOC

View File

@ -3697,6 +3697,41 @@ void tst_QDataStream::enumTest()
}
ba.clear();
enum class E5 : qint64
{
A,
B,
C
};
{
QDataStream stream(&ba, QIODevice::WriteOnly);
stream << E5::C;
QCOMPARE(ba.size(), int(sizeof(E5)));
}
{
QDataStream stream(ba);
E5 e;
stream >> e;
QCOMPARE(e, E5::C);
}
ba.clear();
// unlike regular streaming operators, we accept long and ulong, because
// usually the only reason people see them is because they have
// std::(u)int64_t underlying types.
enum class ELong : long { A, B, C };
{
QDataStream stream(&ba, QIODevice::WriteOnly);
stream << ELong::A;
QCOMPARE(ba.size(), sizeof(long));
}
{
QDataStream stream(ba);
ELong e;
stream >> e;
QCOMPARE(e, ELong::A);
}
ba.clear();
}
void tst_QDataStream::floatingPointPrecision()