497 lines
15 KiB
C++
497 lines
15 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
** Copyright (C) 2016 Intel Corporation.
|
|
** Contact: https://www.qt.io/licensing/
|
|
**
|
|
** 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 The Qt Company. For licensing terms
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
** included in the packaging of this file. Please review the following
|
|
** information to ensure the GNU General Public License requirements will
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
** https://www.gnu.org/licenses/gpl-3.0.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 <QtCore/qglobal.h>
|
|
#include <QtCore/qvariant.h>
|
|
#include <QtCore/private/qmetatype_p.h>
|
|
#include <QtCore/qdebug.h>
|
|
|
|
#include "qmetatypeswitcher_p.h"
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
template<typename T>
|
|
struct QVariantIntegrator
|
|
{
|
|
static const bool CanUseInternalSpace = sizeof(T) <= sizeof(QVariant::Private::Data)
|
|
&& ((QTypeInfoQuery<T>::isRelocatable) || std::is_enum<T>::value);
|
|
typedef std::integral_constant<bool, CanUseInternalSpace> CanUseInternalSpace_t;
|
|
};
|
|
Q_STATIC_ASSERT(QVariantIntegrator<double>::CanUseInternalSpace);
|
|
Q_STATIC_ASSERT(QVariantIntegrator<long int>::CanUseInternalSpace);
|
|
Q_STATIC_ASSERT(QVariantIntegrator<qulonglong>::CanUseInternalSpace);
|
|
|
|
#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 <typename T>
|
|
inline T *v_cast(const QVariant::Private *nd, T * = 0)
|
|
{
|
|
QVariant::Private *d = const_cast<QVariant::Private *>(nd);
|
|
return !QVariantIntegrator<T>::CanUseInternalSpace
|
|
? static_cast<T *>(d->data.shared->ptr)
|
|
: static_cast<T *>(static_cast<void *>(&d->data.c));
|
|
}
|
|
|
|
#else // every other compiler in this world
|
|
|
|
template <typename T>
|
|
inline const T *v_cast(const QVariant::Private *d, T * = 0)
|
|
{
|
|
return !QVariantIntegrator<T>::CanUseInternalSpace
|
|
? static_cast<const T *>(d->data.shared->ptr)
|
|
: static_cast<const T *>(static_cast<const void *>(&d->data.c));
|
|
}
|
|
|
|
template <typename T>
|
|
inline T *v_cast(QVariant::Private *d, T * = 0)
|
|
{
|
|
return !QVariantIntegrator<T>::CanUseInternalSpace
|
|
? static_cast<T *>(d->data.shared->ptr)
|
|
: static_cast<T *>(static_cast<void *>(&d->data.c));
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//a simple template that avoids to allocate 2 memory chunks when creating a QVariant
|
|
template <class T> class QVariantPrivateSharedEx : public QVariant::PrivateShared
|
|
{
|
|
public:
|
|
QVariantPrivateSharedEx() : QVariant::PrivateShared(&m_t), m_t() { }
|
|
QVariantPrivateSharedEx(const T&t) : QVariant::PrivateShared(&m_t), m_t(t) { }
|
|
|
|
private:
|
|
T m_t;
|
|
};
|
|
|
|
template <class T>
|
|
inline void v_construct_helper(QVariant::Private *x, const T &t, std::true_type)
|
|
{
|
|
new (&x->data) T(t);
|
|
x->is_shared = false;
|
|
}
|
|
|
|
template <class T>
|
|
inline void v_construct_helper(QVariant::Private *x, const T &t, std::false_type)
|
|
{
|
|
x->data.shared = new QVariantPrivateSharedEx<T>(t);
|
|
x->is_shared = true;
|
|
}
|
|
|
|
template <class T>
|
|
inline void v_construct_helper(QVariant::Private *x, std::true_type)
|
|
{
|
|
new (&x->data) T();
|
|
x->is_shared = false;
|
|
}
|
|
|
|
template <class T>
|
|
inline void v_construct_helper(QVariant::Private *x, std::false_type)
|
|
{
|
|
x->data.shared = new QVariantPrivateSharedEx<T>;
|
|
x->is_shared = true;
|
|
}
|
|
|
|
template <class T>
|
|
inline void v_construct(QVariant::Private *x, const T &t)
|
|
{
|
|
// dispatch
|
|
v_construct_helper(x, t, typename QVariantIntegrator<T>::CanUseInternalSpace_t());
|
|
}
|
|
|
|
// constructs a new variant if copy is 0, otherwise copy-constructs
|
|
template <class T>
|
|
inline void v_construct(QVariant::Private *x, const void *copy, T * = 0)
|
|
{
|
|
if (copy)
|
|
v_construct<T>(x, *static_cast<const T *>(copy));
|
|
else
|
|
v_construct_helper<T>(x, typename QVariantIntegrator<T>::CanUseInternalSpace_t());
|
|
}
|
|
|
|
// deletes the internal structures
|
|
template <class T>
|
|
inline void v_clear(QVariant::Private *d, T* = 0)
|
|
{
|
|
|
|
if (!QVariantIntegrator<T>::CanUseInternalSpace) {
|
|
//now we need to cast
|
|
//because QVariant::PrivateShared doesn't have a virtual destructor
|
|
delete static_cast<QVariantPrivateSharedEx<T>*>(d->data.shared);
|
|
} else {
|
|
v_cast<T>(d)->~T();
|
|
}
|
|
|
|
}
|
|
|
|
template <typename T>
|
|
struct PrimitiveIsNull
|
|
{
|
|
public:
|
|
static bool isNull(const QVariant::Private *d)
|
|
{
|
|
return d->is_null;
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct PrimitiveIsNull<std::nullptr_t>
|
|
{
|
|
public:
|
|
static bool isNull(const QVariant::Private *)
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
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 *, 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<typename T>
|
|
bool delegate(const T*)
|
|
{
|
|
return FilteredComparator<T>::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 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>
|
|
class HasIsNullMethod {
|
|
struct Yes { char unused[1]; };
|
|
struct No { char unused[2]; };
|
|
Q_STATIC_ASSERT(sizeof(Yes) != sizeof(No));
|
|
|
|
template<class C> static decltype(static_cast<const C*>(0)->isNull(), Yes()) test(int);
|
|
template<class C> static No test(...);
|
|
public:
|
|
static const bool Value = (sizeof(test<T>(0)) == sizeof(Yes));
|
|
};
|
|
|
|
// 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);
|
|
struct SelfTestFinal1 Q_DECL_FINAL { bool isNull() const; };
|
|
Q_STATIC_ASSERT(HasIsNullMethod<SelfTestFinal1>::Value);
|
|
struct SelfTestFinal2 Q_DECL_FINAL {};
|
|
Q_STATIC_ASSERT(!HasIsNullMethod<SelfTestFinal2>::Value);
|
|
struct SelfTestFinal3 Q_DECL_FINAL : public SelfTest1 {};
|
|
Q_STATIC_ASSERT(HasIsNullMethod<SelfTestFinal3>::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 PrimitiveIsNull<T>::isNull(d);
|
|
}
|
|
};
|
|
|
|
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*)
|
|
{
|
|
return CallIsNull<T>::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 Filter>
|
|
class QVariantConstructor
|
|
{
|
|
template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
|
|
struct FilteredConstructor {
|
|
FilteredConstructor(const QVariantConstructor &tc)
|
|
{
|
|
v_construct<T>(tc.m_x, tc.m_copy);
|
|
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::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 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 *)
|
|
{
|
|
// 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<typename T>
|
|
void delegate(const T*)
|
|
{
|
|
FilteredDestructor<T> 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 Filter>
|
|
class QVariantDebugStream
|
|
{
|
|
template<typename T, bool IsAcceptedType = Filter::template Acceptor<T>::IsAccepted>
|
|
struct Filtered {
|
|
Filtered(QDebug dbg, QVariant::Private *d)
|
|
{
|
|
dbg.nospace() << *v_cast<T>(d);
|
|
}
|
|
};
|
|
template<typename T>
|
|
struct Filtered<T, /* IsAcceptedType = */ false> {
|
|
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<typename T>
|
|
void delegate(const T*)
|
|
{
|
|
Filtered<T> 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
|