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
parent
5c808073af
commit
217a25a6bf
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue