Fix all tst_qmetatype breakages after QList to QVector aliasing

In Qt6 QList is just a typedef to QVector. To keep Qt5  behavior
compatibility we need to register aliases, otherwise some type name
based operations would not work. The patch adds automatic
registration of QList metatype alias for every QVector.

The patch doesn't cover usage of already typedef'ed and aliased
QList and QVector, but that should be quite esoteric, especially
after introduction of automatic QList and QVector type registration.

Change-Id: I84672dda2b159d94e76cdc6034861e7d7ef52533
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Jędrzej Nowacki 2019-06-13 12:37:26 +02:00 committed by Lars Knoll
parent 5357231c0a
commit b76f662726
3 changed files with 68 additions and 2 deletions

View File

@ -37,6 +37,8 @@
**
****************************************************************************/
#include <bitset>
#include "qmetatype.h"
#include "qmetatype_p.h"
#include "qobjectdefs.h"
@ -2416,6 +2418,50 @@ const QMetaObject *metaObjectForQWidget()
return nullptr;
return qMetaObjectWidgetsHelper;
}
void qt5CompatibilityHookPostRegister(int id, const QByteArray &normalizedTypeName)
{
// In Qt6 QList got typedef'ed to QVector. To keep runtime behavior compatibility
// with Qt5 we install corresponding aliases. For example if one register
// QVector<QVector<int>>
// we need to register the type plus all possible aliases:
// QVector<QList<int>>
// QList<QVector<int>>
// QList<QList<int>>
// ### Qt6 TODO This is slow, as it allocates couple of strings we would need to
// if def this call with something like QT_NO_QLIST
const char *vectorName = "QVector<";
const char *listName = "QList<";
auto isSubstringOfAType = [](char c) { return c != ' ' && c != ',' && c != '<'; };
QVarLengthArray<int> indexes;
for (auto containerName: {vectorName, listName}) {
for (int i = normalizedTypeName.indexOf(containerName, 0); i != -1; i = normalizedTypeName.indexOf(containerName, i + 1)) {
if (!i || (i > 0 && !isSubstringOfAType(normalizedTypeName[i - 1])))
indexes.append(i);
}
}
// To avoid problems with the constantly changing size we start replacements
// from the end of normalizedTypeName
std::sort(indexes.rbegin(), indexes.rend());
for (quint64 combination = 1; ; ++combination) {
std::bitset<64> bits(combination);
QByteArray name = normalizedTypeName;
for (auto j = 0; j < indexes.size(); ++j) {
if (bits.test(j)) {
auto i = indexes[j];
auto replaceFrom = normalizedTypeName[i + 1] == 'V' ? vectorName : listName;
auto replaceTo = normalizedTypeName[i + 1] == 'V' ? listName : vectorName;
name.replace(i, sizeof(replaceFrom), replaceTo);
}
}
QMetaType::registerNormalizedTypedef(name, id);
if (bits.count() >= size_t(indexes.size()))
break;
}
}
}
namespace QtMetaTypePrivate {

View File

@ -1645,6 +1645,23 @@ namespace QtPrivate
static bool registerConverter(int) { return false; }
};
template<class T>
struct Qt5CompatibilityHook
{
static inline void postRegister(int, const QByteArray &) {};
};
Q_CORE_EXPORT void qt5CompatibilityHookPostRegister(int id, const QByteArray &normalizedTypeName);
template<class T>
struct Qt5CompatibilityHook<QVector<T>>
{
static inline void postRegister(int id, const QByteArray &normalizedTypeName)
{
qt5CompatibilityHookPostRegister(id, normalizedTypeName);
}
};
Q_CORE_EXPORT bool isBuiltinType(const QByteArray &type);
} // namespace QtPrivate
@ -1771,6 +1788,7 @@ int qRegisterNormalizedMetaType(const QT_PREPEND_NAMESPACE(QByteArray) &normaliz
QtPrivate::AssociativeContainerConverterHelper<T>::registerConverter(id);
QtPrivate::MetaTypePairHelper<T>::registerConverter(id);
QtPrivate::MetaTypeSmartPointerHelper<T>::registerConverter(id);
QtPrivate::Qt5CompatibilityHook<T>::postRegister(id, normalizedTypeName);
}
return id;

View File

@ -572,12 +572,14 @@ void tst_QMetaType::typeName_data()
QTest::newRow("124125534") << 124125534 << QString();
// automatic registration
QTest::newRow("QList<int>") << ::qMetaTypeId<QList<int> >() << QString::fromLatin1("QList<int>");
QTest::newRow("QHash<int,int>") << ::qMetaTypeId<QHash<int, int> >() << QString::fromLatin1("QHash<int,int>");
QTest::newRow("QMap<int,int>") << ::qMetaTypeId<QMap<int, int> >() << QString::fromLatin1("QMap<int,int>");
QTest::newRow("QVector<QList<int>>") << ::qMetaTypeId<QVector<QList<int> > >() << QString::fromLatin1("QVector<QList<int> >");
QTest::newRow("QVector<QMap<int,int>>") << ::qMetaTypeId<QVector<QMap<int, int> > >() << QString::fromLatin1("QVector<QMap<int,int> >");
// automatic registration with automatic QList to QVector aliasing
QTest::newRow("QList<int>") << ::qMetaTypeId<QList<int> >() << QString::fromLatin1("QVector<int>");
QTest::newRow("QVector<QList<int>>") << ::qMetaTypeId<QVector<QList<int> > >() << QString::fromLatin1("QVector<QVector<int> >");
QTest::newRow("CustomQObject*") << ::qMetaTypeId<CustomQObject*>() << QString::fromLatin1("CustomQObject*");
QTest::newRow("CustomGadget") << ::qMetaTypeId<CustomGadget>() << QString::fromLatin1("CustomGadget");
QTest::newRow("CustomGadget*") << ::qMetaTypeId<CustomGadget*>() << QString::fromLatin1("CustomGadget*");