Core: Support for converting user defined QVariant types.
This patchs allows the user to convert defined QMetaType types like MyType to be converted by using e.g. QVariant::toString(), mapping to MyType::toString(). Also all the other QVariant::toXYZ() methods are supported so far. The patch adds static methods QMetaType::registerConverter supporting: - implicit convertion - conversion using member method of source type - conversion using unary functor Change-Id: I4f1db83d9c78bcc9df5c42f82f95cce0480cdcc3 Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@digia.com> Reviewed-by: Christoph Schleifenbaum <christoph.schleifenbaum@kdab.com> Reviewed-by: Stephen Kelly <stephen.kelly@kdab.com>
This commit is contained in:
parent
46d80dedb7
commit
f7b313e6d8
@ -420,6 +420,49 @@ public:
|
|||||||
int alias;
|
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
|
namespace
|
||||||
{
|
{
|
||||||
union CheckThatItIsPod
|
union CheckThatItIsPod
|
||||||
@ -431,6 +474,85 @@ union CheckThatItIsPod
|
|||||||
Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE);
|
Q_DECLARE_TYPEINFO(QCustomTypeInfo, Q_MOVABLE_TYPE);
|
||||||
Q_GLOBAL_STATIC(QVector<QCustomTypeInfo>, customTypes)
|
Q_GLOBAL_STATIC(QVector<QCustomTypeInfo>, customTypes)
|
||||||
Q_GLOBAL_STATIC(QReadWriteLock, customTypesLock)
|
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
|
#ifndef QT_NO_DATASTREAM
|
||||||
/*!
|
/*!
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
#include <QtCore/qbytearray.h>
|
#include <QtCore/qbytearray.h>
|
||||||
#include <QtCore/qvarlengtharray.h>
|
#include <QtCore/qvarlengtharray.h>
|
||||||
#include <QtCore/qisenum.h>
|
#include <QtCore/qisenum.h>
|
||||||
|
#include <QtCore/qtypetraits.h>
|
||||||
#ifndef QT_NO_QOBJECT
|
#ifndef QT_NO_QOBJECT
|
||||||
#include <QtCore/qobjectdefs.h>
|
#include <QtCore/qobjectdefs.h>
|
||||||
#endif
|
#endif
|
||||||
@ -62,6 +63,11 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
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)
|
// F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType)
|
||||||
#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\
|
#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\
|
||||||
@ -204,6 +210,105 @@ class QDataStream;
|
|||||||
class QMetaTypeInterface;
|
class QMetaTypeInterface;
|
||||||
struct QMetaObject;
|
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 {
|
class Q_CORE_EXPORT QMetaType {
|
||||||
enum ExtensionFlag { NoExtensionFlags,
|
enum ExtensionFlag { NoExtensionFlags,
|
||||||
CreateEx = 0x1, DestroyEx = 0x2,
|
CreateEx = 0x1, DestroyEx = 0x2,
|
||||||
@ -337,6 +442,70 @@ public:
|
|||||||
inline void destroy(void *data) const;
|
inline void destroy(void *data) const;
|
||||||
inline void *construct(void *where, const void *copy = 0) const;
|
inline void *construct(void *where, const void *copy = 0) const;
|
||||||
inline void destruct(void *data) 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:
|
private:
|
||||||
static QMetaType typeInfo(const int type);
|
static QMetaType typeInfo(const int type);
|
||||||
inline QMetaType(const ExtensionFlag extensionFlags, const QMetaTypeInterface *info,
|
inline QMetaType(const ExtensionFlag extensionFlags, const QMetaTypeInterface *info,
|
||||||
@ -365,6 +534,8 @@ private:
|
|||||||
void *constructExtended(void *where, const void *copy = 0) const;
|
void *constructExtended(void *where, const void *copy = 0) const;
|
||||||
void destructExtended(void *data) const;
|
void destructExtended(void *data) const;
|
||||||
|
|
||||||
|
static bool registerConverterFunction(QtPrivate::AbstractConverterFunction *f, int from, int to);
|
||||||
|
|
||||||
Creator m_creator;
|
Creator m_creator;
|
||||||
Deleter m_deleter;
|
Deleter m_deleter;
|
||||||
SaveOperator m_saveOp;
|
SaveOperator m_saveOp;
|
||||||
|
@ -260,6 +260,16 @@ inline bool qt_convertToBool(const QVariant::Private *const d)
|
|||||||
return !(str == LiteralWrapper("0") || str == LiteralWrapper("false") || str.isEmpty());
|
return !(str == LiteralWrapper("0") || str == LiteralWrapper("false") || str.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\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
|
\internal
|
||||||
|
|
||||||
@ -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(d->type != uint(t));
|
||||||
Q_ASSERT(result);
|
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;
|
bool dummy;
|
||||||
if (!ok)
|
if (!ok)
|
||||||
ok = &dummy;
|
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));
|
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)
|
if (ok)
|
||||||
*ok = false;
|
*ok = false;
|
||||||
return false;
|
return false;
|
||||||
@ -1933,6 +1958,12 @@ inline T qVariantToHelper(const QVariant::Private &d, const HandlersManager &han
|
|||||||
return *v_cast<T>(&d);
|
return *v_cast<T>(&d);
|
||||||
|
|
||||||
T ret;
|
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);
|
handlerManager[d.type]->convert(&d, targetType, &ret, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -2349,13 +2380,19 @@ template <typename T>
|
|||||||
inline T qNumVariantToHelper(const QVariant::Private &d,
|
inline T qNumVariantToHelper(const QVariant::Private &d,
|
||||||
const HandlersManager &handlerManager, bool *ok, const T& val)
|
const HandlersManager &handlerManager, bool *ok, const T& val)
|
||||||
{
|
{
|
||||||
uint t = qMetaTypeId<T>();
|
const uint t = qMetaTypeId<T>();
|
||||||
if (ok)
|
if (ok)
|
||||||
*ok = true;
|
*ok = true;
|
||||||
|
|
||||||
if (d.type == t)
|
if (d.type == t)
|
||||||
return val;
|
return val;
|
||||||
|
|
||||||
T ret = 0;
|
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)
|
if (!handlerManager[d.type]->convert(&d, t, &ret, ok) && ok)
|
||||||
*ok = false;
|
*ok = false;
|
||||||
return ret;
|
return ret;
|
||||||
@ -2714,6 +2751,11 @@ static bool canConvertMetaObject(int fromId, int toId, QObject *fromObject)
|
|||||||
*/
|
*/
|
||||||
bool QVariant::canConvert(int targetTypeId) const
|
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.
|
// TODO Reimplement this function, currently it works but it is a historical mess.
|
||||||
uint currentType = ((d.type == QMetaType::Float) ? QVariant::Double : d.type);
|
uint currentType = ((d.type == QMetaType::Float) ? QVariant::Double : d.type);
|
||||||
if (currentType == QMetaType::SChar || currentType == QMetaType::Char)
|
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
|
bool QVariant::convert(const int type, void *ptr) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(type < int(QMetaType::User));
|
|
||||||
return handlerManager[type]->convert(&d, type, ptr, 0);
|
return handlerManager[type]->convert(&d, type, ptr, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,11 +571,9 @@ namespace QtPrivate {
|
|||||||
const int vid = qMetaTypeId<T>();
|
const int vid = qMetaTypeId<T>();
|
||||||
if (vid == v.userType())
|
if (vid == v.userType())
|
||||||
return *reinterpret_cast<const T *>(v.constData());
|
return *reinterpret_cast<const T *>(v.constData());
|
||||||
if (vid < int(QMetaType::User)) {
|
T t;
|
||||||
T t;
|
if (v.convert(vid, &t))
|
||||||
if (v.convert(vid, &t))
|
return t;
|
||||||
return t;
|
|
||||||
}
|
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
#ifndef QT_NO_QOBJECT
|
#ifndef QT_NO_QOBJECT
|
||||||
|
@ -111,6 +111,8 @@ private slots:
|
|||||||
void metaObject();
|
void metaObject();
|
||||||
void constexprMetaTypeIds();
|
void constexprMetaTypeIds();
|
||||||
void constRefs();
|
void constRefs();
|
||||||
|
void convertCustomType_data();
|
||||||
|
void convertCustomType();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Foo { int i; };
|
struct Foo { int i; };
|
||||||
@ -1818,6 +1820,286 @@ void tst_QMetaType::constRefs()
|
|||||||
#endif
|
#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
|
// Compile-time test, it should be possible to register function pointer types
|
||||||
class Undefined;
|
class Undefined;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user