summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
diff options
context:
space:
mode:
authorChristoph Schleifenbaum <christoph.schleifenbaum@kdab.com>2013-03-09 15:12:20 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-05-08 06:02:35 +0200
commitf7b313e6d865ac7020c3164c2f6f0232f052eb9b (patch)
tree9afb8af0bf8283db844df7f72540613d1ef51319 /src/corelib/kernel
parent46d80dedb7794c3e494cc818c85e023beb65626e (diff)
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>
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r--src/corelib/kernel/qmetatype.cpp122
-rw-r--r--src/corelib/kernel/qmetatype.h171
-rw-r--r--src/corelib/kernel/qvariant.cpp47
-rw-r--r--src/corelib/kernel/qvariant.h8
4 files changed, 340 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