diff options
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 122 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype.h | 171 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 47 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.h | 8 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp | 282 |
5 files changed, 622 insertions, 8 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index b9f7462b12..44246e05ca 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -420,6 +420,49 @@ public: int alias; }; +class QMetaTypeConversionRegistry +{ +public: + typedef QPair<int, int> Key; + + ~QMetaTypeConversionRegistry() + { + const QWriteLocker locker(&lock); + Q_FOREACH (QtPrivate::AbstractConverterFunction *f, map) + f->destroy(f); + map.clear(); + } + + bool contains(int from, int to) const + { + const Key k(from, to); + const QReadLocker locker(&lock); + return map.contains(k); + } + + bool insertIfNotContains(int from, int to, QtPrivate::AbstractConverterFunction *f) + { + const Key k(from, to); + const QWriteLocker locker(&lock); + QtPrivate::AbstractConverterFunction* &fun = map[k]; + if (fun != 0) + return false; + fun = f; + return true; + } + + QtPrivate::AbstractConverterFunction *function(int from, int to) const + { + const Key k(from, to); + const QReadLocker locker(&lock); + return map.value(k, 0); + } + +private: + mutable QReadWriteLock lock; + QHash<Key, QtPrivate::AbstractConverterFunction *> map; +}; + namespace { union CheckThatItIsPod @@ -431,6 +474,85 @@ union CheckThatItIsPod Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE); Q_GLOBAL_STATIC(QVector<QCustomTypeInfo>, customTypes) Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock) +Q_GLOBAL_STATIC(QMetaTypeConversionRegistry, customTypesConversionRegistry) + +/*! + \fn bool QMetaType::registerConverter() + \since 5.1 + Registers the possibility of an implicit conversion from type From to type To in the meta + type system. Returns true if the registration succeeded, otherwise false. +*/ + +/*! + \fn bool QMetaType::registerConverter(MemberFunction function) + \since 5.1 + \overload + Registers a method \a function like To From::function() const as converter from type From + to type To in the meta type system. Returns true if the registration succeeded, otherwise false. +*/ + +/*! + \fn bool QMetaType::registerConverter(MemberFunctionOk function) + \since 5.1 + \overload + Registers a method \a function like To From::function(bool *ok) const as converter from type From + to type To in the meta type system. Returns true if the registration succeeded, otherwise false. +*/ + +/*! + \fn bool QMetaType::registerConverter(UnaryFunction function) + \since 5.1 + \overload + Registers a unary function object \a function as converter from type From + to type To in the meta type system. Returns true if the registration succeeded, otherwise false. +*/ + +/*! + Registers function \a f as converter function from type id \a from to \a to. + If there's already a conversion registered, this does nothing but deleting \a f. + Returns true if the registration succeeded, otherwise false. + \since 5.1 + \internal +*/ +bool QMetaType::registerConverterFunction(QtPrivate::AbstractConverterFunction *f, int from, int to) +{ + if (!customTypesConversionRegistry()->insertIfNotContains(from, to, f)) { + qWarning("Type conversion already registered from type %s to type %s", + QMetaType::typeName(from), QMetaType::typeName(to)); + if (f) + f->destroy(f); + return false; + } + return true; +} + +/*! + Converts the object at \a from from \a fromTypeId to the preallocated space at \a to + typed \a toTypeId. Returns true, if the conversion succeeded, otherwise false. + \since 5.1 +*/ +bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId) +{ + const QtPrivate::AbstractConverterFunction * const f = customTypesConversionRegistry()->function(fromTypeId, toTypeId); + return f && f->convert(f, from, to); +} + +/*! + \fn bool QMetaType::hasRegisteredConverterFunction() + Returns true, if the meta type system has a registered conversion from type From to type To. + \since 5.1 + \overload + */ + +/*! + Returns true, if the meta type system has a registered conversion from meta type id \a fromTypeId + to \a toTypeId + \since 5.1 +*/ +bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId) +{ + return customTypesConversionRegistry()->contains(fromTypeId, toTypeId); +} #ifndef QT_NO_DATASTREAM /*! diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index c5eae7a83e..e173bbe57d 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -47,6 +47,7 @@ #include <QtCore/qbytearray.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qisenum.h> +#include <QtCore/qtypetraits.h> #ifndef QT_NO_QOBJECT #include <QtCore/qobjectdefs.h> #endif @@ -62,6 +63,11 @@ QT_BEGIN_NAMESPACE +template <typename T> +struct QMetaTypeId2; + +template <typename T> +inline Q_DECL_CONSTEXPR int qMetaTypeId(); // F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType) #define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\ @@ -204,6 +210,105 @@ class QDataStream; class QMetaTypeInterface; struct QMetaObject; +namespace QtPrivate +{ +/*! + This template is used for implicit conversion from type From to type To. + \internal +*/ +template<typename From, typename To> +To convertImplicit(const From& from) +{ + return from; +} + +struct AbstractConverterFunction +{ + typedef bool (*Converter)(const AbstractConverterFunction *, const void *, void*); + typedef void (*Destroy)(AbstractConverterFunction *); + explicit AbstractConverterFunction(Converter c = 0, Destroy d = 0) + : convert(c), destroy(d) {} + Q_DISABLE_COPY(AbstractConverterFunction) + Converter convert; + Destroy destroy; +}; + +template<typename From, typename To> +struct ConverterMemberFunction : public AbstractConverterFunction +{ + explicit ConverterMemberFunction(To(From::*function)() const) + : AbstractConverterFunction(convert, destroy), + m_function(function) {} + static bool convert(const AbstractConverterFunction *_this, const void *in, void *out) + { + const From *f = static_cast<const From *>(in); + To *t = static_cast<To *>(out); + const ConverterMemberFunction *_typedThis = + static_cast<const ConverterMemberFunction *>(_this); + *t = (f->*_typedThis->m_function)(); + return true; + } + + static void destroy(AbstractConverterFunction *_this) + { + delete static_cast<ConverterMemberFunction *>(_this); + } + + To(From::* const m_function)() const; +}; + +template<typename From, typename To> +struct ConverterMemberFunctionOk : public AbstractConverterFunction +{ + explicit ConverterMemberFunctionOk(To(From::*function)(bool *) const) + : AbstractConverterFunction(convert, destroy), + m_function(function) {} + static bool convert(const AbstractConverterFunction *_this, const void *in, void *out) + { + const From *f = static_cast<const From *>(in); + To *t = static_cast<To *>(out); + bool ok = false; + const ConverterMemberFunctionOk *_typedThis = + static_cast<const ConverterMemberFunctionOk *>(_this); + *t = (f->*_typedThis->m_function)(&ok); + if (!ok) + *t = To(); + return ok; + } + + static void destroy(AbstractConverterFunction *_this) + { + delete static_cast<ConverterMemberFunctionOk *>(_this); + } + + To(From::* const m_function)(bool*) const; +}; + +template<typename From, typename To, typename UnaryFunction> +struct ConverterFunctor : public AbstractConverterFunction +{ + explicit ConverterFunctor(UnaryFunction function) + : AbstractConverterFunction(convert, destroy), + m_function(function) {} + static bool convert(const AbstractConverterFunction *_this, const void *in, void *out) + { + const From *f = static_cast<const From *>(in); + To *t = static_cast<To *>(out); + const ConverterFunctor *_typedThis = + static_cast<const ConverterFunctor *>(_this); + *t = _typedThis->m_function(*f); + return true; + } + + static void destroy(AbstractConverterFunction *_this) + { + delete static_cast<ConverterFunctor *>(_this); + } + + UnaryFunction m_function; +}; +} + class Q_CORE_EXPORT QMetaType { enum ExtensionFlag { NoExtensionFlags, CreateEx = 0x1, DestroyEx = 0x2, @@ -337,6 +442,70 @@ public: inline void destroy(void *data) const; inline void *construct(void *where, const void *copy = 0) const; inline void destruct(void *data) const; + +public: + // implicit conversion supported like double -> float + template<typename From, typename To> + static bool registerConverter() + { + return registerConverter<From, To>(QtPrivate::convertImplicit<From, To>); + } + +#ifdef Q_QDOC + static bool registerConverter(MemberFunction function); + static bool registerConverter(MemberFunctionOk function); + static bool registerConverter(UnaryFunction function); +#else + // member function as in "QString QFont::toString() const" + template<typename From, typename To> + static bool registerConverter(To(From::*function)() const) + { + Q_STATIC_ASSERT_X((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), + "QMetaType::registerConverter: At least one of the types must be a custom type."); + + const int fromTypeId = qMetaTypeId<From>(); + const int toTypeId = qMetaTypeId<To>(); + return registerConverterFunction(new QtPrivate::ConverterMemberFunction<From, To>(function), + fromTypeId, toTypeId); + } + + // member function as in "double QString::toDouble(bool *ok = 0) const" + template<typename From, typename To> + static bool registerConverter(To(From::*function)(bool*) const) + { + Q_STATIC_ASSERT_X((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), + "QMetaType::registerConverter: At least one of the types must be a custom type."); + + const int fromTypeId = qMetaTypeId<From>(); + const int toTypeId = qMetaTypeId<To>(); + return registerConverterFunction(new QtPrivate::ConverterMemberFunctionOk<From, To>(function), + fromTypeId, toTypeId); + } + + // functor or function pointer + template<typename From, typename To, typename UnaryFunction> + static bool registerConverter(UnaryFunction function) + { + Q_STATIC_ASSERT_X((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn), + "QMetaType::registerConverter: At least one of the types must be a custom type."); + + const int fromTypeId = qMetaTypeId<From>(); + const int toTypeId = qMetaTypeId<To>(); + return registerConverterFunction(new QtPrivate::ConverterFunctor<From, To, UnaryFunction>(function), + fromTypeId, toTypeId); + } +#endif + + static bool convert(const void *from, int fromTypeId, void *to, int toTypeId); + + template<typename From, typename To> + static bool hasRegisteredConverterFunction() + { + return hasRegisteredConverterFunction(qMetaTypeId<From>(), qMetaTypeId<To>()); + } + + static bool hasRegisteredConverterFunction(int fromTypeId, int toTypeId); + private: static QMetaType typeInfo(const int type); inline QMetaType(const ExtensionFlag extensionFlags, const QMetaTypeInterface *info, @@ -365,6 +534,8 @@ private: void *constructExtended(void *where, const void *copy = 0) const; void destructExtended(void *data) const; + static bool registerConverterFunction(QtPrivate::AbstractConverterFunction *f, int from, int to); + Creator m_creator; Deleter m_deleter; SaveOperator m_saveOp; diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index bae4a837a0..be83979205 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -262,6 +262,16 @@ inline bool qt_convertToBool(const QVariant::Private *const d) /*! \internal + Returns the internal data pointer from \a d. + */ + +static const void *constData(const QVariant::Private &d) +{ + return d.is_shared ? d.data.shared->ptr : reinterpret_cast<const void *>(&d.data.c); +} + +/*! + \internal Converts \a d to type \a t, which is placed in \a result. */ @@ -270,6 +280,14 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok) Q_ASSERT(d->type != uint(t)); Q_ASSERT(result); + if (d->type >= QMetaType::User || t >= QMetaType::User) { + const bool isOk = QMetaType::convert(constData(*d), d->type, result, t); + if (ok) + *ok = isOk; + if (isOk) + return true; + } + bool dummy; if (!ok) ok = &dummy; @@ -842,8 +860,15 @@ static bool customCompare(const QVariant::Private *a, const QVariant::Private *b return !memcmp(a_ptr, b_ptr, QMetaType::sizeOf(a->type)); } -static bool customConvert(const QVariant::Private *, int, void *, bool *ok) +static bool customConvert(const QVariant::Private *d, int t, void *result, bool *ok) { + if (d->type >= QMetaType::User || t >= QMetaType::User) { + const bool isOk = QMetaType::convert(constData(*d), d->type, result, t); + if (ok) + *ok = isOk; + return isOk; + } + if (ok) *ok = false; return false; @@ -1933,6 +1958,12 @@ inline T qVariantToHelper(const QVariant::Private &d, const HandlersManager &han return *v_cast<T>(&d); T ret; + if (d.type >= QMetaType::User || targetType >= QMetaType::User) { + const void * const from = constData(d); + if (QMetaType::convert(from, d.type, &ret, targetType)) + return ret; + } + handlerManager[d.type]->convert(&d, targetType, &ret, 0); return ret; } @@ -2349,13 +2380,19 @@ template <typename T> inline T qNumVariantToHelper(const QVariant::Private &d, const HandlersManager &handlerManager, bool *ok, const T& val) { - uint t = qMetaTypeId<T>(); + const uint t = qMetaTypeId<T>(); if (ok) *ok = true; + if (d.type == t) return val; T ret = 0; + if ((d.type >= QMetaType::User || t >= QMetaType::User) + && QMetaType::convert(&val, d.type, &ret, t)) { + return ret; + } + if (!handlerManager[d.type]->convert(&d, t, &ret, ok) && ok) *ok = false; return ret; @@ -2714,6 +2751,11 @@ static bool canConvertMetaObject(int fromId, int toId, QObject *fromObject) */ bool QVariant::canConvert(int targetTypeId) const { + if ((d.type >= QMetaType::User || targetTypeId >= QMetaType::User) + && QMetaType::hasRegisteredConverterFunction(d.type, targetTypeId)) { + return true; + } + // TODO Reimplement this function, currently it works but it is a historical mess. uint currentType = ((d.type == QMetaType::Float) ? QVariant::Double : d.type); if (currentType == QMetaType::SChar || currentType == QMetaType::Char) @@ -2838,7 +2880,6 @@ bool QVariant::convert(int targetTypeId) */ bool QVariant::convert(const int type, void *ptr) const { - Q_ASSERT(type < int(QMetaType::User)); return handlerManager[type]->convert(&d, type, ptr, 0); } diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 6f212f5000..0f4102b70a 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -571,11 +571,9 @@ namespace QtPrivate { const int vid = qMetaTypeId<T>(); if (vid == v.userType()) return *reinterpret_cast<const T *>(v.constData()); - if (vid < int(QMetaType::User)) { - T t; - if (v.convert(vid, &t)) - return t; - } + T t; + if (v.convert(vid, &t)) + return t; return T(); } #ifndef QT_NO_QOBJECT diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 3e569814c9..645b14fd00 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -111,6 +111,8 @@ private slots: void metaObject(); void constexprMetaTypeIds(); void constRefs(); + void convertCustomType_data(); + void convertCustomType(); }; struct Foo { int i; }; @@ -1818,6 +1820,286 @@ void tst_QMetaType::constRefs() #endif } +struct CustomConvertibleType +{ + explicit CustomConvertibleType(const QVariant &foo = QVariant()) : m_foo(foo) {} + virtual ~CustomConvertibleType() {} + QString toString() const { return QLatin1String("CustomConvertibleType::toString()"); } + operator QPoint() const { return QPoint(12, 34); } + template<typename To> + To convert() const { return s_value.value<To>();} + template<typename To> + To convertOk(bool *ok) const { *ok = s_ok; return s_value.value<To>();} + + QVariant m_foo; + static QVariant s_value; + static bool s_ok; +}; + +bool operator==(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) +{ return lhs.m_foo == rhs.m_foo; } +bool operator!=(const CustomConvertibleType &lhs, const CustomConvertibleType &rhs) +{ return !operator==(lhs, rhs); } + +QVariant CustomConvertibleType::s_value; +bool CustomConvertibleType::s_ok = true; + +struct CustomConvertibleType2 +{ + // implicit + CustomConvertibleType2(const CustomConvertibleType &t = CustomConvertibleType()) + : m_foo(t.m_foo) {} + virtual ~CustomConvertibleType2() {} + + QVariant m_foo; +}; + +bool operator==(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs) +{ return lhs.m_foo == rhs.m_foo; } +bool operator!=(const CustomConvertibleType2 &lhs, const CustomConvertibleType2 &rhs) +{ return !operator==(lhs, rhs); } + +Q_DECLARE_METATYPE(CustomConvertibleType); +Q_DECLARE_METATYPE(CustomConvertibleType2); + +template<typename T, typename U> +U convert(const T &t) +{ + return t; +} + +template<typename From> +struct ConvertFunctor +{ + CustomConvertibleType operator()(const From& f) const + { + return CustomConvertibleType(QVariant::fromValue(f)); + } +}; + +template<typename From, typename To> +bool hasRegisteredConverterFunction() +{ + return QMetaType::hasRegisteredConverterFunction<From, To>(); +} + +template<typename From, typename To> +void testCustomTypeNotYetConvertible() +{ + QVERIFY((!hasRegisteredConverterFunction<From, To>())); + QVERIFY((!QVariant::fromValue<From>(From()).canConvert(qMetaTypeId<To>()))); +} + +template<typename From, typename To> +void testCustomTypeConvertible() +{ + QVERIFY((hasRegisteredConverterFunction<From, To>())); + QVERIFY((QVariant::fromValue<From>(From()).canConvert(qMetaTypeId<To>()))); +} + +void customTypeNotYetConvertible() +{ + testCustomTypeNotYetConvertible<CustomConvertibleType, QString>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, bool>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, int>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, double>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, float>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QRect>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QRectF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QPoint>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QPointF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QSize>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QSizeF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QLine>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QLineF>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, QChar>(); + testCustomTypeNotYetConvertible<QString, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<bool, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<int, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<double, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<float, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QRect, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QRectF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QPoint, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QPointF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QSize, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QSizeF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QLine, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QLineF, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<QChar, CustomConvertibleType>(); + testCustomTypeNotYetConvertible<CustomConvertibleType, CustomConvertibleType2>(); +} + +void registerCustomTypeConversions() +{ + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QString>(&CustomConvertibleType::convertOk<QString>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, bool>(&CustomConvertibleType::convert<bool>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, int>(&CustomConvertibleType::convertOk<int>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, double>(&CustomConvertibleType::convert<double>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, float>(&CustomConvertibleType::convertOk<float>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QRect>(&CustomConvertibleType::convert<QRect>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QRectF>(&CustomConvertibleType::convertOk<QRectF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QPoint>(convert<CustomConvertibleType,QPoint>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QPointF>(&CustomConvertibleType::convertOk<QPointF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QSize>(&CustomConvertibleType::convert<QSize>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QSizeF>(&CustomConvertibleType::convertOk<QSizeF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLine>(&CustomConvertibleType::convert<QLine>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QLineF>(&CustomConvertibleType::convertOk<QLineF>))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, QChar>(&CustomConvertibleType::convert<QChar>))); + QVERIFY((QMetaType::registerConverter<QString, CustomConvertibleType>(ConvertFunctor<QString>()))); + QVERIFY((QMetaType::registerConverter<bool, CustomConvertibleType>(ConvertFunctor<bool>()))); + QVERIFY((QMetaType::registerConverter<int, CustomConvertibleType>(ConvertFunctor<int>()))); + QVERIFY((QMetaType::registerConverter<double, CustomConvertibleType>(ConvertFunctor<double>()))); + QVERIFY((QMetaType::registerConverter<float, CustomConvertibleType>(ConvertFunctor<float>()))); + QVERIFY((QMetaType::registerConverter<QRect, CustomConvertibleType>(ConvertFunctor<QRect>()))); + QVERIFY((QMetaType::registerConverter<QRectF, CustomConvertibleType>(ConvertFunctor<QRectF>()))); + QVERIFY((QMetaType::registerConverter<QPoint, CustomConvertibleType>(ConvertFunctor<QPoint>()))); + QVERIFY((QMetaType::registerConverter<QPointF, CustomConvertibleType>(ConvertFunctor<QPointF>()))); + QVERIFY((QMetaType::registerConverter<QSize, CustomConvertibleType>(ConvertFunctor<QSize>()))); + QVERIFY((QMetaType::registerConverter<QSizeF, CustomConvertibleType>(ConvertFunctor<QSizeF>()))); + QVERIFY((QMetaType::registerConverter<QLine, CustomConvertibleType>(ConvertFunctor<QLine>()))); + QVERIFY((QMetaType::registerConverter<QLineF, CustomConvertibleType>(ConvertFunctor<QLineF>()))); + QVERIFY((QMetaType::registerConverter<QChar, CustomConvertibleType>(ConvertFunctor<QChar>()))); + QVERIFY((QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); + QTest::ignoreMessage(QtWarningMsg, "Type conversion already registered from type CustomConvertibleType to type CustomConvertibleType2"); + QVERIFY((!QMetaType::registerConverter<CustomConvertibleType, CustomConvertibleType2>())); +} + +void tst_QMetaType::convertCustomType_data() +{ + customTypeNotYetConvertible(); + registerCustomTypeConversions(); + + QTest::addColumn<bool>("ok"); + QTest::addColumn<QString>("testQString"); + QTest::addColumn<bool>("testBool"); + QTest::addColumn<int>("testInt"); + QTest::addColumn<double>("testDouble"); + QTest::addColumn<float>("testFloat"); + QTest::addColumn<QRect>("testQRect"); + QTest::addColumn<QRectF>("testQRectF"); + QTest::addColumn<QPoint>("testQPoint"); + QTest::addColumn<QPointF>("testQPointF"); + QTest::addColumn<QSize>("testQSize"); + QTest::addColumn<QSizeF>("testQSizeF"); + QTest::addColumn<QLine>("testQLine"); + QTest::addColumn<QLineF>("testQLineF"); + QTest::addColumn<QChar>("testQChar"); + QTest::addColumn<CustomConvertibleType>("testCustom"); + + QTest::newRow("default") << true + << QString::fromLatin1("string") << true << 15 + << double(3.14) << float(3.6) << QRect(1, 2, 3, 4) + << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) + << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) + << QLine(3, 9, 29, 4) << QLineF(38.9, 28.9, 102.3, 0.0) + << QChar('Q') << CustomConvertibleType(QString::fromLatin1("test")); + QTest::newRow("not ok") << false + << QString::fromLatin1("string") << true << 15 + << double(3.14) << float(3.6) << QRect(1, 2, 3, 4) + << QRectF(1.4, 1.9, 10.9, 40.2) << QPoint(12, 34) + << QPointF(9.2, 2.7) << QSize(4, 9) << QSizeF(3.3, 9.8) + << QLine(3, 9, 29, 4) << QLineF() + << QChar('Q') << CustomConvertibleType(42); +} + +void tst_QMetaType::convertCustomType() +{ + QFETCH(bool, ok); + CustomConvertibleType::s_ok = ok; + + CustomConvertibleType t; + QVariant v = QVariant::fromValue(t); + QFETCH(QString, testQString); + CustomConvertibleType::s_value = testQString; + QCOMPARE(v.toString(), ok ? testQString : QString()); + QCOMPARE(v.value<QString>(), ok ? testQString : QString()); + QVERIFY(CustomConvertibleType::s_value.canConvert<CustomConvertibleType>()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toString()), testQString); + + QFETCH(bool, testBool); + CustomConvertibleType::s_value = testBool; + QCOMPARE(v.toBool(), testBool); + QCOMPARE(v.value<bool>(), testBool); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toBool()), testBool); + + QFETCH(int, testInt); + CustomConvertibleType::s_value = testInt; + QCOMPARE(v.toInt(), ok ? testInt : 0); + QCOMPARE(v.value<int>(), ok ? testInt : 0); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toInt()), testInt); + + QFETCH(double, testDouble); + CustomConvertibleType::s_value = testDouble; + QCOMPARE(v.toDouble(), testDouble); + QCOMPARE(v.value<double>(), testDouble); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toDouble()), testDouble); + + QFETCH(float, testFloat); + CustomConvertibleType::s_value = testFloat; + QCOMPARE(v.toFloat(), ok ? testFloat : 0.0); + QCOMPARE(v.value<float>(), ok ? testFloat : 0.0); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toFloat()), testFloat); + + QFETCH(QRect, testQRect); + CustomConvertibleType::s_value = testQRect; + QCOMPARE(v.toRect(), testQRect); + QCOMPARE(v.value<QRect>(), testQRect); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRect()), testQRect); + + QFETCH(QRectF, testQRectF); + CustomConvertibleType::s_value = testQRectF; + QCOMPARE(v.toRectF(), ok ? testQRectF : QRectF()); + QCOMPARE(v.value<QRectF>(), ok ? testQRectF : QRectF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toRectF()), testQRectF); + + QFETCH(QPoint, testQPoint); + CustomConvertibleType::s_value = testQPoint; + QCOMPARE(v.toPoint(), testQPoint); + QCOMPARE(v.value<QPoint>(), testQPoint); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPoint()), testQPoint); + + QFETCH(QPointF, testQPointF); + CustomConvertibleType::s_value = testQPointF; + QCOMPARE(v.toPointF(), ok ? testQPointF : QPointF()); + QCOMPARE(v.value<QPointF>(), ok ? testQPointF : QPointF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toPointF()), testQPointF); + + QFETCH(QSize, testQSize); + CustomConvertibleType::s_value = testQSize; + QCOMPARE(v.toSize(), testQSize); + QCOMPARE(v.value<QSize>(), testQSize); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSize()), testQSize); + + QFETCH(QSizeF, testQSizeF); + CustomConvertibleType::s_value = testQSizeF; + QCOMPARE(v.toSizeF(), ok ? testQSizeF : QSizeF()); + QCOMPARE(v.value<QSizeF>(), ok ? testQSizeF : QSizeF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toSizeF()), testQSizeF); + + QFETCH(QLine, testQLine); + CustomConvertibleType::s_value = testQLine; + QCOMPARE(v.toLine(), testQLine); + QCOMPARE(v.value<QLine>(), testQLine); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLine()), testQLine); + + QFETCH(QLineF, testQLineF); + CustomConvertibleType::s_value = testQLineF; + QCOMPARE(v.toLineF(), ok ? testQLineF : QLineF()); + QCOMPARE(v.value<QLineF>(), ok ? testQLineF : QLineF()); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toLineF()), testQLineF); + + QFETCH(QChar, testQChar); + CustomConvertibleType::s_value = testQChar; + QCOMPARE(v.toChar(), testQChar); + QCOMPARE((CustomConvertibleType::s_value.value<CustomConvertibleType>().m_foo.toChar()), testQChar); + + QFETCH(CustomConvertibleType, testCustom); + v = QVariant::fromValue(testCustom); + QVERIFY(v.canConvert(::qMetaTypeId<CustomConvertibleType2>())); + QCOMPARE(v.value<CustomConvertibleType2>().m_foo, testCustom.m_foo); +} + // Compile-time test, it should be possible to register function pointer types class Undefined; |