QMetaType: Allow registration of mutable views and register iterables

In order to modify a container through an iterable, we need the original
container to be mutable. The iterable, then, is not a conversion of the
container, but rather a view on the container. The concept may be
extended to other types.

In order to facilitate this, provide a set of methods in QMetaType and
QVariant similar to the convert family. The new methods are non-const
and expect the original value to stay available during the life time of
the view.

Change-Id: I363621033f7fc600edcea2acb786820ccba49c86
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Ulf Hermann 2020-09-10 18:30:22 +02:00
parent 5c808073af
commit 217a25a6bf
6 changed files with 564 additions and 112 deletions

View File

@ -501,9 +501,7 @@ public:
}
const void *constIterable() const { return m_iterable.constPointer(); }
// TODO: fix this when introducing mutable iterables
void *mutableIterable() { return const_cast<void *>(m_iterable.constPointer()); }
void *mutableIterable() { return m_iterable.mutablePointer(); }
QConstIterator<Container> constBegin() const
{

View File

@ -1580,6 +1580,10 @@ QMetaTypeConverterRegistry;
Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry)
using QMetaTypeMutableViewRegistry
= QMetaTypeFunctionRegistry<QMetaType::MutableViewFunction, QPair<int,int>>;
Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry)
/*!
\fn bool QMetaType::registerConverter()
\since 5.2
@ -1618,26 +1622,78 @@ Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry)
\since 5.2
\internal
*/
bool QMetaType::registerConverterFunction(const ConverterFunction &f, int from, int to)
bool QMetaType::registerConverterFunction(const ConverterFunction &f, QMetaType from, QMetaType to)
{
if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from, to), f)) {
if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from.id(), to.id()), f)) {
qWarning("Type conversion already registered from type %s to type %s",
QMetaType(from).name(), QMetaType(to).name());
from.name(), to.name());
return false;
}
return true;
}
/*!
\fn template<typename MemberFunction, int> bool QMetaType::registerMutableView(MemberFunction function)
\since 6.0
\overload
Registers a method \a function like \c {To From::function()} as mutable view of type \c {To} on
type \c {From} in the meta type system. Returns \c true if the registration succeeded, otherwise
\c false.
*/
/*!
\fn template<typename MemberFunctionOk, char> bool QMetaType::registerMutableView(MemberFunctionOk function)
\since 6.0
\overload
Registers a method \a function like To From::function(bool *ok) as mutable view of type To on
type From in the meta type system. Returns \c true if the registration succeeded, otherwise
\c false.
*/
/*!
\fn template<typename UnaryFunction> bool QMetaType::registerMutableView(UnaryFunction function)
\since 6.0
\overload
Registers a unary function object \a function as mutable view of type To on type From
in the meta type system. Returns \c true if the registration succeeded, otherwise \c false.
*/
/*!
Registers function \a f as mutable view of type id \a to on type id \a from.
Returns \c true if the registration succeeded, otherwise \c false.
\since 6.0
\internal
*/
bool QMetaType::registerMutableViewFunction(const MutableViewFunction &f, QMetaType from, QMetaType to)
{
if (!customTypesMutableViewRegistry()->insertIfNotContains(qMakePair(from.id(), to.id()), f)) {
qWarning("Mutable view on type already registered from type %s to type %s",
from.name(), to.name());
return false;
}
return true;
}
/*!
\internal
*/
void QMetaType::unregisterMutableViewFunction(QMetaType from, QMetaType to)
{
if (customTypesMutableViewRegistry.isDestroyed())
return;
customTypesMutableViewRegistry()->remove(from.id(), to.id());
}
/*!
\internal
Invoked automatically when a converter function object is destroyed.
*/
void QMetaType::unregisterConverterFunction(int from, int to)
void QMetaType::unregisterConverterFunction(QMetaType from, QMetaType to)
{
if (customTypesConversionRegistry.isDestroyed())
return;
customTypesConversionRegistry()->remove(from, to);
customTypesConversionRegistry()->remove(from.id(), to.id());
}
#ifndef QT_NO_DEBUG_STREAM
@ -1833,14 +1889,10 @@ static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType
#ifndef QT_BOOTSTRAPPED
static bool convertIterableToVariantList(QMetaType fromType, const void *from, void *to)
{
const QMetaType::ConverterFunction * const f =
customTypesConversionRegistry()->function(
qMakePair(fromType.id(), qMetaTypeId<QIterable<QMetaSequence>>()));
if (!f)
QSequentialIterable list;
if (!QMetaType::convert(fromType, from, QMetaType::fromType<QSequentialIterable>(), &list))
return false;
QSequentialIterable list;
(*f)(from, &list);
QVariantList &l = *static_cast<QVariantList *>(to);
l.clear();
l.reserve(list.size());
@ -1852,14 +1904,10 @@ static bool convertIterableToVariantList(QMetaType fromType, const void *from, v
static bool convertIterableToVariantMap(QMetaType fromType, const void *from, void *to)
{
const QMetaType::ConverterFunction * const f =
customTypesConversionRegistry()->function(
qMakePair(fromType.id(), qMetaTypeId<QIterable<QMetaAssociation>>()));
if (!f)
QAssociativeIterable map;
if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map))
return false;
QAssociativeIterable map;
(*f)(from, &map);
QVariantMap &h = *static_cast<QVariantMap *>(to);
h.clear();
auto end = map.end();
@ -1870,14 +1918,10 @@ static bool convertIterableToVariantMap(QMetaType fromType, const void *from, vo
static bool convertIterableToVariantHash(QMetaType fromType, const void *from, void *to)
{
const QMetaType::ConverterFunction * const f =
customTypesConversionRegistry()->function(
qMakePair(fromType.id(), qMetaTypeId<QIterable<QMetaAssociation>>()));
if (!f)
QAssociativeIterable map;
if (!QMetaType::convert(fromType, from, QMetaType::fromType<QAssociativeIterable>(), &map))
return false;
QAssociativeIterable map;
(*f)(from, &map);
QVariantHash &h = *static_cast<QVariantHash *>(to);
h.clear();
h.reserve(map.size());
@ -1948,6 +1992,60 @@ static bool convertToSequentialIterable(QMetaType fromType, const void *from, vo
return false;
}
static bool canConvertToSequentialIterable(QMetaType fromType)
{
switch (fromType.id()) {
case QMetaType::QVariantList:
case QMetaType::QStringList:
case QMetaType::QByteArrayList:
return true;
default:
return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaSequence>>());
}
}
static bool canImplicitlyViewAsSequentialIterable(QMetaType fromType)
{
switch (fromType.id()) {
case QMetaType::QVariantList:
case QMetaType::QStringList:
case QMetaType::QByteArrayList:
return true;
default:
return QMetaType::canView(
fromType, QMetaType::fromType<QIterable<QMetaSequence>>());
}
}
static bool viewAsSequentialIterable(QMetaType fromType, void *from, void *to)
{
using namespace QtMetaTypePrivate;
int fromTypeId = fromType.id();
QSequentialIterable &i = *static_cast<QSequentialIterable *>(to);
if (fromTypeId == QMetaType::QVariantList) {
i = QSequentialIterable(reinterpret_cast<QVariantList *>(from));
return true;
}
if (fromTypeId == QMetaType::QStringList) {
i = QSequentialIterable(reinterpret_cast<QStringList *>(from));
return true;
}
else if (fromTypeId == QMetaType::QByteArrayList) {
i = QSequentialIterable(reinterpret_cast<QByteArrayList *>(from));
return true;
}
QIterable<QMetaSequence> j;
if (QMetaType::view(
fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &j)) {
i = std::move(j);
return true;
}
return false;
}
static bool convertToAssociativeIterable(QMetaType fromType, const void *from, void *to)
{
using namespace QtMetaTypePrivate;
@ -1981,6 +2079,74 @@ static bool canConvertMetaObject(QMetaType fromType, QMetaType toType)
}
return false;
}
static bool canConvertToAssociativeIterable(QMetaType fromType)
{
switch (fromType.id()) {
case QMetaType::QVariantMap:
case QMetaType::QVariantHash:
return true;
default:
return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaAssociation>>());
}
}
static bool canImplicitlyViewAsAssociativeIterable(QMetaType fromType)
{
switch (fromType.id()) {
case QMetaType::QVariantMap:
case QMetaType::QVariantHash:
return true;
default:
return QMetaType::canView(
fromType, QMetaType::fromType<QIterable<QMetaAssociation>>());
}
}
static bool viewAsAssociativeIterable(QMetaType fromType, void *from, void *to)
{
using namespace QtMetaTypePrivate;
int fromTypeId = fromType.id();
QAssociativeIterable &i = *static_cast<QAssociativeIterable *>(to);
if (fromTypeId == QMetaType::QVariantMap) {
i = QAssociativeIterable(reinterpret_cast<QVariantMap *>(from));
return true;
}
if (fromTypeId == QMetaType::QVariantHash) {
i = QAssociativeIterable(reinterpret_cast<QVariantHash *>(from));
return true;
}
QIterable<QMetaAssociation> j;
if (QMetaType::view(
fromType, from, QMetaType::fromType<QIterable<QMetaAssociation>>(), &j)) {
i = std::move(j);
return true;
}
return false;
}
static bool convertQObject(QMetaType fromType, const void *from, QMetaType toType, void *to)
{
// handle QObject conversion
if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) {
QObject *fromObject = *static_cast<QObject * const *>(from);
// use dynamic metatype of from if possible
if (fromObject && fromObject->metaObject()->inherits(toType.metaObject())) {
*static_cast<QObject **>(to) = toType.metaObject()->cast(fromObject);
return true;
} else if (!fromObject && fromType.metaObject()) {
// if fromObject is null, use static fromType to check if conversion works
*static_cast<void **>(to) = nullptr;
return fromType.metaObject()->inherits(toType.metaObject());
} else {
return false;
}
}
return false;
}
#endif
/*!
@ -2053,21 +2219,82 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType,
if (toTypeId == qMetaTypeId<QAssociativeIterable>())
return convertToAssociativeIterable(fromType, from, to);
// handle QObject conversion
if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) {
QObject *fromObject = *static_cast<QObject * const *>(from);
// use dynamic metatype of from if possible
if (fromObject && fromObject->metaObject()->inherits(toType.metaObject())) {
*static_cast<QObject **>(to) = toType.metaObject()->cast(fromObject);
return true;
} else if (!fromObject && fromType.metaObject()) {
// if fromObject is null, use static fromType to check if conversion works
*static_cast<void **>(to) = nullptr;
return fromType.metaObject()->inherits(toType.metaObject());
} else {
return false;
}
}
return convertQObject(fromType, from, toType, to);
#else
return false;
#endif
}
/*!
Creates a mutable view on the object at \a from of \a fromType in the preallocated space at
\a to typed \a toType. Returns \c true if the conversion succeeded, otherwise false.
\since 6.0
*/
bool QMetaType::view(QMetaType fromType, void *from, QMetaType toType, void *to)
{
if (!fromType.isValid() || !toType.isValid())
return false;
int fromTypeId = fromType.id();
int toTypeId = toType.id();
const QMetaType::MutableViewFunction * const f =
customTypesMutableViewRegistry()->function(qMakePair(fromTypeId, toTypeId));
if (f)
return (*f)(from, to);
#ifndef QT_BOOTSTRAPPED
if (toTypeId == qMetaTypeId<QSequentialIterable>())
return viewAsSequentialIterable(fromType, from, to);
if (toTypeId == qMetaTypeId<QAssociativeIterable>())
return viewAsAssociativeIterable(fromType, from, to);
return convertQObject(fromType, from, toType, to);
#else
return false;
#endif
}
/*!
Returns \c true if QMetaType::view can create a mutable view of type \a toType
on type \a fromType.
Converting between pointers of types derived from QObject will return true for this
function if a qobject_cast from the type described by \a fromType to the type described
by \a toType would succeed.
You can create a mutable view of type QSequentialIterable on any container registered with
Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE().
Similarly you can create a mutable view of type QAssociativeIterable on any container
registered with Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE().
\sa convert(), QSequentialIterable, Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE(),
QAssociativeIterable, Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE()
*/
bool QMetaType::canView(QMetaType fromType, QMetaType toType)
{
int fromTypeId = fromType.id();
int toTypeId = toType.id();
if (fromTypeId == UnknownType || toTypeId == UnknownType)
return false;
const MutableViewFunction * const f =
customTypesMutableViewRegistry()->function(qMakePair(fromTypeId, toTypeId));
if (f)
return true;
#ifndef QT_BOOTSTRAPPED
if (toTypeId == qMetaTypeId<QSequentialIterable>())
return canImplicitlyViewAsSequentialIterable(fromType);
if (toTypeId == qMetaTypeId<QAssociativeIterable>())
return canImplicitlyViewAsAssociativeIterable(fromType);
if (canConvertMetaObject(fromType, toType))
return true;
#endif
return false;
@ -2165,17 +2392,25 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType)
return true;
#ifndef QT_BOOTSTRAPPED
if (toTypeId == QVariantList && hasRegisteredConverterFunction(
fromTypeId, qMetaTypeId<QIterable<QMetaSequence>>()))
return true;
if (toTypeId == qMetaTypeId<QSequentialIterable>())
return canConvertToSequentialIterable(fromType);
if ((toTypeId == QVariantHash || toTypeId == QVariantMap) && hasRegisteredConverterFunction(
fromTypeId, qMetaTypeId<QIterable<QMetaAssociation>>()))
if (toTypeId == qMetaTypeId<QAssociativeIterable>())
return canConvertToAssociativeIterable(fromType);
if (toTypeId == QVariantList
&& canConvert(fromType, QMetaType::fromType<QSequentialIterable>())) {
return true;
}
if ((toTypeId == QVariantHash || toTypeId == QVariantMap)
&& canConvert(fromType, QMetaType::fromType<QAssociativeIterable>())) {
return true;
}
#endif
if (toTypeId == QVariantPair && hasRegisteredConverterFunction(
fromTypeId, qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>()))
fromType, QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>()))
return true;
if (fromType.flags() & IsEnumeration) {
@ -2219,9 +2454,26 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType)
to \a toTypeId
\since 5.2
*/
bool QMetaType::hasRegisteredConverterFunction(int fromTypeId, int toTypeId)
bool QMetaType::hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType)
{
return customTypesConversionRegistry()->contains(qMakePair(fromTypeId, toTypeId));
return customTypesConversionRegistry()->contains(qMakePair(fromType.id(), toType.id()));
}
/*!
\fn bool QMetaType::hasRegisteredMutableViewFunction()
Returns \c true, if the meta type system has a registered mutable view on type From of type To.
\since 6.0
\overload
*/
/*!
Returns \c true, if the meta type system has a registered mutable view on meta type id
\a fromTypeId of meta type id \a toTypeId.
\since 5.2
*/
bool QMetaType::hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType)
{
return customTypesMutableViewRegistry()->contains(qMakePair(fromType.id(), toType.id()));
}
/*!

View File

@ -465,6 +465,9 @@ public:
// type erased converter function
using ConverterFunction = std::function<bool(const void *src, void *target)>;
// type erased mutable view, primarily for containers
using MutableViewFunction = std::function<bool(void *src, void *target)>;
// implicit conversion supported like double -> float
template<typename From, typename To>
static bool registerConverter()
@ -479,6 +482,13 @@ public:
static bool registerConverter(MemberFunctionOk function);
template<typename UnaryFunction>
static bool registerConverter(UnaryFunction function);
template<typename MemberFunction, int>
static bool registerMutableView(MemberFunction function);
template<typename MemberFunctionOk, char>
static bool registerMutableView(MemberFunctionOk function);
template<typename UnaryFunction>
static bool registerMutableView(UnaryFunction function);
#else
// member function as in "QString QFont::toString() const"
template<typename From, typename To>
@ -487,15 +497,33 @@ public:
static_assert((!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>();
const QMetaType fromType = QMetaType::fromType<From>();
const QMetaType toType = QMetaType::fromType<To>();
auto converter = [function](const void *from, void *to) -> bool {
const From *f = static_cast<const From *>(from);
To *t = static_cast<To *>(to);
*t = (f->*function)();
return true;
};
return registerConverterFunction(converter, fromTypeId, toTypeId);
return registerConverterFunction(converter, fromType, toType);
}
// member function
template<typename From, typename To>
static bool registerMutableView(To(From::*function)())
{
static_assert((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn),
"QMetaType::registerMutableView: At least one of the types must be a custom type.");
const QMetaType fromType = QMetaType::fromType<From>();
const QMetaType toType = QMetaType::fromType<To>();
auto view = [function](void *from, void *to) -> bool {
From *f = static_cast<From *>(from);
To *t = static_cast<To *>(to);
*t = (f->*function)();
return true;
};
return registerMutableViewFunction(view, fromType, toType);
}
// member function as in "double QString::toDouble(bool *ok = nullptr) const"
@ -505,8 +533,8 @@ public:
static_assert((!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>();
const QMetaType fromType = QMetaType::fromType<From>();
const QMetaType toType = QMetaType::fromType<To>();
auto converter = [function](const void *from, void *to) -> bool {
const From *f = static_cast<const From *>(from);
To *t = static_cast<To *>(to);
@ -516,7 +544,7 @@ public:
*t = To();
return result;
};
return registerConverterFunction(converter, fromTypeId, toTypeId);
return registerConverterFunction(converter, fromType, toType);
}
// functor or function pointer
@ -526,20 +554,41 @@ public:
static_assert((!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>();
const QMetaType fromType = QMetaType::fromType<From>();
const QMetaType toType = QMetaType::fromType<To>();
auto converter = [function](const void *from, void *to) -> bool {
const From *f = static_cast<const From *>(from);
To *t = static_cast<To *>(to);
*t = function(*f);
return true;
};
return registerConverterFunction(converter, fromTypeId, toTypeId);
return registerConverterFunction(converter, fromType, toType);
}
// functor or function pointer
template<typename From, typename To, typename UnaryFunction>
static bool registerMutableView(UnaryFunction function)
{
static_assert((!QMetaTypeId2<To>::IsBuiltIn || !QMetaTypeId2<From>::IsBuiltIn),
"QMetaType::registerMutableView: At least one of the types must be a custom type.");
const QMetaType fromType = QMetaType::fromType<From>();
const QMetaType toType = QMetaType::fromType<To>();
auto view = [function](void *from, void *to) -> bool {
From *f = static_cast<From *>(from);
To *t = static_cast<To *>(to);
*t = function(*f);
return true;
};
return registerMutableViewFunction(view, fromType, toType);
}
#endif
static bool convert(QMetaType fromType, const void *from, QMetaType toType, void *to);
static bool canConvert(QMetaType fromType, QMetaType toType);
static bool view(QMetaType fromType, void *from, QMetaType toType, void *to);
static bool canView(QMetaType fromType, QMetaType toType);
#if QT_DEPRECATED_SINCE(6, 0)
QT_DEPRECATED_VERSION_6_0
static bool convert(const void *from, int fromTypeId, void *to, int toTypeId)
@ -570,10 +619,20 @@ public:
template<typename From, typename To>
static bool hasRegisteredConverterFunction()
{
return hasRegisteredConverterFunction(qMetaTypeId<From>(), qMetaTypeId<To>());
return hasRegisteredConverterFunction(
QMetaType::fromType<From>(), QMetaType::fromType<To>());
}
static bool hasRegisteredConverterFunction(int fromTypeId, int toTypeId);
static bool hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType);
template<typename From, typename To>
static bool hasRegisteredMutableViewFunction()
{
return hasRegisteredMutableViewFunction(
QMetaType::fromType<From>(), QMetaType::fromType<To>());
}
static bool hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType);
#ifndef Q_CLANG_QDOC
template<typename, bool> friend struct QtPrivate::SequentialValueTypeIsMetaType;
@ -581,8 +640,11 @@ public:
template<typename, bool> friend struct QtPrivate::IsMetaTypePair;
template<typename, typename> friend struct QtPrivate::MetaTypeSmartPointerHelper;
#endif
static bool registerConverterFunction(const ConverterFunction &f, int from, int to);
static void unregisterConverterFunction(int from, int to);
static bool registerConverterFunction(const ConverterFunction &f, QMetaType from, QMetaType to);
static void unregisterConverterFunction(QMetaType from, QMetaType to);
static bool registerMutableViewFunction(const MutableViewFunction &f, QMetaType from, QMetaType to);
static void unregisterMutableViewFunction(QMetaType from, QMetaType to);
static void unregisterMetaType(QMetaType type);
QtPrivate::QMetaTypeInterface *iface() { return d_ptr; }
@ -833,9 +895,14 @@ namespace QtPrivate
};
template<typename T, bool = QtPrivate::IsSequentialContainer<T>::Value>
struct SequentialContainerConverterHelper
struct SequentialContainerTransformationHelper
{
static bool registerConverter(int)
static bool registerConverter()
{
return false;
}
static bool registerMutableView()
{
return false;
}
@ -844,51 +911,66 @@ namespace QtPrivate
template<typename T, bool = QMetaTypeId2<typename T::value_type>::Defined>
struct SequentialValueTypeIsMetaType
{
static bool registerConverter(int)
static bool registerConverter()
{
return false;
}
static bool registerMutableView()
{
return false;
}
};
template<typename T>
struct SequentialContainerConverterHelper<T, true> : SequentialValueTypeIsMetaType<T>
struct SequentialContainerTransformationHelper<T, true> : SequentialValueTypeIsMetaType<T>
{
};
template<typename T, bool = QtPrivate::IsAssociativeContainer<T>::Value>
struct AssociativeContainerConverterHelper
struct AssociativeContainerTransformationHelper
{
static bool registerConverter(int)
static bool registerConverter()
{
return false;
}
};
template<typename T, bool = QMetaTypeId2<typename T::mapped_type>::Defined>
struct AssociativeValueTypeIsMetaType
{
static bool registerConverter(int)
static bool registerMutableView()
{
return false;
}
};
template<typename T, bool = QMetaTypeId2<typename T::key_type>::Defined>
struct KeyAndValueTypeIsMetaType
struct AssociativeKeyTypeIsMetaType
{
static bool registerConverter(int)
static bool registerConverter()
{
return false;
}
static bool registerMutableView()
{
return false;
}
};
template<typename T, bool = QMetaTypeId2<typename T::mapped_type>::Defined>
struct AssociativeMappedTypeIsMetaType
{
static bool registerConverter()
{
return false;
}
static bool registerMutableView()
{
return false;
}
};
template<typename T>
struct KeyAndValueTypeIsMetaType<T, true> : AssociativeValueTypeIsMetaType<T>
{
};
template<typename T>
struct AssociativeContainerConverterHelper<T, true> : KeyAndValueTypeIsMetaType<T>
struct AssociativeContainerTransformationHelper<T, true> : AssociativeKeyTypeIsMetaType<T>
{
};
@ -896,7 +978,7 @@ namespace QtPrivate
&& QMetaTypeId2<typename T::second_type>::Defined>
struct IsMetaTypePair
{
static bool registerConverter(int)
static bool registerConverter()
{
return false;
}
@ -905,13 +987,13 @@ namespace QtPrivate
template<typename T>
struct IsMetaTypePair<T, true>
{
inline static bool registerConverter(int id);
inline static bool registerConverter();
};
template<typename T>
struct IsPair
{
static bool registerConverter(int)
static bool registerConverter()
{
return false;
}
@ -925,7 +1007,7 @@ namespace QtPrivate
template<typename T, typename = void>
struct MetaTypeSmartPointerHelper
{
static bool registerConverter(int) { return false; }
static bool registerConverter() { return false; }
};
Q_CORE_EXPORT bool isBuiltinType(const QByteArray &type);
@ -1042,10 +1124,12 @@ int qRegisterNormalizedMetaType(const QT_PREPEND_NAMESPACE(QByteArray) &normaliz
if (id > 0) {
QMetaType::registerNormalizedTypedef(normalizedTypeName, metaType);
QtPrivate::SequentialContainerConverterHelper<T>::registerConverter(id);
QtPrivate::AssociativeContainerConverterHelper<T>::registerConverter(id);
QtPrivate::MetaTypePairHelper<T>::registerConverter(id);
QtPrivate::MetaTypeSmartPointerHelper<T>::registerConverter(id);
QtPrivate::SequentialContainerTransformationHelper<T>::registerConverter();
QtPrivate::SequentialContainerTransformationHelper<T>::registerMutableView();
QtPrivate::AssociativeContainerTransformationHelper<T>::registerConverter();
QtPrivate::AssociativeContainerTransformationHelper<T>::registerMutableView();
QtPrivate::MetaTypePairHelper<T>::registerConverter();
QtPrivate::MetaTypeSmartPointerHelper<T>::registerConverter();
}
return id;
@ -1338,10 +1422,10 @@ template<typename T> \
struct MetaTypeSmartPointerHelper<SMART_POINTER<T> , \
typename std::enable_if<IsPointerToTypeDerivedFromQObject<T*>::Value>::type> \
{ \
static bool registerConverter(int id) \
static bool registerConverter() \
{ \
const int toId = QMetaType::QObjectStar; \
if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { \
const QMetaType to = QMetaType(QMetaType::QObjectStar); \
if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<SMART_POINTER<T>>(), to)) { \
QtPrivate::QSmartPointerConvertFunctor<SMART_POINTER<T> > o; \
return QMetaType::registerConverter<SMART_POINTER<T>, QObject*>(o); \
} \
@ -1421,10 +1505,10 @@ Q_DECLARE_METATYPE(QtMetaTypePrivate::QPairVariantInterfaceImpl)
QT_BEGIN_NAMESPACE
template <typename T>
inline bool QtPrivate::IsMetaTypePair<T, true>::registerConverter(int id)
inline bool QtPrivate::IsMetaTypePair<T, true>::registerConverter()
{
const int toId = qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
if (!QMetaType::hasRegisteredConverterFunction(id, toId)) {
const QMetaType to = QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
QtMetaTypePrivate::QPairVariantInterfaceConvertFunctor<T> o;
return QMetaType::registerConverter<T, QtMetaTypePrivate::QPairVariantInterfaceImpl>(o);
}
@ -1442,18 +1526,37 @@ struct QSequentialIterableConvertFunctor
}
};
template<typename From>
struct QSequentialIterableMutableViewFunctor
{
QIterable<QMetaSequence> operator()(From &f) const
{
return QIterable<QMetaSequence>(QMetaSequence::fromContainer<From>(), &f);
}
};
template<typename T>
struct SequentialValueTypeIsMetaType<T, true>
{
static bool registerConverter(int id)
static bool registerConverter()
{
const int toId = qMetaTypeId<QIterable<QMetaSequence>>();
if (!QMetaType::hasRegisteredConverterFunction(id, toId)) {
const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
QSequentialIterableConvertFunctor<T> o;
return QMetaType::registerConverter<T, QIterable<QMetaSequence>>(o);
}
return true;
}
}
static bool registerMutableView()
{
const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) {
QSequentialIterableMutableViewFunctor<T> o;
return QMetaType::registerMutableView<T, QIterable<QMetaSequence>>(o);
}
return true;
}
};
template<typename From>
@ -1465,24 +1568,41 @@ struct QAssociativeIterableConvertFunctor
}
};
template<typename T>
struct AssociativeValueTypeIsMetaType<T, true>
template<typename From>
struct QAssociativeIterableMutableViewFunctor
{
static bool registerConverter(int id)
QIterable<QMetaAssociation> operator()(From &f) const
{
const int toId = qMetaTypeId<QIterable<QMetaAssociation>>();
if (!QMetaType::hasRegisteredConverterFunction(id, toId)) {
return QIterable<QMetaAssociation>(QMetaAssociation::fromContainer<From>(), &f);
}
};
// Mapped type can be omitted, for example in case of a set.
// However, if it is available, we want to instantiate the metatype here.
template<typename T>
struct AssociativeKeyTypeIsMetaType<T, true> : AssociativeMappedTypeIsMetaType<T>
{
static bool registerConverter()
{
const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
QAssociativeIterableConvertFunctor<T> o;
return QMetaType::registerConverter<T, QIterable<QMetaAssociation>>(o);
}
return true;
}
static bool registerMutableView()
{
const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) {
QAssociativeIterableMutableViewFunctor<T> o;
return QMetaType::registerMutableView<T, QIterable<QMetaAssociation>>(o);
}
return true;
}
};
}
namespace QtPrivate {
class QMetaTypeInterface
{
public:

View File

@ -2068,6 +2068,13 @@ bool QVariant::convert(int type, void *ptr) const
return QMetaType::convert(d.type(), constData(), QMetaType(type), ptr);
}
/*!
\internal
*/
bool QVariant::view(int type, void *ptr)
{
return QMetaType::view(d.type(), data(), QMetaType(type), ptr);
}
/*!
\fn bool operator==(const QVariant &v1, const QVariant &v2)
@ -2457,6 +2464,18 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p)
\sa setValue(), fromValue(), canConvert(), Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE()
*/
/*! \fn template<typename T> T QVariant::view()
Returns a mutable view of template type \c{T} on the stored value.
Call canView() to find out whether such a view is supported.
If no such view can be created, returns the stored value converted to the
template type \c{T}. Call canConvert() to find out whether a type can be
converted. If the value can neither be viewed nor converted, a
\l{default-constructed value} will be returned.
\sa canView(), Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE()
*/
/*! \fn bool QVariant::canConvert() const
Returns \c true if the variant can be converted to the template type \c{T},
@ -2473,6 +2492,14 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p)
\sa convert()
*/
/*! \fn bool QVariant::canView() const
Returns \c true if a mutable view of the template type \c{T} can be created on this variant,
otherwise \c false.
\sa value()
*/
/*! \fn template<typename T> static QVariant QVariant::fromValue(const T &value)
Returns a QVariant containing a copy of \a value. Behaves

View File

@ -248,6 +248,9 @@ class Q_CORE_EXPORT QVariant
{ return QMetaType::canConvert(d.type(), targetType); }
bool convert(QMetaType type);
bool canView(QMetaType targetType) const
{ return QMetaType::canView(d.type(), targetType); }
#if QT_DEPRECATED_SINCE(6, 0)
QT_DEPRECATED_VERSION_6_0
bool canConvert(int targetTypeId) const
@ -363,6 +366,14 @@ class Q_CORE_EXPORT QVariant
inline T value() const
{ return qvariant_cast<T>(*this); }
template<typename T>
inline T view()
{
T t{};
QMetaType::view(metaType(), data(), QMetaType::fromType<T>(), &t);
return t;
}
template<typename T>
static inline QVariant fromValue(const T &value)
{
@ -383,6 +394,10 @@ class Q_CORE_EXPORT QVariant
bool canConvert() const
{ return canConvert(QMetaType::fromType<T>()); }
template<typename T>
bool canView() const
{ return canView(QMetaType::fromType<T>()); }
public:
struct PrivateShared
{
@ -488,7 +503,8 @@ protected:
Private d;
void create(int type, const void *copy);
bool equals(const QVariant &other) const;
bool convert(int t, void *ptr) const;
bool convert(int type, void *ptr) const;
bool view(int type, void *ptr);
private:
// force compile error, prevent QVariant(bool) to be called

View File

@ -278,6 +278,7 @@ private slots:
void sequentialIterableAppend();
void preferDirectConversionOverInterfaces();
void mutableView();
private:
void dataStream_data(QDataStream::Version version);
@ -4173,7 +4174,7 @@ void testSequentialIteration()
QVERIFY(listVariant.canConvert<QVariantList>());
QVariantList varList = listVariant.value<QVariantList>();
QCOMPARE(varList.size(), (int)std::distance(sequence.begin(), sequence.end()));
QSequentialIterable listIter = listVariant.value<QSequentialIterable>();
QSequentialIterable listIter = listVariant.view<QSequentialIterable>();
QCOMPARE(varList.size(), listIter.size());
typename Container::iterator containerIter = sequence.begin();
@ -4758,7 +4759,7 @@ void tst_QVariant::sequentialIterableAppend()
QList<int> container { 1, 2 };
auto variant = QVariant::fromValue(container);
QVERIFY(variant.canConvert<QIterable<QMetaSequence>>());
QSequentialIterable asIterable = variant.value<QIterable<QMetaSequence>>();
QSequentialIterable asIterable = variant.view<QIterable<QMetaSequence>>();
const int i = 3, j = 4;
void *mutableIterable = asIterable.mutableIterable();
asIterable.metaContainer().addValueAtEnd(mutableIterable, &i);
@ -4778,7 +4779,7 @@ void tst_QVariant::sequentialIterableAppend()
QSet<QByteArray> container { QByteArray{"hello"}, QByteArray{"world"} };
auto variant = QVariant::fromValue(std::move(container));
QVERIFY(variant.canConvert<QIterable<QMetaSequence>>());
QSequentialIterable asIterable = variant.value<QIterable<QMetaSequence>>();
QSequentialIterable asIterable = variant.view<QIterable<QMetaSequence>>();
QByteArray qba1 {"goodbye"};
QByteArray qba2 { "moon" };
void *mutableIterable = asIterable.mutableIterable();
@ -4834,5 +4835,43 @@ void tst_QVariant::preferDirectConversionOverInterfaces()
QVERIFY(calledCorrectConverter);
}
struct MyTypeView
{
MyType *data;
};
void tst_QVariant::mutableView()
{
bool calledView = false;
const bool success = QMetaType::registerMutableView<MyType, MyTypeView>([&](MyType &data) {
calledView = true;
return MyTypeView { &data };
});
QVERIFY(success);
QTest::ignoreMessage(
QtWarningMsg,
"Mutable view on type already registered from type MyType to type MyTypeView");
const bool shouldFail = QMetaType::registerMutableView<MyType, MyTypeView>([&](MyType &) {
return MyTypeView { nullptr };
});
QVERIFY(!shouldFail);
auto original = QVariant::fromValue(MyType {});
QVERIFY(original.canView<MyTypeView>());
QVERIFY(!original.canConvert<MyTypeView>());
MyTypeView view = original.view<MyTypeView>();
QVERIFY(calledView);
const char *txt = "lll";
view.data->number = 113;
view.data->text = txt;
MyType extracted = original.view<MyType>();
QCOMPARE(extracted.number, 0);
QCOMPARE(extracted.text, nullptr);
}
QTEST_MAIN(tst_QVariant)
#include "tst_qvariant.moc"