From 0c010cd9c792859551f6d0e34d413809cbf4ae74 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 12 Aug 2024 18:26:09 -0700 Subject: [PATCH] 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 --- src/corelib/serialization/qdatastream.h | 13 +++++-- .../qdatastream/tst_qdatastream.cpp | 35 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index 2a5622f6030..71f2f4bbd9b 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -525,12 +525,21 @@ inline QDataStream &operator>>(QDataStream &s, QFlags &e) template typename std::enable_if_t::value, QDataStream &> operator<<(QDataStream &s, const T &t) -{ return s << static_cast::type>(t); } +{ + // std::underlying_type_t may be long or ulong, for which QDataStream + // provides no streaming operators. For those, cast to qint64 or quint64. + return s << typename QIntegerForSizeof::Unsigned(t); +} template typename std::enable_if_t::value, QDataStream &> operator>>(QDataStream &s, T &t) -{ return s >> reinterpret_cast::type &>(t); } +{ + typename QIntegerForSizeof::Unsigned i; + s >> i; + t = T(i); + return s; +} #ifndef Q_QDOC diff --git a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp index 9a227c782de..0ff70ffb29a 100644 --- a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp @@ -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()