/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QVARIANT_P_H #define QVARIANT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists purely as an // implementation detail. This header file may change from version to // version without notice, or even be removed. // // We mean it. // #include #include #include #include #include "qmetatypeswitcher_p.h" QT_BEGIN_NAMESPACE namespace { template struct QVariantIntegrator { static const bool CanUseInternalSpace = sizeof(T) <= sizeof(QVariant::Private::Data) && (!QTypeInfo::isStatic); }; Q_STATIC_ASSERT(QVariantIntegrator::CanUseInternalSpace); Q_STATIC_ASSERT(QVariantIntegrator::CanUseInternalSpace); Q_STATIC_ASSERT(QVariantIntegrator::CanUseInternalSpace); } // namespace #ifdef Q_CC_SUN // Sun CC picks the wrong overload, so introduce awful hack // takes a type, returns the internal void* pointer cast // to a pointer of the input type template inline T *v_cast(const QVariant::Private *nd, T * = 0) { QVariant::Private *d = const_cast(nd); return !QVariantIntegrator::CanUseInternalSpace ? static_cast(d->data.shared->ptr) : static_cast(static_cast(&d->data.c)); } #else // every other compiler in this world template inline const T *v_cast(const QVariant::Private *d, T * = 0) { return !QVariantIntegrator::CanUseInternalSpace ? static_cast(d->data.shared->ptr) : static_cast(static_cast(&d->data.c)); } template inline T *v_cast(QVariant::Private *d, T * = 0) { return !QVariantIntegrator::CanUseInternalSpace ? static_cast(d->data.shared->ptr) : static_cast(static_cast(&d->data.c)); } #endif //a simple template that avoids to allocate 2 memory chunks when creating a QVariant template class QVariantPrivateSharedEx : public QVariant::PrivateShared { public: QVariantPrivateSharedEx() : QVariant::PrivateShared(&m_t) { } QVariantPrivateSharedEx(const T&t) : QVariant::PrivateShared(&m_t), m_t(t) { } private: T m_t; }; // constructs a new variant if copy is 0, otherwise copy-constructs template inline void v_construct(QVariant::Private *x, const void *copy, T * = 0) { if (!QVariantIntegrator::CanUseInternalSpace) { x->data.shared = copy ? new QVariantPrivateSharedEx(*static_cast(copy)) : new QVariantPrivateSharedEx; x->is_shared = true; } else { if (copy) new (&x->data.ptr) T(*static_cast(copy)); else new (&x->data.ptr) T; } } template inline void v_construct(QVariant::Private *x, const T &t) { if (!QVariantIntegrator::CanUseInternalSpace) { x->data.shared = new QVariantPrivateSharedEx(t); x->is_shared = true; } else { new (&x->data.ptr) T(t); } } // deletes the internal structures template inline void v_clear(QVariant::Private *d, T* = 0) { if (!QVariantIntegrator::CanUseInternalSpace) { //now we need to cast //because QVariant::PrivateShared doesn't have a virtual destructor delete static_cast*>(d->data.shared); } else { v_cast(d)->~T(); } } 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 *, const QVariant::Private *) { // It is not possible to construct a QVariant containing not fully defined type Q_ASSERT(false); return false; } }; 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*) { Q_ASSERT(false); return true; } bool delegate(const QMetaTypeSwitcher::UnknownType*) { return true; // for historical reason invalid variant == invalid variant } bool delegate(const QMetaTypeSwitcher::NotBuiltinType*) { return false; } 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? #if defined(Q_COMPILER_DECLTYPE) // C++11 version template class HasIsNullMethod { struct Yes { char unused[1]; }; struct No { char unused[2]; }; Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); template static decltype(static_cast(0)->isNull(), Yes()) test(int); template static No test(...); public: static const bool Value = (sizeof(test(0)) == sizeof(Yes)); }; #elif defined(Q_CC_MSVC) && _MSC_VER >= 1400 // MSVC 2005, 2008 version: no decltype, but 'sealed' classes (>=2010 has decltype) template class HasIsNullMethod { struct Yes { char unused[1]; }; struct No { char unused[2]; }; Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No)); template static Yes test(char (*)[(&C::isNull == 0) + 1]); template static No test(...); public: static const bool Value = (sizeof(test(0)) == sizeof(Yes)); }; #else // C++98 version (doesn't work for final classes) 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 {}; // <- doesn't work for final classes 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; }; #endif // 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); struct SelfTestFinal1 Q_DECL_FINAL { bool isNull() const; }; Q_STATIC_ASSERT(HasIsNullMethod::Value); struct SelfTestFinal2 Q_DECL_FINAL {}; Q_STATIC_ASSERT(!HasIsNullMethod::Value); struct SelfTestFinal3 Q_DECL_FINAL : 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*) { return CallIsNull::isNull(m_d); } // we need that as sizof(void) is undefined and it is needed in HasIsNullMethod bool delegate(const void *) { Q_ASSERT(false); return m_d->is_null; } bool delegate(const QMetaTypeSwitcher::UnknownType *) { return m_d->is_null; } bool delegate(const QMetaTypeSwitcher::NotBuiltinType *) { // QVariantIsNull is used only for built-in types Q_ASSERT(false); return m_d->is_null; } protected: const QVariant::Private *m_d; }; template class QVariantConstructor { template::CanUseInternalSpace> 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 || sizeof(T) > sizeof(QVariant::Private::Data)); 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::NotBuiltinType*) { // QVariantConstructor is used only for built-in types. Q_ASSERT(false); } void delegate(const void*) { qWarning("Trying to create a QVariant instance of QMetaType::Void type, an invalid QVariant will be constructed instead"); m_x->type = QMetaType::UnknownType; m_x->is_shared = false; m_x->is_null = !m_copy; } void delegate(const QMetaTypeSwitcher::UnknownType*) { if (m_x->type != QMetaType::UnknownType) { qWarning("Trying to construct an instance of an invalid type, type id: %i", m_x->type); m_x->type = QMetaType::UnknownType; } 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 *) { // It is not possible to create not accepted type Q_ASSERT(false); } }; 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::NotBuiltinType*) { // QVariantDestructor class is used only for a built-in type Q_ASSERT(false); } // Ignore nonconstructible type void delegate(const QMetaTypeSwitcher::UnknownType*) {} void delegate(const void*) { Q_ASSERT(false); } private: QVariant::Private *m_d; }; namespace QVariantPrivate { Q_CORE_EXPORT void registerHandler(const int /* Modules::Names */ name, const QVariant::Handler *handler); } #if !defined(QT_NO_DEBUG_STREAM) template class QVariantDebugStream { template::IsAccepted> struct Filtered { Filtered(QDebug dbg, QVariant::Private *d) { dbg.nospace() << *v_cast(d); } }; template struct Filtered { Filtered(QDebug /* dbg */, QVariant::Private *) { // It is not possible to construct not acccepted type, QVariantConstructor creates an invalid variant for them Q_ASSERT(false); } }; public: QVariantDebugStream(QDebug dbg, QVariant::Private *d) : m_debugStream(dbg) , m_d(d) {} template void delegate(const T*) { Filtered streamIt(m_debugStream, m_d); Q_UNUSED(streamIt); } void delegate(const QMetaTypeSwitcher::NotBuiltinType*) { // QVariantDebugStream class is used only for a built-in type Q_ASSERT(false); } void delegate(const QMetaTypeSwitcher::UnknownType*) { m_debugStream.nospace() << "QVariant::Invalid"; } void delegate(const void*) { Q_ASSERT(false); } private: QDebug m_debugStream; QVariant::Private *m_d; }; #endif QT_END_NAMESPACE #endif // QVARIANT_P_H