diff options
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 191 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype.h | 33 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype_p.h | 12 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetatype/qmetatype.pro | 2 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp | 246 |
5 files changed, 413 insertions, 71 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 76e3d0d014..82952919dd 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -1054,45 +1054,40 @@ int QMetaType::registerType(const char *typeName, Deleter deleter, return registerNormalizedType(normalizedTypeName, deleter, creator, destructor, constructor, size, flags, metaObject); } - /*! - \internal - \since 5.0 - \overload - Don't use, kept for binary compatibility + \internal + \since 5.12 - ### TODO Qt6: remove me -*/ -int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, Deleter deleter, - Creator creator, - Destructor destructor, - Constructor constructor, - int size, TypeFlags flags, const QMetaObject *metaObject) + Registers a user type for marshalling, with \a typeName, a \a + \a destructor, a \a constructor, and a \a size. Returns the + type's handle, or -1 if the type could not be registered. + */ +int QMetaType::registerType(const char *typeName, + TypedDestructor destructor, + TypedConstructor constructor, + int size, + TypeFlags flags, + const QMetaObject *metaObject) { - Q_UNUSED(deleter); - Q_UNUSED(creator); +#ifdef QT_NO_QOBJECT + NS(QByteArray) normalizedTypeName = typeName; +#else + NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); +#endif + return registerNormalizedType(normalizedTypeName, destructor, constructor, size, flags, metaObject); } -/*! - \internal - \since 5.5 - - Registers a user type for marshalling, with \a normalizedTypeName, - a \a destructor, a \a constructor, and a \a size. Returns the type's - handle, or -1 if the type could not be registered. - - \note normalizedTypeName is not checked for conformance with - Qt's normalized format, so it must already conform. - */ -int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, - Destructor destructor, - Constructor constructor, - int size, TypeFlags flags, const QMetaObject *metaObject) +static int registerNormalizedType(const NS(QByteArray) &normalizedTypeName, + QMetaType::Destructor destructor, + QMetaType::Constructor constructor, + QMetaType::TypedDestructor typedDestructor, + QMetaType::TypedConstructor typedConstructor, + int size, QMetaType::TypeFlags flags, const QMetaObject *metaObject) { QVector<QCustomTypeInfo> *ct = customTypes(); - if (!ct || normalizedTypeName.isEmpty() || !destructor || !constructor) + if (!ct || normalizedTypeName.isEmpty() || (!destructor && !typedDestructor) || (!constructor && !typedConstructor)) return -1; int idx = qMetaTypeStaticType(normalizedTypeName.constData(), @@ -1100,13 +1095,13 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, int previousSize = 0; QMetaType::TypeFlags::Int previousFlags = 0; - if (idx == UnknownType) { + if (idx == QMetaType::UnknownType) { QWriteLocker locker(customTypesLock()); int posInVector = -1; idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), normalizedTypeName.size(), &posInVector); - if (idx == UnknownType) { + if (idx == QMetaType::UnknownType) { QCustomTypeInfo inf; inf.typeName = normalizedTypeName; #ifndef QT_NO_DATASTREAM @@ -1114,30 +1109,32 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, inf.saveOp = 0; #endif inf.alias = -1; + inf.typedConstructor = typedConstructor; + inf.typedDestructor = typedDestructor; inf.constructor = constructor; inf.destructor = destructor; inf.size = size; inf.flags = flags; inf.metaObject = metaObject; if (posInVector == -1) { - idx = ct->size() + User; + idx = ct->size() + QMetaType::User; ct->append(inf); } else { - idx = posInVector + User; + idx = posInVector + QMetaType::User; ct->data()[posInVector] = inf; } return idx; } - if (idx >= User) { - previousSize = ct->at(idx - User).size; - previousFlags = ct->at(idx - User).flags; + if (idx >= QMetaType::User) { + previousSize = ct->at(idx - QMetaType::User).size; + previousFlags = ct->at(idx - QMetaType::User).flags; // Set new/additional flags in case of old library/app. // Ensures that older code works in conjunction with new Qt releases // requiring the new flags. if (flags != previousFlags) { - QCustomTypeInfo &inf = ct->data()[idx - User]; + QCustomTypeInfo &inf = ct->data()[idx - QMetaType::User]; inf.flags |= flags; if (metaObject) inf.metaObject = metaObject; @@ -1145,7 +1142,7 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, } } - if (idx < User) { + if (idx < QMetaType::User) { previousSize = QMetaType::sizeOf(idx); previousFlags = QMetaType::typeFlags(idx); } @@ -1158,8 +1155,8 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, } // these flags cannot change in a binary compatible way: - const int binaryCompatibilityFlag = PointerToQObject | IsEnumeration | SharedPointerToQObject - | WeakPointerToQObject | TrackingPointerToQObject; + const int binaryCompatibilityFlag = QMetaType::PointerToQObject | QMetaType::IsEnumeration | QMetaType::SharedPointerToQObject + | QMetaType::WeakPointerToQObject | QMetaType::TrackingPointerToQObject; if (Q_UNLIKELY((previousFlags ^ flags) & binaryCompatibilityFlag)) { const char *msg = "QMetaType::registerType: Binary compatibility break. " @@ -1173,6 +1170,66 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, } /*! + \internal + \since 5.0 + \overload + Don't use, kept for binary compatibility + + ### TODO Qt6: remove me +*/ +int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, Deleter deleter, + Creator creator, + Destructor destructor, + Constructor constructor, + int size, TypeFlags flags, const QMetaObject *metaObject) +{ + Q_UNUSED(deleter); + Q_UNUSED(creator); + return registerNormalizedType(normalizedTypeName, destructor, constructor, size, flags, metaObject); +} + + +/*! + \internal + \since 5.5 + + Registers a user type for marshalling, with \a normalizedTypeName, + a \a destructor, a \a constructor, and a \a size. Returns the type's + handle, or -1 if the type could not be registered. + + \note normalizedTypeName is not checked for conformance with + Qt's normalized format, so it must already conform. + + ### TODO Qt6: remove me + */ +int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, + Destructor destructor, + Constructor constructor, + int size, TypeFlags flags, const QMetaObject *metaObject) +{ + return NS(registerNormalizedType)(normalizedTypeName, destructor, constructor, nullptr, nullptr, size, flags, metaObject); +} + +/*! + \internal + \since 5.12 + + Registers a user type for marshalling, with \a normalizedTypeName, + a \a destructor, a \a constructor, and a \a size. Returns the type's + handle, or -1 if the type could not be registered. + + \note normalizedTypeName is not checked for conformance with + Qt's normalized format, so it must already conform. + */ +int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, + TypedDestructor destructor, + TypedConstructor constructor, + int size, TypeFlags flags, const QMetaObject *metaObject) +{ + return NS(registerNormalizedType)(normalizedTypeName, nullptr, nullptr, destructor, constructor, size, flags, metaObject); +} + +/*! \internal \since 4.7 @@ -1850,14 +1907,19 @@ private: static void *customTypeConstructor(const int type, void *where, const void *copy) { QMetaType::Constructor ctor; + QMetaType::TypedConstructor tctor; const QVector<QCustomTypeInfo> * const ct = customTypes(); { QReadLocker locker(customTypesLock()); if (Q_UNLIKELY(type < QMetaType::User || !ct || ct->count() <= type - QMetaType::User)) return 0; - ctor = ct->at(type - QMetaType::User).constructor; + const auto &typeInfo = ct->at(type - QMetaType::User); + ctor = typeInfo.constructor; + tctor = typeInfo.typedConstructor; } - Q_ASSERT_X(ctor, "void *QMetaType::construct(int type, void *where, const void *copy)", "The type was not properly registered"); + Q_ASSERT_X((ctor || tctor) , "void *QMetaType::construct(int type, void *where, const void *copy)", "The type was not properly registered"); + if (Q_UNLIKELY(tctor)) + return tctor(type, where, copy); return ctor(where, copy); } @@ -1943,14 +2005,19 @@ private: static void customTypeDestructor(const int type, void *where) { QMetaType::Destructor dtor; + QMetaType::TypedDestructor tdtor; const QVector<QCustomTypeInfo> * const ct = customTypes(); { QReadLocker locker(customTypesLock()); if (Q_UNLIKELY(type < QMetaType::User || !ct || ct->count() <= type - QMetaType::User)) return; - dtor = ct->at(type - QMetaType::User).destructor; + const auto &typeInfo = ct->at(type - QMetaType::User); + dtor = typeInfo.destructor; + tdtor = typeInfo.typedDestructor; } - Q_ASSERT_X(dtor, "void QMetaType::destruct(int type, void *where)", "The type was not properly registered"); + Q_ASSERT_X((dtor || tdtor), "void QMetaType::destruct(int type, void *where)", "The type was not properly registered"); + if (Q_UNLIKELY(tdtor)) + return tdtor(type, where); dtor(where); } @@ -2363,10 +2430,12 @@ QMetaType QMetaType::typeInfo(const int type) { TypeInfo typeInfo(type); QMetaTypeSwitcher::switcher<void>(typeInfo, type, 0); - return typeInfo.info.constructor ? QMetaType(static_cast<ExtensionFlag>(QMetaType::CreateEx | QMetaType::DestroyEx) - , static_cast<const QMetaTypeInterface *>(0) // typeInfo::info is a temporary variable, we can't return address of it. - , 0 // unused - , 0 // unused + return (typeInfo.info.constructor || typeInfo.info.typedConstructor) + ? QMetaType(static_cast<ExtensionFlag>(QMetaType::CreateEx | QMetaType::DestroyEx | + (typeInfo.info.typedConstructor ? QMetaType::ConstructEx | QMetaType::DestructEx : 0)) + , static_cast<const QMetaTypeInterface *>(nullptr) // typeInfo::info is a temporary variable, we can't return address of it. + , typeInfo.info.typedConstructor + , typeInfo.info.typedDestructor , typeInfo.info.saveOp , typeInfo.info.loadOp , typeInfo.info.constructor @@ -2408,8 +2477,8 @@ QMetaType::QMetaType(const int typeId) Copy constructs a QMetaType object. */ QMetaType::QMetaType(const QMetaType &other) - : m_creator_unused(other.m_creator_unused) - , m_deleter_unused(other.m_deleter_unused) + : m_typedConstructor(other.m_typedConstructor) + , m_typedDestructor(other.m_typedDestructor) , m_saveOp(other.m_saveOp) , m_loadOp(other.m_loadOp) , m_constructor(other.m_constructor) @@ -2424,8 +2493,8 @@ QMetaType::QMetaType(const QMetaType &other) QMetaType &QMetaType::operator =(const QMetaType &other) { - m_creator_unused = other.m_creator_unused; - m_deleter_unused = other.m_deleter_unused; + m_typedConstructor = other.m_typedConstructor; + m_typedDestructor = other.m_typedDestructor; m_saveOp = other.m_saveOp; m_loadOp = other.m_loadOp; m_constructor = other.m_constructor; @@ -2481,6 +2550,8 @@ void *QMetaType::createExtended(const void *copy) const { if (m_typeId == QMetaType::UnknownType) return 0; + if (Q_UNLIKELY(m_typedConstructor && !m_constructor)) + return m_typedConstructor(m_typeId, operator new(m_size), copy); return m_constructor(operator new(m_size), copy); } @@ -2495,7 +2566,10 @@ void *QMetaType::createExtended(const void *copy) const */ void QMetaType::destroyExtended(void *data) const { - m_destructor(data); + if (Q_UNLIKELY(m_typedDestructor && !m_destructor)) + m_typedDestructor(m_typeId, data); + else + m_destructor(data); operator delete(data); } @@ -2508,9 +2582,9 @@ void QMetaType::destroyExtended(void *data) const */ void *QMetaType::constructExtended(void *where, const void *copy) const { - Q_UNUSED(where); - Q_UNUSED(copy); - return 0; + if (m_typedConstructor && !m_constructor) + return m_typedConstructor(m_typeId, where, copy); + return nullptr; } /*! @@ -2522,7 +2596,8 @@ void *QMetaType::constructExtended(void *where, const void *copy) const */ void QMetaType::destructExtended(void *data) const { - Q_UNUSED(data); + if (m_typedDestructor && !m_destructor) + m_typedDestructor(m_typeId, data); } /*! diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 48cbe7f00b..ed7feee775 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -493,8 +493,12 @@ public: typedef void (*Deleter)(void *); typedef void *(*Creator)(const void *); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) typedef void (*Destructor)(void *); - typedef void *(*Constructor)(void *, const void *); + typedef void *(*Constructor)(void *, const void *); // TODO Qt6: remove me +#endif + typedef void (*TypedDestructor)(int, void *); + typedef void *(*TypedConstructor)(int, void *, const void *); typedef void (*SaveOperator)(QDataStream &, const void *); typedef void (*LoadOperator)(QDataStream &, void *); @@ -513,6 +517,12 @@ public: int size, QMetaType::TypeFlags flags, const QMetaObject *metaObject); + static int registerType(const char *typeName, + TypedDestructor destructor, + TypedConstructor constructor, + int size, + QMetaType::TypeFlags flags, + const QMetaObject *metaObject); static bool unregisterType(int type); static int registerNormalizedType(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName, Deleter deleter, Creator creator, @@ -526,6 +536,11 @@ public: int size, QMetaType::TypeFlags flags, const QMetaObject *metaObject); + static int registerNormalizedType(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName, TypedDestructor destructor, + TypedConstructor constructor, + int size, + QMetaType::TypeFlags flags, + const QMetaObject *metaObject); static int registerTypedef(const char *typeName, int aliasId); static int registerNormalizedTypedef(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName, int aliasId); static int type(const char *typeName); @@ -683,8 +698,8 @@ public: private: static QMetaType typeInfo(const int type); inline QMetaType(const ExtensionFlag extensionFlags, const QMetaTypeInterface *info, - Creator creator, - Deleter deleter, + TypedConstructor creator, + TypedDestructor deleter, SaveOperator saveOp, LoadOperator loadOp, Constructor constructor, @@ -731,8 +746,8 @@ public: static void unregisterConverterFunction(int from, int to); private: - Creator m_creator_unused; - Deleter m_deleter_unused; + TypedConstructor m_typedConstructor; + TypedDestructor m_typedDestructor; SaveOperator m_saveOp; LoadOperator m_loadOp; Constructor m_constructor; @@ -2163,8 +2178,8 @@ QT_BEGIN_NAMESPACE #undef Q_DECLARE_METATYPE_TEMPLATE_SMART_POINTER_ITER inline QMetaType::QMetaType(const ExtensionFlag extensionFlags, const QMetaTypeInterface *info, - Creator creator, - Deleter deleter, + TypedConstructor creator, + TypedDestructor deleter, SaveOperator saveOp, LoadOperator loadOp, Constructor constructor, @@ -2173,8 +2188,8 @@ inline QMetaType::QMetaType(const ExtensionFlag extensionFlags, const QMetaTypeI uint theTypeFlags, int typeId, const QMetaObject *_metaObject) - : m_creator_unused(creator) - , m_deleter_unused(deleter) + : m_typedConstructor(creator) + , m_typedDestructor(deleter) , m_saveOp(saveOp) , m_loadOp(loadOp) , m_constructor(constructor) diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h index 76f43db8d7..94e9228778 100644 --- a/src/corelib/kernel/qmetatype_p.h +++ b/src/corelib/kernel/qmetatype_p.h @@ -126,11 +126,13 @@ class QMetaTypeInterface public: QMetaType::SaveOperator saveOp; QMetaType::LoadOperator loadOp; - QMetaType::Constructor constructor; + QMetaType::Constructor constructor; // TODO Qt6: remove me QMetaType::Destructor destructor; int size; QMetaType::TypeFlags::Int flags; const QMetaObject *metaObject; + QMetaType::TypedConstructor typedConstructor; + QMetaType::TypedDestructor typedDestructor; }; #ifndef QT_NO_DATASTREAM @@ -161,7 +163,9 @@ public: /*destructor*/(QtMetaTypePrivate::QMetaTypeFunctionHelper<Type, QtMetaTypePrivate::TypeDefinition<Type>::IsAvailable>::Destruct), \ /*size*/(QTypeInfo<Type>::sizeOf), \ /*flags*/QtPrivate::QMetaTypeTypeFlags<Type>::Flags, \ - /*metaObject*/METAOBJECT_DELEGATE(Type) \ + /*metaObject*/METAOBJECT_DELEGATE(Type), \ + /*typedConstructor*/ nullptr, \ + /*typedDestructor*/ nullptr \ } @@ -184,7 +188,9 @@ public: /*destructor*/ 0, \ /*size*/ 0, \ /*flags*/ 0, \ - /*metaObject*/ 0 \ + /*metaObject*/ 0 , \ + /*typedConstructor*/ nullptr, \ + /*typedDestructor*/ nullptr \ } namespace QtMetaTypePrivate { diff --git a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro index ad148ccc7f..d70befecfd 100644 --- a/tests/auto/corelib/kernel/qmetatype/qmetatype.pro +++ b/tests/auto/corelib/kernel/qmetatype/qmetatype.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qmetatype -QT = core testlib +QT = core-private testlib INCLUDEPATH += $$PWD/../../../other/qvariant_common SOURCES = tst_qmetatype.cpp TESTDATA=./typeFlags.bin diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 73618db3f4..fdf3bb541e 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -29,6 +29,8 @@ #include <QtCore> #include <QtTest/QtTest> +#include <QtCore/private/qmetaobject_p.h> +#include <QtCore/private/qmetaobjectbuilder_p.h> #include "tst_qmetatype.h" #include "tst_qvariant_common.h" @@ -38,6 +40,7 @@ #endif #include <algorithm> +#include <memory> // mingw gcc 4.8 also takes way too long, letting the CI system abort the test #if defined(__MINGW32__) @@ -52,12 +55,19 @@ class tst_QMetaType: public QObject Q_PROPERTY(QList<QVariant> prop READ prop WRITE setProp) public: + struct GadgetPropertyType { + QByteArray type; + QByteArray name; + QVariant testData; + }; + tst_QMetaType() { propList << 42 << "Hello"; } QList<QVariant> prop() const { return propList; } void setProp(const QList<QVariant> &list) { propList = list; } private: + void registerGadget(const char * name, const QVector<GadgetPropertyType> &gadgetProperties); QList<QVariant> propList; private slots: @@ -89,6 +99,7 @@ private slots: void flagsBinaryCompatibility5_0(); void construct_data(); void construct(); + void typedConstruct(); void constructCopy_data(); void constructCopy(); void typedefs(); @@ -115,6 +126,128 @@ private slots: void customDebugStream(); }; +struct BaseGenericType +{ + int m_typeId = -1; + virtual void *constructor(int typeId, void *where, const void *copy) = 0; + virtual void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) = 0; + virtual void saveOperator(QDataStream & out) const = 0; + virtual void loadOperator(QDataStream &in) = 0; + virtual ~BaseGenericType() {} +}; + +struct GenericGadgetType : BaseGenericType +{ + void *constructor(int typeId, void *where, const void *copy) override + { + GenericGadgetType *ret = where ? new(where) GenericGadgetType : new GenericGadgetType; + ret->m_typeId = typeId; + if (copy) { + Q_ASSERT(ret->m_typeId == reinterpret_cast<const GenericGadgetType*>(copy)->m_typeId); + *ret = *reinterpret_cast<const GenericGadgetType*>(copy); + } else { + ret->properties = properties; + } + return ret; + } + + void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) override + { + if (_c == QMetaObject::ReadProperty) { + if (_id < properties.size()) { + const auto &prop = properties.at(_id); + QMetaType::destruct(int(prop.userType()), _a[0]); + QMetaType::construct(int(prop.userType()), _a[0], prop.constData()); + } + } else if (_c == QMetaObject::WriteProperty) { + if (_id < properties.size()) { + auto & prop = properties[_id]; + prop = QVariant(prop.userType(), _a[0]); + } + } + } + + void saveOperator(QDataStream & out) const override + { + for (const auto &prop : properties) + out << prop; + } + + void loadOperator(QDataStream &in) override + { + for (auto &prop : properties) + in >> prop; + } + QVector<QVariant> properties; +}; + +struct GenericPODType : BaseGenericType +{ + // BaseGenericType interface + void *constructor(int typeId, void *where, const void *copy) override + { + GenericPODType *ret = where ? new(where) GenericPODType : new GenericPODType; + ret->m_typeId = typeId; + if (copy) { + Q_ASSERT(ret->m_typeId == reinterpret_cast<const GenericPODType*>(copy)->m_typeId); + *ret = *reinterpret_cast<const GenericPODType*>(copy); + } else { + ret->podData = podData; + } + return ret; + } + + void staticMetacallFunction(QMetaObject::Call _c, int _id, void **_a) override + { + Q_UNUSED(_c); + Q_UNUSED(_id); + Q_UNUSED(_a); + Q_ASSERT(false); + } + + void saveOperator(QDataStream &out) const override + { + out << podData; + } + void loadOperator(QDataStream &in) override + { + in >> podData; + } + QByteArray podData; +}; + +using RegisteredType = QPair<std::shared_ptr<BaseGenericType>, std::shared_ptr<QMetaObject>>; +static QHash<int, RegisteredType> s_managedTypes; + +static void GadgetsStaticMetacallFunction(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + reinterpret_cast<BaseGenericType*>(_o)->staticMetacallFunction(_c, _id, _a); +} + +static void GadgetTypedDestructor(int typeId, void *ptr) +{ + QCOMPARE(typeId, reinterpret_cast<BaseGenericType*>(ptr)->m_typeId); + reinterpret_cast<BaseGenericType*>(ptr)->~BaseGenericType(); +} + +static void *GadgetTypedConstructor(int type, void *where, const void *copy) +{ + auto it = s_managedTypes.find(type); + if (it == s_managedTypes.end()) + return nullptr; // crash the test + return it->first->constructor(type, where, copy); +} + +static void GadgetSaveOperator(QDataStream & out, const void *data) +{ + reinterpret_cast<const BaseGenericType *>(data)->saveOperator(out); +} + +static void GadgetLoadOperator(QDataStream &in, void *data) +{ + reinterpret_cast<BaseGenericType *>(data)->loadOperator(in); +} + struct Foo { int i; }; @@ -149,6 +282,35 @@ class GadgetDerivedAndTyped : public CustomGadget {}; Q_DECLARE_METATYPE(GadgetDerivedAndTyped<int>) Q_DECLARE_METATYPE(GadgetDerivedAndTyped<int>*) +void tst_QMetaType::registerGadget(const char *name, const QVector<GadgetPropertyType> &gadgetProperties) +{ + QMetaObjectBuilder gadgetBuilder; + gadgetBuilder.setClassName(name); + QMetaObjectBuilder::MetaObjectFlags metaObjectflags = QMetaObjectBuilder::DynamicMetaObject; + metaObjectflags |= QMetaObjectBuilder::MetaObjectFlags(PropertyAccessInStaticMetaCall); + gadgetBuilder.setFlags(metaObjectflags); + auto dynamicGadgetProperties = std::make_shared<GenericGadgetType>(); + for (const auto &prop : gadgetProperties) { + int propertyType = QMetaType::type(prop.type); + dynamicGadgetProperties->properties.push_back(QVariant(QVariant::Type(propertyType))); + auto dynamicPropery = gadgetBuilder.addProperty(prop.name, prop.type); + dynamicPropery.setWritable(true); + dynamicPropery.setReadable(true); + } + auto meta = gadgetBuilder.toMetaObject(); + meta->d.static_metacall = &GadgetsStaticMetacallFunction; + meta->d.superdata = nullptr; + const auto flags = QMetaType::WasDeclaredAsMetaType | QMetaType::IsGadget | QMetaType::NeedsConstruction | QMetaType::NeedsDestruction; + int gadgetTypeId = QMetaType::registerType(name, + &GadgetTypedDestructor, + &GadgetTypedConstructor, + sizeof(GenericGadgetType), + flags, meta); + QVERIFY(gadgetTypeId > 0); + QMetaType::registerStreamOperators(gadgetTypeId, &GadgetSaveOperator, &GadgetLoadOperator); + s_managedTypes[gadgetTypeId] = qMakePair(dynamicGadgetProperties, std::shared_ptr<QMetaObject>{meta, [](QMetaObject *ptr){ ::free(ptr); }}); +} + void tst_QMetaType::defined() { QCOMPARE(int(QMetaTypeId2<QString>::Defined), 1); @@ -906,6 +1068,90 @@ FOR_EACH_CORE_METATYPE(RETURN_CONSTRUCT_FUNCTION) TypeTestFunctionGetter::get(type)(); } +void tst_QMetaType::typedConstruct() +{ + auto testMetaObjectWriteOnGadget = [](QVariant &gadget, const QVector<GadgetPropertyType> &properties) + { + auto metaObject = QMetaType::metaObjectForType(gadget.userType()); + QVERIFY(metaObject != nullptr); + QCOMPARE(metaObject->methodCount(), 0); + QCOMPARE(metaObject->propertyCount(), properties.size()); + for (int i = 0; i < metaObject->propertyCount(); ++i) { + auto prop = metaObject->property(i); + QCOMPARE(properties[i].name, prop.name()); + QCOMPARE(properties[i].type, prop.typeName()); + prop.writeOnGadget(gadget.data(), properties[i].testData); + } + }; + + auto testMetaObjectReadOnGadget = [](QVariant gadget, const QVector<GadgetPropertyType> &properties) + { + auto metaObject = QMetaType::metaObjectForType(gadget.userType()); + QVERIFY(metaObject != nullptr); + QCOMPARE(metaObject->methodCount(), 0); + QCOMPARE(metaObject->propertyCount(), properties.size()); + for (int i = 0; i < metaObject->propertyCount(); ++i) { + auto prop = metaObject->property(i); + QCOMPARE(properties[i].name, prop.name()); + QCOMPARE(properties[i].type, prop.typeName()); + if (!QMetaType::typeFlags(prop.userType()).testFlag(QMetaType::IsGadget)) + QCOMPARE(properties[i].testData, prop.readOnGadget(gadget.constData())); + } + }; + + QVector<GadgetPropertyType> dynamicGadget1 = { + {"int", "int_prop", 34526}, + {"float", "float_prop", 1.23f}, + {"QString", "string_prop", QString{"Test QString"}} + }; + registerGadget("DynamicGadget1", dynamicGadget1); + + QVariant testGadget1(QVariant::Type(QMetaType::type("DynamicGadget1"))); + testMetaObjectWriteOnGadget(testGadget1, dynamicGadget1); + testMetaObjectReadOnGadget(testGadget1, dynamicGadget1); + + + QVector<GadgetPropertyType> dynamicGadget2 = { + {"int", "int_prop", 512}, + {"double", "double_prop", 4.56}, + {"QString", "string_prop", QString{"Another String"}}, + {"DynamicGadget1", "dynamicGadget1_prop", testGadget1} + }; + registerGadget("DynamicGadget2", dynamicGadget2); + QVariant testGadget2(QVariant::Type(QMetaType::type("DynamicGadget2"))); + testMetaObjectWriteOnGadget(testGadget2, dynamicGadget2); + testMetaObjectReadOnGadget(testGadget2, dynamicGadget2); + auto g2mo = QMetaType::metaObjectForType(testGadget2.userType()); + auto dynamicGadget1_prop = g2mo->property(g2mo->indexOfProperty("dynamicGadget1_prop")); + testMetaObjectReadOnGadget(dynamicGadget1_prop.readOnGadget(testGadget2.constData()), dynamicGadget1); + + + // Register POD + const QByteArray myPodTesData = "My POD test data"; + const char podTypeName[] = "DynamicPOD"; + auto dynamicGadgetProperties = std::make_shared<GenericPODType>(); + dynamicGadgetProperties->podData = myPodTesData; + const auto flags = QMetaType::NeedsConstruction | QMetaType::NeedsDestruction; + int podTypeId = QMetaType::registerType(podTypeName, + &GadgetTypedDestructor, + &GadgetTypedConstructor, + sizeof(GenericGadgetType), + flags, nullptr); + QVERIFY(podTypeId > 0); + QMetaType::registerStreamOperators(podTypeId, &GadgetSaveOperator, &GadgetLoadOperator); + s_managedTypes[podTypeId] = qMakePair(dynamicGadgetProperties, std::shared_ptr<QMetaObject>{}); + + // Test POD + QCOMPARE(podTypeId, QMetaType::type(podTypeName)); + QVariant podVariant{QVariant::Type(podTypeId)}; + QCOMPARE(myPodTesData, static_cast<const GenericPODType *>(reinterpret_cast<const BaseGenericType *>(podVariant.constData()))->podData); + + QVariant podVariant1{podVariant}; + podVariant1.detach(); // Test stream operators + static_cast<GenericPODType *>(reinterpret_cast<BaseGenericType *>(podVariant.data()))->podData.clear(); + QCOMPARE(myPodTesData, static_cast<const GenericPODType *>(reinterpret_cast<const BaseGenericType *>(podVariant1.constData()))->podData); +} + template<int ID> static void testConstructCopyHelper() { |