From c2165f891c1a4380fdbf3bbf7af4696dc863cb99 Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Wed, 22 Aug 2018 15:03:12 +0300 Subject: Allow registering constructor for a set of dynamic types This feature is needed to register complex types (e.g. Q_GADGETS) at runtime, using a single constructor method and added type parameter. Without having the type id available to the Constructor it is impossible to specialize behavior, thus requiring separate constructors for each type. Generating these separate constructors at compile time is easy, but not at runtime. [ChangeLog][QMetaType] QMetaType can now register constructor for a set of dynamic types. Change-Id: I6071271d0e157864594dd07b4bc3a0abbeb15a44 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qmetatype.cpp | 191 +++++++++++----- src/corelib/kernel/qmetatype.h | 33 ++- src/corelib/kernel/qmetatype_p.h | 12 +- tests/auto/corelib/kernel/qmetatype/qmetatype.pro | 2 +- .../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 *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. " @@ -1172,6 +1169,66 @@ int QMetaType::registerNormalizedType(const NS(QByteArray) &normalizedTypeName, return idx; } +/*! + \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 * 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 * 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(typeInfo, type, 0); - return typeInfo.info.constructor ? QMetaType(static_cast(QMetaType::CreateEx | QMetaType::DestroyEx) - , static_cast(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(QMetaType::CreateEx | QMetaType::DestroyEx | + (typeInfo.info.typedConstructor ? QMetaType::ConstructEx | QMetaType::DestructEx : 0)) + , static_cast(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::IsAvailable>::Destruct), \ /*size*/(QTypeInfo::sizeOf), \ /*flags*/QtPrivate::QMetaTypeTypeFlags::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 #include +#include +#include #include "tst_qmetatype.h" #include "tst_qvariant_common.h" @@ -38,6 +40,7 @@ #endif #include +#include // 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 prop READ prop WRITE setProp) public: + struct GadgetPropertyType { + QByteArray type; + QByteArray name; + QVariant testData; + }; + tst_QMetaType() { propList << 42 << "Hello"; } QList prop() const { return propList; } void setProp(const QList &list) { propList = list; } private: + void registerGadget(const char * name, const QVector &gadgetProperties); QList 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(copy)->m_typeId); + *ret = *reinterpret_cast(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 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(copy)->m_typeId); + *ret = *reinterpret_cast(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>; +static QHash s_managedTypes; + +static void GadgetsStaticMetacallFunction(QObject *_o, QMetaObject::Call _c, int _id, void **_a) +{ + reinterpret_cast(_o)->staticMetacallFunction(_c, _id, _a); +} + +static void GadgetTypedDestructor(int typeId, void *ptr) +{ + QCOMPARE(typeId, reinterpret_cast(ptr)->m_typeId); + reinterpret_cast(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(data)->saveOperator(out); +} + +static void GadgetLoadOperator(QDataStream &in, void *data) +{ + reinterpret_cast(data)->loadOperator(in); +} + struct Foo { int i; }; @@ -149,6 +282,35 @@ class GadgetDerivedAndTyped : public CustomGadget {}; Q_DECLARE_METATYPE(GadgetDerivedAndTyped) Q_DECLARE_METATYPE(GadgetDerivedAndTyped*) +void tst_QMetaType::registerGadget(const char *name, const QVector &gadgetProperties) +{ + QMetaObjectBuilder gadgetBuilder; + gadgetBuilder.setClassName(name); + QMetaObjectBuilder::MetaObjectFlags metaObjectflags = QMetaObjectBuilder::DynamicMetaObject; + metaObjectflags |= QMetaObjectBuilder::MetaObjectFlags(PropertyAccessInStaticMetaCall); + gadgetBuilder.setFlags(metaObjectflags); + auto dynamicGadgetProperties = std::make_shared(); + 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{meta, [](QMetaObject *ptr){ ::free(ptr); }}); +} + void tst_QMetaType::defined() { QCOMPARE(int(QMetaTypeId2::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 &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 &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 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 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(); + 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{}); + + // Test POD + QCOMPARE(podTypeId, QMetaType::type(podTypeName)); + QVariant podVariant{QVariant::Type(podTypeId)}; + QCOMPARE(myPodTesData, static_cast(reinterpret_cast(podVariant.constData()))->podData); + + QVariant podVariant1{podVariant}; + podVariant1.detach(); // Test stream operators + static_cast(reinterpret_cast(podVariant.data()))->podData.clear(); + QCOMPARE(myPodTesData, static_cast(reinterpret_cast(podVariant1.constData()))->podData); +} + template static void testConstructCopyHelper() { -- cgit v1.2.3