QMetaType: auto-unregister converters and mutable views again

The port from hand-rolled function storage to std::function
inadvertently removed the helper variable of static storage duration
whose dtor would unregister the conversion function. This caused
QTBUG-94831, where the cleanup of conversion functions attempts to
call code (via std::function) from a library that has already been
unloaded.

Restore the 5.15 behavior by adding a static-storage-duration scope
guard to unregister the conversion and view functions from Qt upon
library unload (when static objects are destroyed). Unlike 5.15, only
install the scope guard upon successful registration, ensuring that
only the DLL which successfully registered its conversion function
unregisters it again.

Amends 0e4ae4fbf8e320d18c29a55b5db2bba25b3d9d50.

Add some strategic std::move()s as a drive-by.

Pick-to: 6.2
Task-number: QTBUG-94831
Change-Id: I391ca667420cf0d98a166676b9bb363d6e190306
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2021-08-02 15:01:40 +02:00
parent e933d71a61
commit fa7f4c5efd

View File

@ -48,6 +48,7 @@
#include <QtCore/qcompare.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qrefcount.h>
#include <QtCore/qscopeguard.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qiterable.h>
#ifndef QT_NO_QOBJECT
@ -570,7 +571,7 @@ public:
*t = (f->*function)();
return true;
};
return registerConverterFunction(converter, fromType, toType);
return registerConverterImpl<From, To>(converter, fromType, toType);
}
// member function
@ -588,7 +589,7 @@ public:
*t = (f->*function)();
return true;
};
return registerMutableViewFunction(view, fromType, toType);
return registerMutableViewImpl<From, To>(view, fromType, toType);
}
// member function as in "double QString::toDouble(bool *ok = nullptr) const"
@ -609,7 +610,7 @@ public:
*t = To();
return result;
};
return registerConverterFunction(converter, fromType, toType);
return registerConverterImpl<From, To>(converter, fromType, toType);
}
// functor or function pointer
@ -621,13 +622,13 @@ public:
const QMetaType fromType = QMetaType::fromType<From>();
const QMetaType toType = QMetaType::fromType<To>();
auto converter = [function](const void *from, void *to) -> bool {
auto converter = [function = std::move(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, fromType, toType);
return registerConverterImpl<From, To>(std::move(converter), fromType, toType);
}
// functor or function pointer
@ -639,15 +640,43 @@ public:
const QMetaType fromType = QMetaType::fromType<From>();
const QMetaType toType = QMetaType::fromType<To>();
auto view = [function](void *from, void *to) -> bool {
auto view = [function = std::move(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);
return registerMutableViewImpl<From, To>(std::move(view), fromType, toType);
}
#endif
private:
template<typename From, typename To>
static bool registerConverterImpl(ConverterFunction converter, QMetaType fromType, QMetaType toType)
{
if (registerConverterFunction(std::move(converter), fromType, toType)) {
static const auto unregister = qScopeGuard([=] {
unregisterConverterFunction(fromType, toType);
});
return true;
} else {
return false;
}
}
template<typename From, typename To>
static bool registerMutableViewImpl(MutableViewFunction view, QMetaType fromType, QMetaType toType)
{
if (registerMutableViewFunction(std::move(view), fromType, toType)) {
static const auto unregister = qScopeGuard([=] {
unregisterMutableViewFunction(fromType, toType);
});
return true;
} else {
return false;
}
}
#endif // Q_CLANG_DOC
public:
static bool convert(QMetaType fromType, const void *from, QMetaType toType, void *to);
static bool canConvert(QMetaType fromType, QMetaType toType);