diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 470be1c0b0..894ebcbb80 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -53,6 +53,7 @@ #include "qurl.h" #include "qlocale.h" #include "private/qvariant_p.h" +#include "qmetatype_p.h" #ifndef QT_NO_GEOM_VARIANT #include "qsize.h" @@ -72,299 +73,49 @@ QT_BEGIN_NAMESPACE # define FLT_DIG 6 #endif +template +struct TypeDefiniton { + static const bool IsAvailable = true; +}; + +// Ignore these types, as incomplete +#ifdef QT_BOOTSTRAPPED +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_GEOM_VARIANT +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif + +struct CoreTypesFilter { + template + struct Acceptor { + static const bool IsAccepted = QTypeModuleInfo::IsCore && TypeDefiniton::IsAvailable; + }; +}; + static void construct(QVariant::Private *x, const void *copy) { - x->is_shared = false; - - switch (x->type) { - case QVariant::String: - v_construct(x, copy); - break; - case QVariant::Char: - v_construct(x, copy); - break; - case QVariant::StringList: - v_construct(x, copy); - break; - case QVariant::Map: - v_construct(x, copy); - break; - case QVariant::Hash: - v_construct(x, copy); - break; - case QVariant::List: - v_construct(x, copy); - break; - case QVariant::Date: - v_construct(x, copy); - break; - case QVariant::Time: - v_construct(x, copy); - break; - case QVariant::DateTime: - v_construct(x, copy); - break; - case QVariant::ByteArray: - v_construct(x, copy); - break; - case QVariant::BitArray: - v_construct(x, copy); - break; -#ifndef QT_NO_GEOM_VARIANT - case QVariant::Size: - v_construct(x, copy); - break; - case QVariant::SizeF: - v_construct(x, copy); - break; - case QVariant::Rect: - v_construct(x, copy); - break; - case QVariant::LineF: - v_construct(x, copy); - break; - case QVariant::Line: - v_construct(x, copy); - break; - case QVariant::RectF: - v_construct(x, copy); - break; - case QVariant::Point: - v_construct(x, copy); - break; - case QVariant::PointF: - v_construct(x, copy); - break; -#endif - case QVariant::Url: - v_construct(x, copy); - break; - case QVariant::Locale: - v_construct(x, copy); - break; -#ifndef QT_NO_REGEXP - case QVariant::RegExp: - v_construct(x, copy); - break; -#endif -#ifndef QT_BOOTSTRAPPED - case QVariant::EasingCurve: - v_construct(x, copy); - break; -#endif - case QVariant::Int: - x->data.i = copy ? *static_cast(copy) : 0; - break; - case QVariant::UInt: - x->data.u = copy ? *static_cast(copy) : 0u; - break; - case QVariant::Bool: - x->data.b = copy ? *static_cast(copy) : false; - break; - case QVariant::Double: - x->data.d = copy ? *static_cast(copy) : 0.0; - break; - case QMetaType::Float: - x->data.f = copy ? *static_cast(copy) : 0.0f; - break; - case QMetaType::QObjectStar: - x->data.o = copy ? *static_cast(copy) : 0; - break; - case QVariant::LongLong: - x->data.ll = copy ? *static_cast(copy) : Q_INT64_C(0); - break; - case QVariant::ULongLong: - x->data.ull = copy ? *static_cast(copy) : Q_UINT64_C(0); - break; - case QVariant::Invalid: - case QVariant::UserType: - break; - default: - void *ptr = QMetaType::create(x->type, copy); - if (!ptr) { - x->type = QVariant::Invalid; - } else { - x->is_shared = true; - x->data.shared = new QVariant::PrivateShared(ptr); - } - break; - } - x->is_null = !copy; + QVariantConstructor constructor(x, copy); + QMetaTypeSwitcher::switcher(constructor, x->type, 0); } static void clear(QVariant::Private *d) { - switch (d->type) { - case QVariant::String: - v_clear(d); - break; - case QVariant::Char: - v_clear(d); - break; - case QVariant::StringList: - v_clear(d); - break; - case QVariant::Map: - v_clear(d); - break; - case QVariant::Hash: - v_clear(d); - break; - case QVariant::List: - v_clear(d); - break; - case QVariant::Date: - v_clear(d); - break; - case QVariant::Time: - v_clear(d); - break; - case QVariant::DateTime: - v_clear(d); - break; - case QVariant::ByteArray: - v_clear(d); - break; - case QVariant::BitArray: - v_clear(d); - break; -#ifndef QT_NO_GEOM_VARIANT - case QVariant::Point: - v_clear(d); - break; - case QVariant::PointF: - v_clear(d); - break; - case QVariant::Size: - v_clear(d); - break; - case QVariant::SizeF: - v_clear(d); - break; - case QVariant::Rect: - v_clear(d); - break; - case QVariant::LineF: - v_clear(d); - break; - case QVariant::Line: - v_clear(d); - break; - case QVariant::RectF: - v_clear(d); - break; -#endif - case QVariant::Url: - v_clear(d); - break; - case QVariant::Locale: - v_clear(d); - break; -#ifndef QT_NO_REGEXP - case QVariant::RegExp: - v_clear(d); - break; -#endif -#ifndef QT_BOOTSTRAPPED - case QVariant::EasingCurve: - v_clear(d); - break; -#endif - case QVariant::LongLong: - case QVariant::ULongLong: - case QVariant::Double: - case QMetaType::Float: - case QMetaType::QObjectStar: - break; - case QVariant::Invalid: - case QVariant::UserType: - case QVariant::Int: - case QVariant::UInt: - case QVariant::Bool: - break; - default: - QMetaType::destroy(d->type, d->data.shared->ptr); - delete d->data.shared; - break; - } - - d->type = QVariant::Invalid; - d->is_null = true; - d->is_shared = false; + QVariantDestructor cleaner(d); + QMetaTypeSwitcher::switcher(cleaner, d->type, 0); } static bool isNull(const QVariant::Private *d) { - switch(d->type) { - case QVariant::String: - return v_cast(d)->isNull(); - case QVariant::Char: - return v_cast(d)->isNull(); - case QVariant::Date: - return v_cast(d)->isNull(); - case QVariant::Time: - return v_cast(d)->isNull(); - case QVariant::DateTime: - return v_cast(d)->isNull(); - case QVariant::ByteArray: - return v_cast(d)->isNull(); - case QVariant::BitArray: - return v_cast(d)->isNull(); -#ifndef QT_NO_GEOM_VARIANT - case QVariant::Size: - return v_cast(d)->isNull(); - case QVariant::SizeF: - return v_cast(d)->isNull(); - case QVariant::Rect: - return v_cast(d)->isNull(); - case QVariant::Line: - return v_cast(d)->isNull(); - case QVariant::LineF: - return v_cast(d)->isNull(); - case QVariant::RectF: - return v_cast(d)->isNull(); - case QVariant::Point: - return v_cast(d)->isNull(); - case QVariant::PointF: - return v_cast(d)->isNull(); -#endif -#ifndef QT_BOOTSTRAPPED - case QVariant::EasingCurve: -#endif - case QVariant::Url: - case QVariant::Locale: - case QVariant::RegExp: - case QVariant::StringList: - case QVariant::Map: - case QVariant::Hash: - case QVariant::List: - case QVariant::Invalid: - case QVariant::UserType: - case QVariant::Int: - case QVariant::UInt: - case QVariant::LongLong: - case QVariant::ULongLong: - case QVariant::Bool: - case QVariant::Double: - case QMetaType::Float: - case QMetaType::QObjectStar: - break; - } - return d->is_null; -} - -/* - \internal - \since 4.4 - - We cannot use v_cast() for QMetaType's numeric types because they're smaller than QVariant::Private::Data, - which in turns makes v_cast() believe the value is stored in d->data.c. But - it's not, since we're a QMetaType type. - */ -template -inline bool compareNumericMetaType(const QVariant::Private *const a, const QVariant::Private *const b) -{ - return *static_cast(a->data.shared->ptr) == *static_cast(b->data.shared->ptr); + QVariantIsNull isNull(d); + return QMetaTypeSwitcher::switcher(isNull, d->type, 0); } /*! @@ -375,123 +126,8 @@ inline bool compareNumericMetaType(const QVariant::Private *const a, const QVari */ static bool compare(const QVariant::Private *a, const QVariant::Private *b) { - switch(a->type) { - case QVariant::List: - return *v_cast(a) == *v_cast(b); - case QVariant::Map: { - const QVariantMap *m1 = v_cast(a); - const QVariantMap *m2 = v_cast(b); - if (m1->count() != m2->count()) - return false; - QVariantMap::ConstIterator it = m1->constBegin(); - QVariantMap::ConstIterator it2 = m2->constBegin(); - while (it != m1->constEnd()) { - if (*it != *it2 || it.key() != it2.key()) - return false; - ++it; - ++it2; - } - return true; - } - case QVariant::Hash: - return *v_cast(a) == *v_cast(b); - case QVariant::String: - return *v_cast(a) == *v_cast(b); - case QVariant::Char: - return *v_cast(a) == *v_cast(b); - case QVariant::StringList: - return *v_cast(a) == *v_cast(b); -#ifndef QT_NO_GEOM_VARIANT - case QVariant::Size: - return *v_cast(a) == *v_cast(b); - case QVariant::SizeF: - return *v_cast(a) == *v_cast(b); - case QVariant::Rect: - return *v_cast(a) == *v_cast(b); - case QVariant::Line: - return *v_cast(a) == *v_cast(b); - case QVariant::LineF: - return *v_cast(a) == *v_cast(b); - case QVariant::RectF: - return *v_cast(a) == *v_cast(b); - case QVariant::Point: - return *v_cast(a) == *v_cast(b); - case QVariant::PointF: - return *v_cast(a) == *v_cast(b); -#endif - case QVariant::Url: - return *v_cast(a) == *v_cast(b); - case QVariant::Locale: - return *v_cast(a) == *v_cast(b); -#ifndef QT_NO_REGEXP - case QVariant::RegExp: - return *v_cast(a) == *v_cast(b); -#endif - case QVariant::Int: - return a->data.i == b->data.i; - case QVariant::UInt: - return a->data.u == b->data.u; - case QVariant::LongLong: - return a->data.ll == b->data.ll; - case QVariant::ULongLong: - return a->data.ull == b->data.ull; - case QVariant::Bool: - return a->data.b == b->data.b; - case QVariant::Double: - return a->data.d == b->data.d; - case QMetaType::Float: - return a->data.f == b->data.f; - case QMetaType::QObjectStar: - return a->data.o == b->data.o; - case QVariant::Date: - return *v_cast(a) == *v_cast(b); - case QVariant::Time: - return *v_cast(a) == *v_cast(b); - case QVariant::DateTime: - return *v_cast(a) == *v_cast(b); -#ifndef QT_BOOTSTRAPPED - case QVariant::EasingCurve: - return *v_cast(a) == *v_cast(b); -#endif - case QVariant::ByteArray: - return *v_cast(a) == *v_cast(b); - case QVariant::BitArray: - return *v_cast(a) == *v_cast(b); - case QVariant::Invalid: - return true; - case QMetaType::Long: - return compareNumericMetaType(a, b); - case QMetaType::ULong: - return compareNumericMetaType(a, b); - case QMetaType::Short: - return compareNumericMetaType(a, b); - case QMetaType::UShort: - return compareNumericMetaType(a, b); - case QMetaType::UChar: - return compareNumericMetaType(a, b); - case QMetaType::Char: - return compareNumericMetaType(a, b); - default: - break; - } - if (!QMetaType::isRegistered(a->type)) - qFatal("QVariant::compare: type %d unknown to QVariant.", a->type); - - const void *a_ptr = a->is_shared ? a->data.shared->ptr : &(a->data.ptr); - const void *b_ptr = b->is_shared ? b->data.shared->ptr : &(b->data.ptr); - - /* The reason we cannot place this test in a case branch above for the types - * QMetaType::VoidStar, QMetaType::QObjectStar and so forth, is that it wouldn't include - * user defined pointer types. */ - const char *const typeName = QMetaType::typeName(a->type); - uint typeNameLen = qstrlen(typeName); - if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*') - return *static_cast(a_ptr) == *static_cast(b_ptr); - - if (a->is_null && b->is_null) - return true; - - return a_ptr == b_ptr; + QVariantComparator comparator(a, b); + return QMetaTypeSwitcher::switcher(comparator, a->type, 0); } /*! @@ -505,11 +141,11 @@ static qlonglong qMetaTypeNumber(const QVariant::Private *d) case QMetaType::LongLong: return d->data.ll; case QMetaType::Char: - return qlonglong(*static_cast(d->data.shared->ptr)); + return qlonglong(d->data.c); case QMetaType::Short: - return qlonglong(*static_cast(d->data.shared->ptr)); + return qlonglong(d->data.s); case QMetaType::Long: - return qlonglong(*static_cast(d->data.shared->ptr)); + return qlonglong(d->data.l); case QMetaType::Float: return qRound64(d->data.f); case QVariant::Double: @@ -527,11 +163,11 @@ static qulonglong qMetaTypeUNumber(const QVariant::Private *d) case QVariant::ULongLong: return d->data.ull; case QMetaType::UChar: - return qulonglong(*static_cast(d->data.shared->ptr)); + return d->data.uc; case QMetaType::UShort: - return qulonglong(*static_cast(d->data.shared->ptr)); + return d->data.us; case QMetaType::ULong: - return qulonglong(*static_cast(d->data.shared->ptr)); + return d->data.ul; } Q_ASSERT(false); return 0; @@ -642,7 +278,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, break; case QMetaType::Char: case QMetaType::UChar: - *str = QChar::fromAscii(*static_cast(d->data.shared->ptr)); + *str = QChar::fromAscii(d->data.c); break; case QMetaType::Short: case QMetaType::Long: @@ -835,7 +471,7 @@ static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, break; case QMetaType::Char: case QMetaType::UChar: - *ba = QByteArray(1, *static_cast(d->data.shared->ptr)); + *ba = QByteArray(1, d->data.c); break; case QVariant::Int: case QVariant::LongLong: @@ -1396,7 +1032,7 @@ void QVariant::create(int type, const void *copy) QVariant::~QVariant() { - if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared && d.type > Char && d.type < UserType)) + if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared && d.type > Char)) handler->clear(&d); } @@ -1412,7 +1048,7 @@ QVariant::QVariant(const QVariant &p) { if (d.is_shared) { d.data.shared->ref.ref(); - } else if (p.d.type > Char && p.d.type < QVariant::UserType) { + } else if (p.d.type > Char) { handler->construct(&d, p.constData()); d.is_null = p.d.is_null; } @@ -1669,11 +1305,10 @@ QVariant::QVariant(int typeOrUserType, const void *copy, uint flags) if (flags) { //type is a pointer type d.type = typeOrUserType; d.data.ptr = *reinterpret_cast(copy); - d.is_null = false; } else { create(typeOrUserType, copy); - d.is_null = false; } + d.is_null = false; } QVariant::QVariant(int val) @@ -1796,7 +1431,7 @@ QVariant& QVariant::operator=(const QVariant &variant) if (variant.d.is_shared) { variant.d.data.shared->ref.ref(); d = variant.d; - } else if (variant.d.type > Char && variant.d.type < UserType) { + } else if (variant.d.type > Char) { d.type = variant.d.type; handler->construct(&d, variant.constData()); d.is_null = variant.d.is_null; @@ -1858,7 +1493,7 @@ const char *QVariant::typeName() const */ void QVariant::clear() { - if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared && d.type < UserType && d.type > Char)) + if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared && d.type > Char)) handler->clear(&d); d.type = Invalid; d.is_null = true; diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index fbf5a84d3e..ee1a2add54 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -317,8 +317,13 @@ class Q_CORE_EXPORT QVariant union Data { char c; + uchar uc; + short s; + ushort us; int i; uint u; + long l; + ulong ul; bool b; double d; float f; diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 98a7bcbf57..aa03bb298a 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -59,6 +59,8 @@ #include #include +#include "qmetatypeswitcher_p.h" + QT_BEGIN_NAMESPACE #ifdef Q_CC_SUN // Sun CC picks the wrong overload, so introduce awful hack @@ -146,8 +148,298 @@ inline void v_clear(QVariant::Private *d, T* = 0) } +template +class QVariantComparator { + template::IsAccepted> + struct FilteredComparator { + static bool compare(const QVariant::Private *a, const QVariant::Private *b) + { + return *v_cast(a) == *v_cast(b); + } + }; + template + struct FilteredComparator { + static bool compare(const QVariant::Private *m_a, const QVariant::Private *m_b) + { + if (!QMetaType::isRegistered(m_a->type)) + qFatal("QVariant::compare: type %d unknown to QVariant.", m_a->type); + + const void *a_ptr = m_a->is_shared ? m_a->data.shared->ptr : &(m_a->data.ptr); + const void *b_ptr = m_b->is_shared ? m_b->data.shared->ptr : &(m_b->data.ptr); + + const char *const typeName = QMetaType::typeName(m_a->type); + uint typeNameLen = qstrlen(typeName); + if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*') + return *static_cast(a_ptr) == *static_cast(b_ptr); + + if (m_a->is_null && m_b->is_null) + return true; + + return !memcmp(a_ptr, b_ptr, QMetaType::sizeOf(m_a->type)); + } + }; +public: + QVariantComparator(const QVariant::Private *a, const QVariant::Private *b) + : m_a(a), m_b(b) + { + Q_ASSERT(a->type == b->type); + } + + template + bool delegate(const T*) + { + return FilteredComparator::compare(m_a, m_b); + } + + bool delegate(const void*) { return true; } + +protected: + const QVariant::Private *m_a; + const QVariant::Private *m_b; +}; + + Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler(); +template +class QVariantIsNull +{ + /// \internal + /// This class checks if a type T has method called isNull. Result is kept in the Value property + /// TODO Can we somehow generalize it? A macro version? + template::isComplex> + class HasIsNullMethod + { + struct Yes { char unused[1]; }; + struct No { char unused[2]; }; + Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); + + struct FallbackMixin { bool isNull() const; }; + struct Derived : public T, public FallbackMixin {}; + template struct TypeCheck {}; + + template static Yes test(...); + template static No test(TypeCheck *); + public: + static const bool Value = (sizeof(test(0)) == sizeof(Yes)); + }; + + // We need to exclude primitive types as they won't compile with HasIsNullMethod::Check classes + // anyway it is not a problem as the types do not have isNull method. + template + class HasIsNullMethod { + public: + static const bool Value = false; + }; + + // TODO This part should go to autotests during HasIsNullMethod generalization. + Q_STATIC_ASSERT(!HasIsNullMethod::Value); + struct SelfTest1 { bool isNull() const; }; + Q_STATIC_ASSERT(HasIsNullMethod::Value); + struct SelfTest2 {}; + Q_STATIC_ASSERT(!HasIsNullMethod::Value); + struct SelfTest3 : public SelfTest1 {}; + Q_STATIC_ASSERT(HasIsNullMethod::Value); + + template::Value> + struct CallFilteredIsNull + { + static bool isNull(const QVariant::Private *d) + { + return v_cast(d)->isNull(); + } + }; + template + struct CallFilteredIsNull + { + static bool isNull(const QVariant::Private *d) + { + return d->is_null; + } + }; + + template::IsAccepted> + struct CallIsNull + { + static bool isNull(const QVariant::Private *d) + { + return CallFilteredIsNull::isNull(d); + } + }; + template + struct CallIsNull + { + static bool isNull(const QVariant::Private *d) + { + return CallFilteredIsNull::isNull(d); + } + }; + +public: + QVariantIsNull(const QVariant::Private *d) + : m_d(d) + {} + template + bool delegate(const T*) + { + CallIsNull null; + return null.isNull(m_d); + } + // we need that as sizof(void) is undefined and it is needed in HasIsNullMethod + bool delegate(const void *) { return m_d->is_null; } +protected: + const QVariant::Private *m_d; +}; + +template +class QVariantConstructor +{ + template + struct CallConstructor {}; + + template + struct CallConstructor + { + CallConstructor(const QVariantConstructor &tc) + { + if (tc.m_copy) + new (&tc.m_x->data.ptr) T(*static_cast(tc.m_copy)); + else + new (&tc.m_x->data.ptr) T(); + tc.m_x->is_shared = false; + } + }; + + template + struct CallConstructor + { + CallConstructor(const QVariantConstructor &tc) + { + Q_STATIC_ASSERT(QTypeInfo::isComplex); + tc.m_x->data.shared = tc.m_copy ? new QVariantPrivateSharedEx(*static_cast(tc.m_copy)) + : new QVariantPrivateSharedEx; + tc.m_x->is_shared = true; + } + }; + + template::IsAccepted> + struct FilteredConstructor { + FilteredConstructor(const QVariantConstructor &tc) + { + CallConstructor tmp(tc); + tc.m_x->is_null = !tc.m_copy; + } + }; + template + struct FilteredConstructor { + FilteredConstructor(const QVariantConstructor &tc) + { + // ignore types that lives outside of the current library + tc.m_x->type = QVariant::Invalid; + } + }; +public: + QVariantConstructor(QVariant::Private *x, const void *copy) + : m_x(x) + , m_copy(copy) + {} + + template + void delegate(const T*) + { + FilteredConstructor(*this); + } + + void delegate(const QMetaTypeSwitcher::UnknownType*) + { + if (m_x->type == QVariant::UserType) { + // TODO get rid of it + // And yes! we can support historical magic, unkonwn/unconstructed user type isn't that + // awesome? this QVariant::isValid will be true! + m_x->is_null = !m_copy; + m_x->is_shared = false; + return; + } + // it is not a static known type, lets ask QMetaType if it can be constructed for us. + const uint size = QMetaType::sizeOf(m_x->type); + + if (size && size <= sizeof(QVariant::Private::Data)) { + void *ptr = QMetaType::construct(m_x->type, &m_x->data.ptr, m_copy); + if (!ptr) { + m_x->type = QVariant::Invalid; + } + m_x->is_shared = false; + } else { + void *ptr = QMetaType::create(m_x->type, m_copy); + if (!ptr) { + m_x->type = QVariant::Invalid; + } else { + m_x->is_shared = true; + m_x->data.shared = new QVariant::PrivateShared(ptr); + } + } + } + + void delegate(const void*) + { + // QMetaType::Void == QVariant::Invalid, creating an invalid value creates invalid QVariant + // TODO it might go away, check is needed + m_x->is_shared = false; + m_x->is_null = !m_copy; + } +private: + QVariant::Private *m_x; + const void *m_copy; +}; + +template +class QVariantDestructor +{ + template::IsAccepted> + struct FilteredDestructor { + FilteredDestructor(QVariant::Private *d) + { + v_clear(d); + } + }; + template + struct FilteredDestructor { + FilteredDestructor(QVariant::Private *) {} // ignore non accessible types + }; + +public: + QVariantDestructor(QVariant::Private *d) + : m_d(d) + {} + ~QVariantDestructor() + { + m_d->type = QVariant::Invalid; + m_d->is_null = true; + m_d->is_shared = false; + } + + template + void delegate(const T*) + { + FilteredDestructor cleaner(m_d); + } + + void delegate(const QMetaTypeSwitcher::UnknownType*) + { + // This is not a static type, so lets delegate everyting to QMetaType + if (!m_d->is_shared) { + QMetaType::destruct(m_d->type, &m_d->data.ptr); + } else { + QMetaType::destroy(m_d->type, m_d->data.shared->ptr); + delete m_d->data.shared; + } + } + // Ignore nonconstructible type + void delegate(const void*) {} +private: + QVariant::Private *m_d; +}; + QT_END_NAMESPACE #endif // QVARIANT_P_H diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp index 72f7e183e1..2bb7524ab9 100644 --- a/src/gui/kernel/qguivariant.cpp +++ b/src/gui/kernel/qguivariant.cpp @@ -41,6 +41,7 @@ #include "qvariant.h" +// Gui types #include "qbitmap.h" #include "qbrush.h" #include "qcolor.h" @@ -64,7 +65,32 @@ #include "qvector4d.h" #include "qquaternion.h" +// Core types +#include "qvariant.h" +#include "qbitarray.h" +#include "qbytearray.h" +#include "qdatastream.h" +#include "qdebug.h" +#include "qmap.h" +#include "qdatetime.h" +#include "qeasingcurve.h" +#include "qlist.h" +#include "qstring.h" +#include "qstringlist.h" +#include "qurl.h" +#include "qlocale.h" + +#ifndef QT_NO_GEOM_VARIANT +#include "qsize.h" +#include "qpoint.h" +#include "qrect.h" +#include "qline.h" +#endif + +#include + #include "private/qvariant_p.h" +#include QT_BEGIN_NAMESPACE @@ -72,340 +98,151 @@ Q_GUI_EXPORT const QVariant::Handler *qt_widgets_variant_handler = 0; Q_CORE_EXPORT const QVariant::Handler *qcoreVariantHandler(); +template +struct TypeDefiniton { + static const bool IsAvailable = true; +}; +// Ignore these types, as incomplete +#ifdef QT_NO_GEOM_VARIANT +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_SHORTCUT +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_CURSOR +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_MATRIX4X4 +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_VECTOR2D +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_VECTOR3D +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_VECTOR4D +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif +#ifdef QT_NO_QUATERNION +template<> struct TypeDefiniton { static const bool IsAvailable = false; }; +#endif + +struct CoreAndGuiTypesFilter { + template + struct Acceptor { + static const bool IsAccepted = (QTypeModuleInfo::IsCore || QTypeModuleInfo::IsGui) && TypeDefiniton::IsAvailable; + }; +}; + static void construct(QVariant::Private *x, const void *copy) { - switch (x->type) { - case QVariant::Bitmap: - v_construct(x, copy); - break; - case QVariant::Region: - v_construct(x, copy); - break; - case QVariant::Polygon: - v_construct(x, copy); - break; - case QVariant::Font: - v_construct(x, copy); - break; - case QVariant::Pixmap: - v_construct(x, copy); - break; - case QVariant::Image: - v_construct(x, copy); - break; - case QVariant::Brush: - v_construct(x, copy); - break; - case QVariant::Color: - v_construct(x, copy); - break; - case QVariant::Palette: - v_construct(x, copy); - break; - case QVariant::Matrix: - v_construct(x, copy); - break; - case QVariant::Transform: - v_construct(x, copy); - break; - case QVariant::TextFormat: - v_construct(x, copy); - break; - case QVariant::TextLength: - v_construct(x, copy); - break; -#ifndef QT_NO_SHORTCUT - case QVariant::KeySequence: - v_construct(x, copy); - break; -#endif - case QVariant::Pen: - v_construct(x, copy); - break; -#ifndef QT_NO_CURSOR - case QVariant::Cursor: - v_construct(x, copy); - break; -#endif - case 62: { - // small 'trick' to let a QVariant(Qt::blue) create a variant - // of type QColor - x->type = QVariant::Color; - QColor color(*reinterpret_cast(copy)); - v_construct(x, &color); - break; - } -#ifndef QT_NO_MATRIX4X4 - case QVariant::Matrix4x4: - v_construct(x, copy); - break; -#endif -#ifndef QT_NO_VECTOR2D - case QVariant::Vector2D: - v_construct(x, copy); - break; -#endif -#ifndef QT_NO_VECTOR3D - case QVariant::Vector3D: - v_construct(x, copy); - break; -#endif -#ifndef QT_NO_VECTOR4D - case QVariant::Vector4D: - v_construct(x, copy); - break; -#endif -#ifndef QT_NO_QUATERNION - case QVariant::Quaternion: - v_construct(x, copy); - break; -#endif - case QVariant::SizePolicy: - case QVariant::Icon: - if (qt_widgets_variant_handler) { - qt_widgets_variant_handler->construct(x, copy); + const int type = x->type; + QVariantConstructor constructor(x, copy); + QMetaTypeSwitcher::switcher(constructor, type, 0); + + // FIXME This is an ugly hack if QVariantConstructor fails to build a value it constructs an invalid type + if (Q_UNLIKELY(x->type == QVariant::Invalid)) { + if (type == 62) { + // small 'trick' to let a QVariant(Qt::blue) create a variant + // of type QColor + // TODO Get rid of this hack. + x->type = QVariant::Color; + QColor color(*reinterpret_cast(copy)); + v_construct(x, &color); return; } - break; - default: - qcoreVariantHandler()->construct(x, copy); - return; + if (type == QVariant::Icon || type == QVariant::SizePolicy) { + // TODO we need to clean up variant handlers, so they are replacament, not extension + x->type = type; + if (qt_widgets_variant_handler) { + qt_widgets_variant_handler->construct(x, copy); + } + } } - x->is_null = !copy; } static void clear(QVariant::Private *d) { - switch (d->type) { - case QVariant::Bitmap: - v_clear(d); - break; - case QVariant::Cursor: - v_clear(d); - break; - case QVariant::Region: - v_clear(d); - break; - case QVariant::Polygon: - v_clear(d); - break; - case QVariant::Font: - v_clear(d); - break; - case QVariant::Pixmap: - v_clear(d); - break; - case QVariant::Image: - v_clear(d); - break; - case QVariant::Brush: - v_clear(d); - break; - case QVariant::Color: - v_clear(d); - break; - case QVariant::Palette: - v_clear(d); - break; - case QVariant::Matrix: - v_clear(d); - break; - case QVariant::Transform: - v_clear(d); - break; - case QVariant::TextFormat: - v_clear(d); - break; - case QVariant::TextLength: - v_clear(d); - break; -#ifndef QT_NO_SHORTCUT - case QVariant::KeySequence: - v_clear(d); - break; -#endif - case QVariant::Pen: - v_clear(d); - break; -#ifndef QT_NO_MATRIX4X4 - case QVariant::Matrix4x4: - v_clear(d); - break; -#endif -#ifndef QT_NO_VECTOR2D - case QVariant::Vector2D: - v_clear(d); - break; -#endif -#ifndef QT_NO_VECTOR3D - case QVariant::Vector3D: - v_clear(d); - break; -#endif -#ifndef QT_NO_VECTOR4D - case QVariant::Vector4D: - v_clear(d); - break; -#endif -#ifndef QT_NO_QUATERNION - case QVariant::Quaternion: - v_clear(d); - break; -#endif - case QVariant::SizePolicy: - case QVariant::Icon: + const int type = d->type; + if (type == QVariant::Icon || type == QVariant::SizePolicy) { + // TODO we need to clean up variant handlers, so they are replacament, not extension if (qt_widgets_variant_handler) { qt_widgets_variant_handler->clear(d); return; } - break; - default: - qcoreVariantHandler()->clear(d); - return; } - - d->type = QVariant::Invalid; - d->is_null = true; - d->is_shared = false; + QVariantDestructor destructor(d); + QMetaTypeSwitcher::switcher(destructor, type, 0); } - +// This class is a hack that customizes access to QPolygon +template +class QGuiVariantIsNull : public QVariantIsNull { + typedef QVariantIsNull Base; +public: + QGuiVariantIsNull(const QVariant::Private *d) + : QVariantIsNull(d) + {} + template + bool delegate(const T *p) { return Base::delegate(p); } + bool delegate(const QPolygon*) { return v_cast(Base::m_d)->isEmpty(); } + bool delegate(const void *p) { return Base::delegate(p); } +}; static bool isNull(const QVariant::Private *d) { - switch(d->type) { - case QVariant::Bitmap: - return v_cast(d)->isNull(); - case QVariant::Region: - return v_cast(d)->isEmpty(); - case QVariant::Polygon: - return v_cast(d)->isEmpty(); - case QVariant::Pixmap: - return v_cast(d)->isNull(); - case QVariant::Image: - return v_cast(d)->isNull(); - case QVariant::Matrix: - case QVariant::TextFormat: - case QVariant::TextLength: - case QVariant::Cursor: - case QVariant::StringList: - case QVariant::Font: - case QVariant::Brush: - case QVariant::Color: - case QVariant::Palette: - case QVariant::SizePolicy: -#ifndef QT_NO_SHORTCUT - case QVariant::KeySequence: -#endif - case QVariant::Pen: -#ifndef QT_NO_MATRIX4X4 - case QVariant::Matrix4x4: -#endif - break; -#ifndef QT_NO_VECTOR2D - case QVariant::Vector2D: - return v_cast(d)->isNull(); -#endif -#ifndef QT_NO_VECTOR3D - case QVariant::Vector3D: - return v_cast(d)->isNull(); -#endif -#ifndef QT_NO_VECTOR4D - case QVariant::Vector4D: - return v_cast(d)->isNull(); -#endif -#ifndef QT_NO_QUATERNION - case QVariant::Quaternion: - return v_cast(d)->isNull(); -#endif - case QVariant::Icon: - if (qt_widgets_variant_handler) - return qt_widgets_variant_handler->isNull(d); - break; - default: - return qcoreVariantHandler()->isNull(d); - } - return d->is_null; + QGuiVariantIsNull isNull(d); + return QMetaTypeSwitcher::switcher(isNull, d->type, 0); } +// This class is a hack that customizes access to QPixmap, QBitmap and QCursor +template +class QGuiVariantComparator : public QVariantComparator { + typedef QVariantComparator Base; +public: + QGuiVariantComparator(const QVariant::Private *a, const QVariant::Private *b) + : QVariantComparator(a, b) + {} + template + bool delegate(const T *p) + { + if (Q_UNLIKELY(Base::m_a->type == QVariant::Icon || Base::m_a->type == QVariant::SizePolicy)) { + // TODO we need to clean up variant handlers, so they are replacament, not extension + if (Q_LIKELY(qt_widgets_variant_handler)) + return qt_widgets_variant_handler->compare(Base::m_a, Base::m_b); + } + return Base::delegate(p); + } + bool delegate(const QPixmap*) + { + return v_cast(Base::m_a)->cacheKey() == v_cast(Base::m_b)->cacheKey(); + } + bool delegate(const QBitmap*) + { + return v_cast(Base::m_a)->cacheKey() == v_cast(Base::m_b)->cacheKey(); + } +#ifndef QT_NO_CURSOR + bool delegate(const QCursor*) + { + return v_cast(Base::m_a)->shape() == v_cast(Base::m_b)->shape(); + } +#endif + bool delegate(const void *p) { return Base::delegate(p); } +}; + static bool compare(const QVariant::Private *a, const QVariant::Private *b) { - Q_ASSERT(a->type == b->type); - switch(a->type) { - case QVariant::Cursor: -#ifndef QT_NO_CURSOR - return v_cast(a)->shape() == v_cast(b)->shape(); -#endif - case QVariant::Bitmap: - return v_cast(a)->cacheKey() - == v_cast(b)->cacheKey(); - case QVariant::Polygon: - return *v_cast(a) == *v_cast(b); - case QVariant::Region: - return *v_cast(a) == *v_cast(b); - case QVariant::Font: - return *v_cast(a) == *v_cast(b); - case QVariant::Pixmap: - return v_cast(a)->cacheKey() == v_cast(b)->cacheKey(); - case QVariant::Image: - return *v_cast(a) == *v_cast(b); - case QVariant::Brush: - return *v_cast(a) == *v_cast(b); - case QVariant::Color: - return *v_cast(a) == *v_cast(b); - case QVariant::Palette: - return *v_cast(a) == *v_cast(b); -#ifndef QT_NO_ICON - case QVariant::Icon: - /* QIcon::operator==() cannot be reasonably implemented for QIcon, - * so we always return false. */ - return false; -#endif - case QVariant::Matrix: - return *v_cast(a) == *v_cast(b); - case QVariant::Transform: - return *v_cast(a) == *v_cast(b); - case QVariant::TextFormat: - return *v_cast(a) == *v_cast(b); - case QVariant::TextLength: - return *v_cast(a) == *v_cast(b); -#ifndef QT_NO_SHORTCUT - case QVariant::KeySequence: - return *v_cast(a) == *v_cast(b); -#endif - case QVariant::Pen: - return *v_cast(a) == *v_cast(b); -#ifndef QT_NO_MATRIX4X4 - case QVariant::Matrix4x4: - return *v_cast(a) == *v_cast(b); -#endif -#ifndef QT_NO_VECTOR2D - case QVariant::Vector2D: - return *v_cast(a) == *v_cast(b); -#endif -#ifndef QT_NO_VECTOR3D - case QVariant::Vector3D: - return *v_cast(a) == *v_cast(b); -#endif -#ifndef QT_NO_VECTOR4D - case QVariant::Vector4D: - return *v_cast(a) == *v_cast(b); -#endif -#ifndef QT_NO_QUATERNION - case QVariant::Quaternion: - return *v_cast(a) == *v_cast(b); -#endif - case QVariant::SizePolicy: - if (qt_widgets_variant_handler) - return qt_widgets_variant_handler->compare(a, b); - break; - default: - break; - } - return qcoreVariantHandler()->compare(a, b); + QGuiVariantComparator comparator(a, b); + return QMetaTypeSwitcher::switcher(comparator, a->type, 0); } - - static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, bool *ok) { diff --git a/src/widgets/kernel/qwidgetsvariant.cpp b/src/widgets/kernel/qwidgetsvariant.cpp index 1a21d91829..dcffa0e87e 100644 --- a/src/widgets/kernel/qwidgetsvariant.cpp +++ b/src/widgets/kernel/qwidgetsvariant.cpp @@ -106,6 +106,10 @@ static bool compare(const QVariant::Private *a, const QVariant::Private *b) { Q_ASSERT(a->type == b->type); switch(a->type) { +#ifndef QT_NO_ICON + case QVariant::Icon: + return false; +#endif case QVariant::SizePolicy: return *v_cast(a) == *v_cast(b); default: diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index c4fed9088f..21bdfa7791 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -325,6 +325,10 @@ void tst_QVariant::isNull() QVariant var; QVERIFY( var.isNull() ); + QString str1; + QVariant var1( str1 ); + QVERIFY( var1.isNull() ); + QVariant var2( QString::null ); QVERIFY( var2.isNull() ); @@ -2017,10 +2021,12 @@ void tst_QVariant::userType() QVariant userVar3; qVariantSetValue(userVar3, data2); - QVERIFY(userVar2 != userVar3); + QVERIFY(userVar2 == userVar3); userVar3 = userVar2; QVERIFY(userVar2 == userVar3); } + // At this point all QVariants got destroyed but we have 2 MyType instances. + QCOMPARE(instanceCount, 2); { QVariant userVar; qVariantSetValue(userVar, &data); @@ -2056,10 +2062,9 @@ void tst_QVariant::userType() QVariant myCarrier; qVariantSetValue(myCarrier, data); QCOMPARE(instanceCount, 3); - { QVariant second = myCarrier; - QCOMPARE(instanceCount, 3); + QCOMPARE(instanceCount, 4); second.detach(); QCOMPARE(instanceCount, 4); } @@ -2103,6 +2108,7 @@ void tst_QVariant::userType() QCOMPARE(qvariant_cast(myCarrier), 42); } + // At this point all QVariants got destroyed and MyType objects too. QCOMPARE(instanceCount, 0); } @@ -2701,7 +2707,7 @@ void tst_QVariant::task172061_invalidDate() const struct WontCompare { - int x; + int x,y,z,q,w,e,r,t; }; Q_DECLARE_METATYPE(WontCompare); @@ -2952,7 +2958,12 @@ template void playWithVariant(const T &orig, bool isNull, const QString { QVariant v2 = v; - QCOMPARE(v2, v); + if (!(QTypeInfo::isStatic && QTypeInfo::isComplex)) { + // Type is movable so standard comparison algorithm in QVariant should work + // In a custom type QVariant is not aware of ==operator so it won't be called, + // which may cause problems especially visible when using a not-movable type + QCOMPARE(v2, v); + } QVERIFY(v2.isValid()); QCOMPARE(v2.isNull(), isNull); QCOMPARE(v2.toString(), toString); @@ -2964,7 +2975,12 @@ template void playWithVariant(const T &orig, bool isNull, const QString v = QVariant(); QCOMPARE(v3, v); v = v2; - QCOMPARE(v, v2); + if (!(QTypeInfo::isStatic && QTypeInfo::isComplex)) { + // Type is movable so standard comparison algorithm in QVariant should work + // In a custom type QVariant is not aware of ==operator so it won't be called, + // which may cause problems especially visible when using a not-movable type + QCOMPARE(v2, v); + } QCOMPARE(qvariant_cast(v2), qvariant_cast(v)); QCOMPARE(v2.toString(), toString); v3 = qVariantFromValue(orig); diff --git a/tests/auto/testlib/selftests/cmptest/tst_cmptest.cpp b/tests/auto/testlib/selftests/cmptest/tst_cmptest.cpp index 4d38aa1646..70396a75b3 100644 --- a/tests/auto/testlib/selftests/cmptest/tst_cmptest.cpp +++ b/tests/auto/testlib/selftests/cmptest/tst_cmptest.cpp @@ -80,8 +80,10 @@ void tst_Cmptest::compare_pointerfuncs() Q_DECLARE_METATYPE(QVariant) -class PhonyClass -{}; +struct PhonyClass +{ + int i; +}; void tst_Cmptest::compare_tostring_data() { @@ -108,9 +110,11 @@ void tst_Cmptest::compare_tostring_data() << QVariant(QVariant::Type(qRegisterMetaType("PhonyClass"))) ; + PhonyClass fake1 = {1}; + PhonyClass fake2 = {2}; QTest::newRow("both non-null user type") - << QVariant(qRegisterMetaType("PhonyClass"), (const void*)0) - << QVariant(qRegisterMetaType("PhonyClass"), (const void*)0) + << QVariant(qRegisterMetaType("PhonyClass"), (const void*)&fake1) + << QVariant(qRegisterMetaType("PhonyClass"), (const void*)&fake2) ; }