summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <[email protected]>2024-03-21 11:56:56 +0100
committerMarc Mutz <[email protected]>2024-04-29 09:23:50 +0200
commitab0b2a490eb98f3216403e87e4ea108a10fbeed2 (patch)
treebb78402984bf529fcd53557d06d07433fdb912bc
parent4946982534d4d256590dffd44046fe642456703f (diff)
QSignalSpy: fix -Wweak-vtable by removing the QObject inheritance
Normally, we'd fix -Wweak-vtable by exporting the class and making at least one virtual method out-of-line (typically the dtor), thereby pinning the vtable to exactly one TU. We can't export QSignalSpy, though, because it also inherits QList, and we don't want to export QList subclasses to avoid QList API becoming part of the ABI. So remove the vtable, and therefore its being a weak symbol, by moving the qt_metacall implementation from QSignalSpy into its newly-added Private, at the cost of an additional memory allocation at construction (though there was already the wish to make this class pimpl'ed for extensibility, and this patch accomplishes exactly that). This class used to be one of few places left that prevents adding -Wweak-vtable to headersclean, so while this is a breaking change, QSignalSpy doesn't really model is-a QObject. It uses QObject to reuse, not to be reused. In fact, no external code should use the QObject-ness of QSignalSpy, so it should be an acceptable SC break to drop the inheritance. We don't need to care about BC here, as we don't promise BC for QtTest. This now also allows (and requires) to make the dtor and the private ctor out-of-line, avoiding the need for the init() hack. [ChangeLog][QtTest][Potentially Source-Incompatible Changes] QSignalSpy no longer inherits from QObject. If your code uses the fact that QSignalSpy is-a QObject, you need to redesign around this now. Task-number: QTBUG-45582 Fixes: QTBUG-123544 Change-Id: Id93ba0ee6bbb811455d3744a045e38e1b9f9c584 Reviewed-by: Tor Arne Vestbø <[email protected]> Reviewed-by: Jason McDonald <[email protected]> Reviewed-by: Ahmad Samir <[email protected]>
-rw-r--r--src/testlib/qsignalspy.cpp47
-rw-r--r--src/testlib/qsignalspy.h33
2 files changed, 49 insertions, 31 deletions
diff --git a/src/testlib/qsignalspy.cpp b/src/testlib/qsignalspy.cpp
index 5fd61d42dcd..a45ca59378e 100644
--- a/src/testlib/qsignalspy.cpp
+++ b/src/testlib/qsignalspy.cpp
@@ -110,10 +110,6 @@ QT_BEGIN_NAMESPACE
Returns the normalized signal the spy is currently listening to.
*/
-/*! \fn int QSignalSpy::qt_metacall(QMetaObject::Call call, int id, void **a)
- \internal
-*/
-
/*! \fn bool QSignalSpy::wait(int timeout)
\since 5.0
@@ -244,22 +240,42 @@ QList<int> QSignalSpy::makeArgs(const QMetaMethod &member, const QObject *obj)
return result;
}
-void QSignalSpy::init(ObjectSignal os)
+class QSignalSpyPrivate : public QObject
+{
+ QSignalSpy * const q;
+public:
+ explicit QSignalSpyPrivate(QSignalSpy *qq) : q(qq) {}
+
+ int qt_metacall(QMetaObject::Call call, int methodId, void **a) override;
+};
+
+QSignalSpy::QSignalSpy(ObjectSignal os)
+ : args(os.obj ? makeArgs(os.sig, os.obj) : QList<int>{})
{
if (!os.obj)
return;
+ auto i = std::make_unique<QSignalSpyPrivate>(this);
+
const auto signalIndex = os.sig.methodIndex();
const auto slotIndex = QObject::staticMetaObject.methodCount();
if (!QMetaObject::connect(os.obj, signalIndex,
- this, slotIndex, Qt::DirectConnection)) {
+ i.get(), slotIndex, Qt::DirectConnection)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
+ d_ptr = std::move(i);
+
sig = os.sig.methodSignature();
}
+/*!
+ Destructor.
+*/
+QSignalSpy::~QSignalSpy()
+ = default;
+
void QSignalSpy::appendArgs(void **a)
{
QList<QVariant> list;
@@ -280,4 +296,23 @@ void QSignalSpy::appendArgs(void **a)
}
}
+/*!
+ \reimp
+ \internal
+*/
+int QSignalSpyPrivate::qt_metacall(QMetaObject::Call call, int methodId, void **a)
+{
+ methodId = QObject::qt_metacall(call, methodId, a);
+ if (methodId < 0)
+ return methodId;
+
+ if (call == QMetaObject::InvokeMetaMethod) {
+ if (methodId == 0) {
+ q->appendArgs(a);
+ }
+ --methodId;
+ }
+ return methodId;
+}
+
QT_END_NAMESPACE
diff --git a/src/testlib/qsignalspy.h b/src/testlib/qsignalspy.h
index f4c322ae718..591545b4d5d 100644
--- a/src/testlib/qsignalspy.h
+++ b/src/testlib/qsignalspy.h
@@ -6,24 +6,26 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qlist.h>
-#include <QtCore/qobject.h>
#include <QtCore/qmetaobject.h>
#include <QtTest/qtesteventloop.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmutex.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QVariant;
-
-class QSignalSpy: public QObject, public QList<QList<QVariant> >
+class QSignalSpyPrivate;
+class QSignalSpy : public QList<QList<QVariant> >
{
struct ObjectSignal {
const QObject *obj;
QMetaMethod sig;
};
-
+ friend class QSignalSpyPrivate;
+ std::unique_ptr<QSignalSpyPrivate> d_ptr;
public:
explicit QSignalSpy(const QObject *obj, const char *aSignal)
: QSignalSpy(verify(obj, aSignal)) {}
@@ -37,6 +39,7 @@ public:
#endif // Q_QDOC
QSignalSpy(const QObject *obj, QMetaMethod signal)
: QSignalSpy(verify(obj, signal)) {}
+ Q_TESTLIB_EXPORT ~QSignalSpy();
inline bool isValid() const { return !sig.isEmpty(); }
inline QByteArray signal() const { return sig; }
@@ -46,28 +49,8 @@ public:
Q_TESTLIB_EXPORT bool wait(std::chrono::milliseconds timeout = std::chrono::seconds{5});
- int qt_metacall(QMetaObject::Call call, int methodId, void **a) override
- {
- methodId = QObject::qt_metacall(call, methodId, a);
- if (methodId < 0)
- return methodId;
-
- if (call == QMetaObject::InvokeMetaMethod) {
- if (methodId == 0) {
- appendArgs(a);
- }
- --methodId;
- }
- return methodId;
- }
-
private:
- explicit QSignalSpy(ObjectSignal os)
- : args(os.obj ? makeArgs(os.sig, os.obj) : QList<int>{})
- {
- init(os);
- }
- Q_TESTLIB_EXPORT void init(ObjectSignal os);
+ Q_TESTLIB_EXPORT explicit QSignalSpy(ObjectSignal os);
Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, QMetaMethod signal);
Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, const char *aSignal);