diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2021-09-27 18:27:15 -0700 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2021-12-17 17:54:05 +0100 |
commit | 46dc8e453ae1d0c1eb749cfebe686995f3a6cfd0 (patch) | |
tree | d7893f8e2ce0a0b1d1d8cb5dffd2aebe1c8de48c /src/corelib | |
parent | af36675afd4300387390ceaba35a81994751cb75 (diff) |
QVariant: use a typedef name when saving user types to QDataStream
Due to the way Qt 5 and 6 registered type names, they end up producing
different type names for the same content for a typedef. For example,
because Q_DECLARE_METATYPE can't manage a comma (it's a macro), users
are forced to write something like:
using MyTypeMap = QMap<QString, MyType>
Q_DECLARE_METATYPE(MyTypeMap)
Qt 5's Q_DECLARE_METATYPE's argument "MyTypeMap" was the only name we
knew about the type, so that's what got saved in the stream. However, Qt
6 QtPrivate::typenameHelper is much more clever and obtains the name
from the compiler itself, so it "sees through" the typedef and registers
"QMap<QString,MyType>" as the official type name.
If another library/plugin has a different typedef name for the same type
(e.g., StringTypeMap), it's indeterminate which type gets saved and will
even change from run to run (depends on the QHash order).
[ChangeLog][QtCore][QDataStream] If QDataStream is used with a
QDataStream::Version < Qt_6_0 to serialize a user type that was
registered via a typedef with the metatype system, the typedef's name is
used in the stream instead of the non-typedef name. This restores
compatibility with Qt 5, allowing existing content to read the same
QDataStreams; reading from older Qt 6 versions should not be affected.
(Note: if more than one typedef name is registered, it's indetermine
which name gets used)
Fixes: QTBUG-96916
Pick-to: 6.3 6.2
Change-Id: I2bbf422288924c198645fffd16a8d811aa58201e
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 40 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype_p.h | 1 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 14 |
3 files changed, 49 insertions, 6 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 9494ff84f9..476234d7c5 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -1,6 +1,8 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com +** Copyright (C) 2021 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -179,6 +181,42 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry) } // namespace +// used by QVariant::save(): returns the name used in the Q_DECLARE_METATYPE +// macro (one of them, indetermine which one) +const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d) +{ + const char *name = nullptr; + QMetaTypeCustomRegistry *r = customTypeRegistry; + if (!r) + return name; + + QByteArrayView officialName(type_d->name); + QReadLocker l(&r->lock); + auto it = r->aliases.constBegin(); + auto end = r->aliases.constEnd(); + for ( ; it != end; ++it) { + if (it.value() != type_d) + continue; + if (it.key() == officialName) + continue; // skip the official name + name = it.key().constData(); + break; + } + +#ifndef QT_NO_DEBUG + QByteArrayList otherNames; + for ( ; it != end; ++it) { + if (it.value() == type_d) + otherNames << it.key(); + } + if (!otherNames.isEmpty()) + qWarning("QMetaType: type %s has more than one typedef alias: %s, %s", + type_d->name, name, otherNames.join(", ").constData()); +#endif + + return name; +} + /*! \macro Q_DECLARE_OPAQUE_POINTER(PointerType) \relates QMetaType diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h index 16270bc374..2b533608be 100644 --- a/src/corelib/kernel/qmetatype_p.h +++ b/src/corelib/kernel/qmetatype_p.h @@ -233,6 +233,7 @@ static const QT_PREPEND_NAMESPACE(QtPrivate::QMetaTypeInterface) *getInterfaceFr case QMetaType::MetaTypeName: \ return QtMetaTypePrivate::getInterfaceFromType<RealName>(); +const char *typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d); } //namespace QtMetaTypePrivate QT_END_NAMESPACE diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index b34da6ca18..b942ac50c7 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. -** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2021 Intel Corporation. ** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com> ** Contact: https://www.qt.io/licensing/ ** @@ -1314,13 +1314,17 @@ void QVariant::save(QDataStream &s) const } } const char *typeName = nullptr; - if (saveAsUserType) - typeName = d.type().name(); + if (saveAsUserType) { + if (s.version() < QDataStream::Qt_6_0) + typeName = QtMetaTypePrivate::typedefNameForType(d.type().d_ptr); + if (!typeName) + typeName = d.type().name(); + } s << typeId; if (s.version() >= QDataStream::Qt_4_2) s << qint8(d.is_null); if (typeName) - s << d.type().name(); + s << typeName; if (!isValid()) { if (s.version() < QDataStream::Qt_5_0) |