Add template specialization of QMetaType for QObject derived pointers.

This makes it possible to do things like

QVariant::fromValue(new SomeObject);

without first using Q_DECLARE_METATYPE(Something*)

This functionality was originally part of
http://codereview.qt-project.org/#change,11710 but was rejected
because the functionality was based on specialization of
QVariant::fromValue which could be dangerous.

This new implementation doesn't have such danger.

Change-Id: I83fe941b6984be54469bc6b9191f6eacaceaa036
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
bb10
Stephen Kelly 2012-03-02 14:48:09 +01:00 committed by Qt by Nokia
parent 9a5c728e46
commit eb24dfcccb
3 changed files with 83 additions and 24 deletions

View File

@ -364,33 +364,11 @@ void qMetaTypeLoadHelper(QDataStream &stream, void *t)
template <> inline void qMetaTypeLoadHelper<void>(QDataStream &, void *) {}
#endif // QT_NO_DATASTREAM
template <typename T>
struct QMetaTypeId
{
enum { Defined = 0 };
};
template <typename T>
struct QMetaTypeId2
{
enum { Defined = QMetaTypeId<T>::Defined };
static inline int qt_metatype_id() { return QMetaTypeId<T>::qt_metatype_id(); }
};
class QObject;
class QWidget;
namespace QtPrivate {
template <typename T, bool Defined = QMetaTypeId2<T>::Defined>
struct QMetaTypeIdHelper {
static inline int qt_metatype_id()
{ return QMetaTypeId2<T>::qt_metatype_id(); }
};
template <typename T> struct QMetaTypeIdHelper<T, false> {
static inline int qt_metatype_id()
{ return -1; }
};
namespace QtPrivate
{
template<typename T>
struct IsPointerToTypeDerivedFromQObject
{
@ -427,6 +405,38 @@ namespace QtPrivate {
Q_STATIC_ASSERT_X(sizeof(T), "Type argument of Q_DECLARE_METATYPE(T*) must be fully defined");
enum { Value = sizeof(checkType(static_cast<T*>(0))) == sizeof(yes_type) };
};
}
template <typename T, bool = QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value>
struct QMetaTypeIdQObject
{
enum {
Defined = 0
};
};
template <typename T>
struct QMetaTypeId : public QMetaTypeIdQObject<T>
{
};
template <typename T>
struct QMetaTypeId2
{
enum { Defined = QMetaTypeId<T>::Defined };
static inline int qt_metatype_id() { return QMetaTypeId<T>::qt_metatype_id(); }
};
namespace QtPrivate {
template <typename T, bool Defined = QMetaTypeId2<T>::Defined>
struct QMetaTypeIdHelper {
static inline int qt_metatype_id()
{ return QMetaTypeId2<T>::qt_metatype_id(); }
};
template <typename T> struct QMetaTypeIdHelper<T, false> {
static inline int qt_metatype_id()
{ return -1; }
};
// Function pointers don't derive from QObject
template <class Result> struct IsPointerToTypeDerivedFromQObject<Result(*)()> { enum { Value = false }; };
@ -501,6 +511,23 @@ inline int qRegisterMetaType(
#endif
}
template <typename T>
struct QMetaTypeIdQObject<T*, /* isPointerToTypeDerivedFromQObject */ true>
{
enum {
Defined = 1
};
static int qt_metatype_id()
{
static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
if (!metatype_id.load())
metatype_id.storeRelease(qRegisterMetaType<T*>(QByteArray(T::staticMetaObject.className() + QByteArrayLiteral("*")).constData(),
reinterpret_cast<T**>(quintptr(-1))));
return metatype_id.loadAcquire();
}
};
#ifndef QT_NO_DATASTREAM
template <typename T>
inline int qRegisterMetaTypeStreamOperators()

View File

@ -100,12 +100,29 @@ private slots:
struct Foo { int i; };
class CustomQObject : public QObject
{
Q_OBJECT
public:
CustomQObject(QObject *parent = 0)
: QObject(parent)
{
}
};
class CustomNonQObject {};
void tst_QMetaType::defined()
{
QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1);
QCOMPARE(int(QMetaTypeId2<Foo>::Defined), 0);
QCOMPARE(int(QMetaTypeId2<void*>::Defined), 1);
QCOMPARE(int(QMetaTypeId2<int*>::Defined), 0);
QVERIFY(QMetaTypeId2<CustomQObject*>::Defined);
QVERIFY(!QMetaTypeId2<CustomQObject>::Defined);
QVERIFY(!QMetaTypeId2<CustomNonQObject>::Defined);
QVERIFY(!QMetaTypeId2<CustomNonQObject*>::Defined);
}
struct Bar

View File

@ -2541,8 +2541,23 @@ public:
};
Q_DECLARE_METATYPE(CustomQObjectDerived*)
class CustomQObjectDerivedNoMetaType : public CustomQObject {
Q_OBJECT
public:
CustomQObjectDerivedNoMetaType(QObject *parent = 0) : CustomQObject(parent) {}
};
void tst_QVariant::qvariant_cast_QObject_derived()
{
{
CustomQObjectDerivedNoMetaType *object = new CustomQObjectDerivedNoMetaType(this);
QVariant data = QVariant::fromValue(object);
QVERIFY(data.userType() == qMetaTypeId<CustomQObjectDerivedNoMetaType*>());
QCOMPARE(data.value<QObject *>(), object);
QCOMPARE(data.value<CustomQObjectDerivedNoMetaType *>(), object);
QCOMPARE(data.value<CustomQObject *>(), object);
QVERIFY(data.value<CustomQWidget*>() == 0);
}
{
CustomQObjectDerived *object = new CustomQObjectDerived(this);
QVariant data = QVariant::fromValue(object);