Long live 64-bit QFlags
This commit removes the limitation on size that QFlags used to have, allowing up to 64 bits. We could increase to 128 bits at this time, but I'm choosing not to allow this yet, due to limitations in handling 128-bit integers in QMetaObject & QMetaObjectBuilder. The API wouldn't be cross-platform. This commit duplicates the tst_QFlags into a new tst_QFlags64. It restores the tst_QVariant state to how it was before (even though it doesn't actually test the QMetaEnum functionality). [ChangeLog][QtCore][QFlags] Now supports 64-bit enumerations, with the same syntax, including extraction into QMetaObject using the Q_FLAG and Q_DECLARE_FLAGS markers. Note the QFlag helper class remains 32-bit wide. Creation of a 64-bit QFlags from a generic integer can be achieved by passing a std::in_place first argument. Fixes: QTBUG-111926 Change-Id: Ifb754f0e28774c20aa7cfffd17e7fc1d16baf8e4 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io> Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
This commit is contained in:
parent
39bb305416
commit
2effeb25c0
@ -7,6 +7,7 @@
|
||||
#include <QtCore/qcompare_impl.h>
|
||||
#include <QtCore/qtypeinfo.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <initializer_list>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -51,38 +52,76 @@ constexpr inline QIncompatibleFlag::QIncompatibleFlag(int value) noexcept : i(va
|
||||
namespace QtPrivate {
|
||||
template <typename T> struct IsQFlags : std::false_type {};
|
||||
template <typename E> struct IsQFlags<QFlags<E>> : std::true_type {};
|
||||
|
||||
template<typename Enum>
|
||||
class QFlagsStorage
|
||||
{
|
||||
static_assert(sizeof(Enum) <= sizeof(quint64),
|
||||
"Only enumerations 64 bits or smaller are supported.");
|
||||
static_assert((std::is_enum<Enum>::value), "QFlags is only usable on enumeration types.");
|
||||
|
||||
static constexpr size_t IntegerSize = (std::max)(sizeof(Enum), sizeof(int));
|
||||
using Integers = QIntegerForSize<IntegerSize>;
|
||||
|
||||
protected:
|
||||
typedef typename std::conditional<
|
||||
std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
|
||||
typename Integers::Unsigned,
|
||||
typename Integers::Signed
|
||||
>::type Int;
|
||||
|
||||
Int i = 0;
|
||||
|
||||
public:
|
||||
constexpr inline QFlagsStorage() noexcept = default;
|
||||
constexpr inline explicit QFlagsStorage(std::in_place_t, Int v) : i(v) {}
|
||||
};
|
||||
|
||||
template <typename Enum, int Size = sizeof(QFlagsStorage<Enum>)>
|
||||
struct QFlagsStorageHelper : QFlagsStorage<Enum>
|
||||
{
|
||||
using QFlagsStorage<Enum>::QFlagsStorage;
|
||||
};
|
||||
template <typename Enum> struct QFlagsStorageHelper<Enum, sizeof(int)> : QFlagsStorage<Enum>
|
||||
{
|
||||
using QFlagsStorage<Enum>::QFlagsStorage;
|
||||
|
||||
// For compatibility with Qt 3, moc goes through QFlag in order to
|
||||
// read/write properties of type QFlags; so a conversion to QFlag is also
|
||||
// needed here. (It otherwise goes through a QFlags->int->QFlag conversion
|
||||
// sequence.)
|
||||
constexpr inline Q_IMPLICIT QFlagsStorageHelper(QFlag flag) noexcept
|
||||
: QFlagsStorage<Enum>(std::in_place, flag) {}
|
||||
#ifdef QT_TYPESAFE_FLAGS
|
||||
constexpr inline explicit operator QFlag() const noexcept { return QFlag(this->i); }
|
||||
#endif
|
||||
};
|
||||
} // namespace QtPrivate
|
||||
|
||||
template<typename Enum>
|
||||
class QFlags
|
||||
class QFlags : public QtPrivate::QFlagsStorageHelper<Enum>
|
||||
{
|
||||
static_assert((sizeof(Enum) <= sizeof(int)),
|
||||
"QFlags uses an int as storage, so an enum with underlying "
|
||||
"long long will overflow.");
|
||||
static_assert((std::is_enum<Enum>::value), "QFlags is only usable on enumeration types.");
|
||||
|
||||
using Base = QtPrivate::QFlagsStorageHelper<Enum>;
|
||||
public:
|
||||
#if defined(Q_CC_MSVC) || defined(Q_QDOC)
|
||||
// see above for MSVC
|
||||
// the definition below is too complex for qdoc
|
||||
typedef int Int;
|
||||
#else
|
||||
typedef typename std::conditional<
|
||||
std::is_unsigned<typename std::underlying_type<Enum>::type>::value,
|
||||
unsigned int,
|
||||
signed int
|
||||
>::type Int;
|
||||
#endif
|
||||
typedef Enum enum_type;
|
||||
using Int = typename Base::Int;
|
||||
using Base::Base;
|
||||
|
||||
// compiler-generated copy/move ctor/assignment operators are fine!
|
||||
constexpr inline QFlags() noexcept : i(0) {}
|
||||
constexpr inline Q_IMPLICIT QFlags(Enum flags) noexcept : i(Int(flags)) {}
|
||||
constexpr inline Q_IMPLICIT QFlags(QFlag flag) noexcept : i(flag) {}
|
||||
constexpr inline QFlags() noexcept = default;
|
||||
|
||||
constexpr inline Q_IMPLICIT QFlags(Enum flags) noexcept : Base(std::in_place, Int(flags)) {}
|
||||
|
||||
#ifdef Q_QDOC
|
||||
constexpr inline Q_IMPLICIT QFlags(std::in_place_t, Int flags) noexcept;
|
||||
constexpr inline Q_IMPLICIT QFlags(QFlag flag) noexcept
|
||||
requires(sizeof(Enum) == sizeof(int));
|
||||
#endif
|
||||
|
||||
constexpr inline QFlags(std::initializer_list<Enum> flags) noexcept
|
||||
: i(initializer_list_helper(flags.begin(), flags.end())) {}
|
||||
: Base(std::in_place, initializer_list_helper(flags.begin(), flags.end())) {}
|
||||
|
||||
constexpr static inline QFlags fromInt(Int i) noexcept { return QFlags(QFlag(i)); }
|
||||
constexpr static inline QFlags fromInt(Int i) noexcept { return QFlags(std::in_place, i); }
|
||||
constexpr inline Int toInt() const noexcept { return i; }
|
||||
|
||||
#ifndef QT_TYPESAFE_FLAGS
|
||||
@ -99,27 +138,22 @@ public:
|
||||
#ifdef QT_TYPESAFE_FLAGS
|
||||
constexpr inline explicit operator Int() const noexcept { return i; }
|
||||
constexpr inline explicit operator bool() const noexcept { return i; }
|
||||
// For some reason, moc goes through QFlag in order to read/write
|
||||
// properties of type QFlags; so a conversion to QFlag is also
|
||||
// needed here. (It otherwise goes through a QFlags->int->QFlag
|
||||
// conversion sequence.)
|
||||
constexpr inline explicit operator QFlag() const noexcept { return QFlag(i); }
|
||||
#else
|
||||
constexpr inline Q_IMPLICIT operator Int() const noexcept { return i; }
|
||||
constexpr inline bool operator!() const noexcept { return !i; }
|
||||
#endif
|
||||
|
||||
constexpr inline QFlags operator|(QFlags other) const noexcept { return QFlags(QFlag(i | other.i)); }
|
||||
constexpr inline QFlags operator|(Enum other) const noexcept { return QFlags(QFlag(i | Int(other))); }
|
||||
constexpr inline QFlags operator^(QFlags other) const noexcept { return QFlags(QFlag(i ^ other.i)); }
|
||||
constexpr inline QFlags operator^(Enum other) const noexcept { return QFlags(QFlag(i ^ Int(other))); }
|
||||
constexpr inline QFlags operator|(QFlags other) const noexcept { return QFlags(std::in_place, i | other.i); }
|
||||
constexpr inline QFlags operator|(Enum other) const noexcept { return QFlags(std::in_place, i | Int(other)); }
|
||||
constexpr inline QFlags operator^(QFlags other) const noexcept { return QFlags(std::in_place, i ^ other.i); }
|
||||
constexpr inline QFlags operator^(Enum other) const noexcept { return QFlags(std::in_place, i ^ Int(other)); }
|
||||
#ifndef QT_TYPESAFE_FLAGS
|
||||
constexpr inline QFlags operator&(int mask) const noexcept { return QFlags(QFlag(i & mask)); }
|
||||
constexpr inline QFlags operator&(uint mask) const noexcept { return QFlags(QFlag(i & mask)); }
|
||||
constexpr inline QFlags operator&(int mask) const noexcept { return QFlags(std::in_place, i & mask); }
|
||||
constexpr inline QFlags operator&(uint mask) const noexcept { return QFlags(std::in_place, i & mask); }
|
||||
#endif
|
||||
constexpr inline QFlags operator&(QFlags other) const noexcept { return QFlags(QFlag(i & other.i)); }
|
||||
constexpr inline QFlags operator&(Enum other) const noexcept { return QFlags(QFlag(i & Int(other))); }
|
||||
constexpr inline QFlags operator~() const noexcept { return QFlags(QFlag(~i)); }
|
||||
constexpr inline QFlags operator&(QFlags other) const noexcept { return QFlags(std::in_place, i & other.i); }
|
||||
constexpr inline QFlags operator&(Enum other) const noexcept { return QFlags(std::in_place, i & Int(other)); }
|
||||
constexpr inline QFlags operator~() const noexcept { return QFlags(std::in_place, ~i); }
|
||||
|
||||
constexpr inline void operator+(QFlags other) const noexcept = delete;
|
||||
constexpr inline void operator+(Enum other) const noexcept = delete;
|
||||
@ -174,7 +208,7 @@ private:
|
||||
|
||||
template <typename E> friend QDataStream &operator<<(QDataStream &, QFlags<E>);
|
||||
template <typename E> friend QDataStream &operator>>(QDataStream &, QFlags<E> &);
|
||||
Int i;
|
||||
using Base::i;
|
||||
};
|
||||
|
||||
#ifndef Q_MOC_RUN
|
||||
|
@ -72,6 +72,12 @@
|
||||
enum value can be OR'd with any other enum value and passed on to
|
||||
a function that takes an \c int or \c uint.
|
||||
|
||||
Since Qt 6.9, QFlags supports 64-bit enumerations. It is recommended to use
|
||||
explicit (fixed) underlying types when going above 32 bits, to ensure
|
||||
neither the enumeration nor the QFlags type change sizes if an enumerator
|
||||
is removed. Some compilers will also only make the \c enum type larger than
|
||||
32 bits with explicit underlying types.
|
||||
|
||||
Qt uses QFlags to provide type safety. For example, the
|
||||
Qt::Alignment type is simply a typedef for
|
||||
QFlags<Qt::AlignmentFlag>. QLabel::setAlignment() takes a
|
||||
@ -112,8 +118,6 @@
|
||||
When a singular name is desired for the QFlags type (e.g., \c
|
||||
Alignment), you can use \c Flag as the suffix for the enum type
|
||||
(e.g., \c AlignmentFlag).
|
||||
|
||||
\sa QFlag
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -121,8 +125,10 @@
|
||||
\since 5.0
|
||||
|
||||
Typedef for the integer type used for storage as well as for
|
||||
implicit conversion. Either \c int or \c{unsigned int}, depending
|
||||
on whether the enum's underlying type is signed or unsigned.
|
||||
implicit conversion. Either \c qintXX or \c quintXX, depending on
|
||||
whether the enum's underlying type is signed or unsigned and, since Qt 6.9,
|
||||
the enum's size. Typically, it will be \c qint32 (\c{int}) or \c quint32
|
||||
(\c{unsigned}).
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -159,6 +165,16 @@
|
||||
int, we effectively ensure that arbitrary enum values cannot be
|
||||
cast to a QFlags, whereas untyped enum values (i.e., \c int
|
||||
values) can.
|
||||
|
||||
This constructor is only present for 32-bit \c Enum types. To support all
|
||||
enum sizes, consider the constructor using \c{std::in_place_t}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Enum> QFlags<Enum>::QFlags(std::in_place_t, Int flags)
|
||||
\since 6.9
|
||||
|
||||
Constructs a QFlags object initialized with the integer \a flags.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -184,6 +200,12 @@
|
||||
Performs a bitwise AND operation with \a mask and stores the
|
||||
result in this QFlags object. Returns a reference to this object.
|
||||
|
||||
//! [unsafe-integer]
|
||||
This operator is disabled if the \c{QT_TYPESAFE_FLAGS} macro is defined.
|
||||
Note that it is not extended to 64-bit for 64-bit QFlags either: for 64-bit
|
||||
support, use the type-safe overload.
|
||||
//! [unsafe-integer]
|
||||
|
||||
\sa operator&(), operator|=(), operator^=()
|
||||
*/
|
||||
|
||||
@ -191,6 +213,7 @@
|
||||
\fn template <typename Enum> QFlags &QFlags<Enum>::operator&=(uint mask)
|
||||
|
||||
\overload
|
||||
\include qflags.qdoc unsafe-integer
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -280,6 +303,8 @@
|
||||
Returns a QFlags object containing the result of the bitwise AND
|
||||
operation on this object and \a mask.
|
||||
|
||||
\include qflags.qdoc unsafe-integer
|
||||
|
||||
\sa operator&=(), operator|(), operator^(), operator~()
|
||||
*/
|
||||
|
||||
@ -287,6 +312,7 @@
|
||||
\fn template <typename Enum> QFlags QFlags<Enum>::operator&(uint mask) const
|
||||
|
||||
\overload
|
||||
\include qflags.qdoc unsafe-integer
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
@ -579,7 +579,7 @@ inline QDebug operator<<(QDebug debug, Flags flags)
|
||||
// mutually exclusive.
|
||||
|
||||
namespace QtPrivate {
|
||||
template <typename T, bool IsEnum = std::is_enum_v<T>, bool SizedForQFlags = sizeof(T) <= 4>
|
||||
template <typename T, bool IsEnum = std::is_enum_v<T>, bool = sizeof(T) <= sizeof(quint64)>
|
||||
struct EnumHasQFlag { static constexpr bool Value = false; };
|
||||
template <typename T> struct EnumHasQFlag<T, true, true> : QtPrivate::IsQEnumHelper<QFlags<T>> {};
|
||||
|
||||
|
@ -2990,8 +2990,6 @@ QMetaType QMetaType::underlyingType() const
|
||||
differentiate between different underlying types of the
|
||||
same size and signedness (consider char <-> (un)signed char,
|
||||
int <-> long <-> long long).
|
||||
|
||||
### TODO PENDING: QTBUG-111926 - QFlags supporting >32 bit int
|
||||
*/
|
||||
if (flags() & IsUnsignedEnumeration) {
|
||||
switch (sizeOf()) {
|
||||
|
@ -16,6 +16,13 @@ qt_internal_add_test(tst_qflags
|
||||
tst_qflags.cpp
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qflags64
|
||||
SOURCES
|
||||
tst_qflags.cpp
|
||||
DEFINES
|
||||
QFLAGS_TEST_64
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qflags_non_typesafe
|
||||
SOURCES
|
||||
tst_qflags.cpp
|
||||
|
@ -14,10 +14,22 @@
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#if defined(__cpp_concepts) && __has_include(<concepts>)
|
||||
# include <concepts>
|
||||
#endif
|
||||
|
||||
#ifdef QFLAGS_TEST_64
|
||||
# define tst_QFlags tst_QFlags64
|
||||
using IntegerSize = QIntegerForSize<8>;
|
||||
#else
|
||||
using IntegerSize = QIntegerForSize<4>;
|
||||
#endif
|
||||
|
||||
class tst_QFlags: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void construction() const;
|
||||
void boolCasts() const;
|
||||
void operators() const;
|
||||
void mixingDifferentEnums() const;
|
||||
@ -28,26 +40,25 @@ private slots:
|
||||
void testAnyFlag();
|
||||
void constExpr();
|
||||
void signedness();
|
||||
void size();
|
||||
void castToFromQFlag();
|
||||
void classEnum();
|
||||
void initializerLists();
|
||||
void testSetFlags();
|
||||
void adl();
|
||||
};
|
||||
|
||||
// untyped enum: even though we expect this to be signed, it's possible the
|
||||
// compiler implements it as unsigned
|
||||
enum SignedFlag {
|
||||
enum SignedFlag : IntegerSize::Signed {
|
||||
NoSignedFlag = 0x00000000,
|
||||
LeftSignedFlag = 0x00000001,
|
||||
RightSignedFlag = 0x00000002,
|
||||
MiddleSignedFlag = 0x00000004,
|
||||
SignedFlagMask = -1
|
||||
SignedFlagMask = IntegerSize::Signed(-1)
|
||||
};
|
||||
Q_DECLARE_FLAGS(SignedFlags, SignedFlag)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SignedFlags)
|
||||
|
||||
// typed enum: this one we expect to be unsigned (but MSVC says it isn't)
|
||||
enum UnsignedFlag : unsigned {
|
||||
enum UnsignedFlag : IntegerSize::Unsigned {
|
||||
UnsignedFlag01 = 0x0001,
|
||||
UnsignedFlag02 = 0x0002,
|
||||
UnsignedFlag04 = 0x0004,
|
||||
@ -62,7 +73,18 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(UnsignedFlags)
|
||||
enum MixingFlag {
|
||||
MixingFlag100 = 0x0100,
|
||||
};
|
||||
Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(int, UnsignedFlag, MixingFlag)
|
||||
Q_DECLARE_MIXED_ENUM_OPERATORS_SYMMETRIC(IntegerSize::Signed, UnsignedFlag, MixingFlag)
|
||||
|
||||
void tst_QFlags::construction() const
|
||||
{
|
||||
UnsignedFlags def;
|
||||
UnsignedFlags copied(def);
|
||||
UnsignedFlags moved(std::move(copied)); Q_UNUSED(moved);
|
||||
UnsignedFlags fromEnum(UnsignedFlag01); Q_UNUSED(fromEnum);
|
||||
UnsignedFlags inPlace(std::in_place, 0xffff); Q_UNUSED(inPlace);
|
||||
UnsignedFlags fromInt = UnsignedFlags::fromInt(0xffff); Q_UNUSED(fromInt);
|
||||
// initializer_list tested in initializerLists()
|
||||
}
|
||||
|
||||
void tst_QFlags::boolCasts() const
|
||||
{
|
||||
@ -294,7 +316,11 @@ void tst_QFlags::constExpr()
|
||||
VERIFY_CONSTEXPR((LeftSignedFlag | RightSignedFlag) | MiddleSignedFlag, LeftSignedFlag | RightSignedFlag | MiddleSignedFlag);
|
||||
VERIFY_CONSTEXPR(~(LeftSignedFlag | RightSignedFlag), ~(LeftSignedFlag | RightSignedFlag));
|
||||
VERIFY_CONSTEXPR(SignedFlags(LeftSignedFlag) ^ RightSignedFlag, LeftSignedFlag ^ RightSignedFlag);
|
||||
VERIFY_CONSTEXPR(SignedFlags{}, 0);
|
||||
#ifndef tst_QFlags
|
||||
// only works with QFlag help
|
||||
VERIFY_CONSTEXPR(SignedFlags(0), 0);
|
||||
#endif
|
||||
#ifndef QT_TYPESAFE_FLAGS
|
||||
VERIFY_CONSTEXPR(SignedFlags(RightSignedFlag) & 0xff, RightSignedFlag);
|
||||
VERIFY_CONSTEXPR(SignedFlags(RightSignedFlag) | 0xff, 0xff);
|
||||
@ -320,17 +346,119 @@ void tst_QFlags::signedness()
|
||||
#endif
|
||||
}
|
||||
|
||||
enum class MyStrictEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 };
|
||||
enum class MyStrictEnum : IntegerSize::Unsigned
|
||||
{ StrictZero, StrictOne, StrictTwo, StrictFour=4 };
|
||||
Q_DECLARE_FLAGS( MyStrictFlags, MyStrictEnum )
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( MyStrictFlags )
|
||||
|
||||
enum class MyStrictNoOpEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 };
|
||||
enum class MyStrictNoOpEnum : IntegerSize::Unsigned
|
||||
{ StrictZero, StrictOne, StrictTwo, StrictFour=4 };
|
||||
Q_DECLARE_FLAGS( MyStrictNoOpFlags, MyStrictNoOpEnum )
|
||||
|
||||
static_assert( !QTypeInfo<MyStrictFlags>::isComplex );
|
||||
static_assert( QTypeInfo<MyStrictFlags>::isRelocatable );
|
||||
static_assert( !std::is_pointer_v<MyStrictFlags> );
|
||||
|
||||
void tst_QFlags::size()
|
||||
{
|
||||
static_assert(sizeof(UnsignedFlags) >= sizeof(IntegerSize::Unsigned));
|
||||
static_assert(sizeof(MyStrictFlags) == sizeof(IntegerSize::Unsigned));
|
||||
}
|
||||
|
||||
template <typename Flags, typename FlagType> void castToFromQFlag_template()
|
||||
{
|
||||
// Verify that 32-bit QFlags works with QFlag and, through it,
|
||||
// can be constructed from integer types.
|
||||
auto testType = [](auto initialValue) {
|
||||
using T = decltype(+initialValue);
|
||||
FlagType flag(initialValue); // can construct QFlag from this type
|
||||
T v1 = flag; // can cast QFlag to this type
|
||||
Q_UNUSED(v1);
|
||||
|
||||
Flags flags(initialValue); // can construct QFlags through QFlag from this type
|
||||
T v2 = QFlag(flags); // can cast QFlags to this type through QFlag
|
||||
Q_UNUSED(v2);
|
||||
};
|
||||
testType(qint8(-1));
|
||||
testType(char(1));
|
||||
testType(uchar(2));
|
||||
testType(short(3));
|
||||
testType(ushort(4));
|
||||
testType(int(5));
|
||||
testType(uint(6));
|
||||
#ifdef Q_CC_MSVC
|
||||
// QFlag has a constructor for uint for all other compilers, which make
|
||||
// the construction from long or ulong ambiguous.
|
||||
testType(long(7));
|
||||
testType(ulong(8));
|
||||
#endif
|
||||
|
||||
FlagType flag(1);
|
||||
IntegerSize::Signed i = flag; // must cast to integers
|
||||
IntegerSize::Unsigned u = flag; // must cast to integers
|
||||
QCOMPARE(i, 1);
|
||||
QCOMPARE(u, 1U);
|
||||
|
||||
// QFlags has a constructor on QFlag
|
||||
SignedFlags f = flag;
|
||||
QCOMPARE(f, LeftSignedFlag);
|
||||
UnsignedFlags uf = flag;
|
||||
QCOMPARE(uf, UnsignedFlag01);
|
||||
MyStrictFlags sf = flag;
|
||||
QCOMPARE(sf, MyStrictEnum::StrictOne);
|
||||
|
||||
#ifndef Q_CC_MSVC
|
||||
// QFlags has a cast operator to QFlag
|
||||
// ### this used to work but began failing with MSVC after QFlagsStorage
|
||||
// was introduced
|
||||
flag = FlagType(f);
|
||||
QCOMPARE(IntegerSize::Signed(flag), 1);
|
||||
flag = FlagType(uf);
|
||||
QCOMPARE(IntegerSize::Signed(flag), 1);
|
||||
flag = FlagType(sf);
|
||||
QCOMPARE(IntegerSize::Signed(flag), 1);
|
||||
#endif
|
||||
|
||||
// and thus this should compile
|
||||
QCOMPARE(f, 1);
|
||||
QCOMPARE(uf, 1);
|
||||
QCOMPARE(sf, 1);
|
||||
}
|
||||
|
||||
template <typename Flags> void noCastToFromQFlag_template()
|
||||
{
|
||||
// Verify that non-32-bit QFlags doesn't have QFlag support
|
||||
static_assert(!std::is_constructible_v<Flags, char>);
|
||||
static_assert(!std::is_constructible_v<Flags, uchar>);
|
||||
static_assert(!std::is_constructible_v<Flags, signed char>);
|
||||
static_assert(!std::is_constructible_v<Flags, char16_t>);
|
||||
static_assert(!std::is_constructible_v<Flags, char32_t>);
|
||||
static_assert(!std::is_constructible_v<Flags, short>);
|
||||
static_assert(!std::is_constructible_v<Flags, ushort>);
|
||||
static_assert(!std::is_constructible_v<Flags, int>);
|
||||
static_assert(!std::is_constructible_v<Flags, uint>);
|
||||
static_assert(!std::is_constructible_v<Flags, long>);
|
||||
static_assert(!std::is_constructible_v<Flags, ulong>);
|
||||
static_assert(!std::is_constructible_v<Flags, qlonglong>);
|
||||
static_assert(!std::is_constructible_v<Flags, qulonglong>);
|
||||
static_assert(!std::is_constructible_v<Flags, QFlag>);
|
||||
static_assert(!std::is_constructible_v<QFlag, Flags>);
|
||||
|
||||
#if defined(__cpp_concepts) && __has_include(<concepts>)
|
||||
static_assert(!std::equality_comparable_with<QFlag, Flags>);
|
||||
static_assert(!std::equality_comparable_with<Flags, int>);
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QFlags::castToFromQFlag()
|
||||
{
|
||||
if constexpr (sizeof(IntegerSize::Signed) == sizeof(int)) {
|
||||
castToFromQFlag_template<MyStrictFlags, QFlag>();
|
||||
} else {
|
||||
noCastToFromQFlag_template<MyStrictFlags>();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFlags::classEnum()
|
||||
{
|
||||
// The main aim of the test is making sure it compiles
|
||||
@ -476,7 +604,7 @@ void tst_QFlags::testSetFlags()
|
||||
}
|
||||
|
||||
namespace SomeNS {
|
||||
enum Foo { Foo_A = 1 << 0, Foo_B = 1 << 1, Foo_C = 1 << 2 };
|
||||
enum Foo : IntegerSize::Unsigned { Foo_A = 1 << 0, Foo_B = 1 << 1, Foo_C = 1 << 2 };
|
||||
|
||||
Q_DECLARE_FLAGS(Foos, Foo)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(Foos);
|
||||
@ -497,7 +625,7 @@ void tst_QFlags::adl()
|
||||
}
|
||||
|
||||
// (statically) check QTypeInfo for QFlags instantiations:
|
||||
enum MyEnum { Zero, One, Two, Four=4 };
|
||||
enum MyEnum : IntegerSize::Unsigned { Zero, One, Two, Four=4 };
|
||||
Q_DECLARE_FLAGS( MyFlags, MyEnum )
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS( MyFlags )
|
||||
|
||||
|
@ -1858,6 +1858,14 @@ void tst_QMetaType::isEnum()
|
||||
|
||||
int type6 = ::qMetaTypeId<isEnumTest_Enum1>();
|
||||
QVERIFY((QMetaType(type6).flags() & QMetaType::IsEnumeration) == QMetaType::IsEnumeration);
|
||||
|
||||
// QFlags are considered enums
|
||||
QCOMPARE(QMetaType::fromType<QFlags<isEnumTest_Enum0>>().flags() & QMetaType::IsEnumeration,
|
||||
QMetaType::IsEnumeration);
|
||||
QCOMPARE(QMetaType::fromType<QFlags<isEnumTest_Enum1>>().flags() & QMetaType::IsEnumeration,
|
||||
QMetaType::IsEnumeration);
|
||||
QCOMPARE(QMetaType::fromType<QFlags<isEnumTest_Struct0::A>>().flags() & QMetaType::IsEnumeration,
|
||||
QMetaType::IsEnumeration);
|
||||
}
|
||||
|
||||
enum E1 : unsigned char {};
|
||||
@ -1870,12 +1878,13 @@ namespace myflags {
|
||||
|
||||
enum Flag1 : int { A, B };
|
||||
enum Flag2 : short { X, Y };
|
||||
enum Flag3 : qlonglong { T, W = Q_INT64_C(0x1'0000'0002) };
|
||||
|
||||
Q_DECLARE_FLAGS(Flags1, myflags::Flag1);
|
||||
Q_FLAG_NS(Flags1)
|
||||
Q_DECLARE_FLAGS(Flags2, myflags::Flag2);
|
||||
Q_FLAG_NS(Flags2)
|
||||
|
||||
Q_DECLARE_FLAGS(Flags3, myflags::Flag3)
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -1895,14 +1904,16 @@ void tst_QMetaType::underlyingType_data()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<isEnumTest_Enum1>>();
|
||||
QTest::newRow("uchar") << QMetaType::fromType<E1>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<E1>>();
|
||||
QTest::newRow("long") << QMetaType::fromType<E2>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<E2>>();
|
||||
QTest::newRow("qlonglong") << QMetaType::fromType<E2>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<E2>>();
|
||||
QTest::newRow("class_ushort") << QMetaType::fromType<E3>()
|
||||
<< QMetaType::fromType<getUnderlyingTypeNormalized<E3>>();
|
||||
QTest::newRow("flags_int") << QMetaType::fromType<myflags::Flags1>()
|
||||
<< QMetaType::fromType<int>();
|
||||
QTest::newRow("flags_short") << QMetaType::fromType<myflags::Flags2>()
|
||||
<< QMetaType::fromType<int>(); // sic, not short!
|
||||
QTest::newRow("flags_qlonglong") << QMetaType::fromType<myflags::Flags3>()
|
||||
<< QMetaType::fromType<qlonglong>();
|
||||
}
|
||||
|
||||
void tst_QMetaType::underlyingType()
|
||||
|
Loading…
x
Reference in New Issue
Block a user