summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qvariant_p.h
diff options
context:
space:
mode:
authorJędrzej Nowacki <jedrzej.nowacki@nokia.com>2011-10-12 17:32:26 +0200
committerQt by Nokia <qt-info@nokia.com>2011-11-09 10:11:34 +0100
commit8fd64d22ac7892b061a09c42c72aacf033b80876 (patch)
tree9c0d04049188ad5ff726419279ee88c3fdd0d1bf /src/corelib/kernel/qvariant_p.h
parent2b39be6dd5d111482e5df06ac6dea18ca338d9f0 (diff)
Make usage of internal QVariant space.
Each QVariant instance has internal storage which may be used for well-know basic types. This patch changes the behavior by delegating type dependent operation to QMetaType class which knows more types than QVariant itself. The patch significantly reduce amount of code in QVariant implementation. There are few side effects of this patch: - better performance: * for Core types when using Gui (QGuiVariant is able to construct Core types) * for small custom types (QVariant::Private::Data is used for all types that has size small enough) - comparing two QVariants can give different result for small custom types (binary comparison instead of pointer comparison) Change-Id: Ic17fa500d6a882110bfba896fd456c8e6c7a63a9 Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Diffstat (limited to 'src/corelib/kernel/qvariant_p.h')
-rw-r--r--src/corelib/kernel/qvariant_p.h292
1 files changed, 292 insertions, 0 deletions
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 <QtCore/qglobal.h>
#include <QtCore/qvariant.h>
+#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 Filter>
+class QVariantComparator {
+ template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
+ struct FilteredComparator {
+ static bool compare(const QVariant::Private *a, const QVariant::Private *b)
+ {
+ return *v_cast<T>(a) == *v_cast<T>(b);
+ }
+ };
+ template<typename T>
+ struct FilteredComparator<T, /* IsAcceptedType = */ false> {
+ 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<void *const *>(a_ptr) == *static_cast<void *const *>(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<typename T>
+ bool delegate(const T*)
+ {
+ return FilteredComparator<T>::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 Filter>
+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<typename T, bool IsClass = QTypeInfo<T>::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<class C, C> struct TypeCheck {};
+
+ template<class C> static Yes test(...);
+ template<class C> static No test(TypeCheck<bool (FallbackMixin::*)() const, &C::isNull> *);
+ public:
+ static const bool Value = (sizeof(test<Derived>(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<typename T>
+ class HasIsNullMethod<T, /* IsClass = */ false> {
+ public:
+ static const bool Value = false;
+ };
+
+ // TODO This part should go to autotests during HasIsNullMethod generalization.
+ Q_STATIC_ASSERT(!HasIsNullMethod<bool>::Value);
+ struct SelfTest1 { bool isNull() const; };
+ Q_STATIC_ASSERT(HasIsNullMethod<SelfTest1>::Value);
+ struct SelfTest2 {};
+ Q_STATIC_ASSERT(!HasIsNullMethod<SelfTest2>::Value);
+ struct SelfTest3 : public SelfTest1 {};
+ Q_STATIC_ASSERT(HasIsNullMethod<SelfTest3>::Value);
+
+ template<typename T, bool HasIsNull = HasIsNullMethod<T>::Value>
+ struct CallFilteredIsNull
+ {
+ static bool isNull(const QVariant::Private *d)
+ {
+ return v_cast<T>(d)->isNull();
+ }
+ };
+ template<typename T>
+ struct CallFilteredIsNull<T, /* HasIsNull = */ false>
+ {
+ static bool isNull(const QVariant::Private *d)
+ {
+ return d->is_null;
+ }
+ };
+
+ template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
+ struct CallIsNull
+ {
+ static bool isNull(const QVariant::Private *d)
+ {
+ return CallFilteredIsNull<T>::isNull(d);
+ }
+ };
+ template<typename T>
+ struct CallIsNull<T, /* IsAcceptedType = */ false>
+ {
+ static bool isNull(const QVariant::Private *d)
+ {
+ return CallFilteredIsNull<T, false>::isNull(d);
+ }
+ };
+
+public:
+ QVariantIsNull(const QVariant::Private *d)
+ : m_d(d)
+ {}
+ template<typename T>
+ bool delegate(const T*)
+ {
+ CallIsNull<T> 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 Filter>
+class QVariantConstructor
+{
+ template<typename T, bool IsSmall = (sizeof(T) <= sizeof(QVariant::Private::Data))>
+ struct CallConstructor {};
+
+ template<typename T>
+ struct CallConstructor<T, /* IsSmall = */ true>
+ {
+ CallConstructor(const QVariantConstructor &tc)
+ {
+ if (tc.m_copy)
+ new (&tc.m_x->data.ptr) T(*static_cast<const T*>(tc.m_copy));
+ else
+ new (&tc.m_x->data.ptr) T();
+ tc.m_x->is_shared = false;
+ }
+ };
+
+ template<typename T>
+ struct CallConstructor<T, /* IsSmall = */ false>
+ {
+ CallConstructor(const QVariantConstructor &tc)
+ {
+ Q_STATIC_ASSERT(QTypeInfo<T>::isComplex);
+ tc.m_x->data.shared = tc.m_copy ? new QVariantPrivateSharedEx<T>(*static_cast<const T*>(tc.m_copy))
+ : new QVariantPrivateSharedEx<T>;
+ tc.m_x->is_shared = true;
+ }
+ };
+
+ template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
+ struct FilteredConstructor {
+ FilteredConstructor(const QVariantConstructor &tc)
+ {
+ CallConstructor<T> tmp(tc);
+ tc.m_x->is_null = !tc.m_copy;
+ }
+ };
+ template<typename T>
+ struct FilteredConstructor<T, /* IsAcceptedType = */ false> {
+ 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<typename T>
+ void delegate(const T*)
+ {
+ FilteredConstructor<T>(*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 Filter>
+class QVariantDestructor
+{
+ template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
+ struct FilteredDestructor {
+ FilteredDestructor(QVariant::Private *d)
+ {
+ v_clear<T>(d);
+ }
+ };
+ template<typename T>
+ struct FilteredDestructor<T, /* IsAcceptedType = */ false> {
+ 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<typename T>
+ void delegate(const T*)
+ {
+ FilteredDestructor<T> 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