From ab59a7ef09d56e59f9496ac59d88ba05db61e298 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Fri, 12 Oct 2012 12:20:40 +0200 Subject: Add private API to connect to slots in QObjectPrivate Change-Id: I16ffbf91ff4c6e9fca6fe7984800d2c24e70701b Reviewed-by: Thiago Macieira --- src/corelib/kernel/qobject_p.h | 77 +++++++++++++++++++++++ tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 67 ++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index e4b4ce8b42..e849ec1599 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Olivier Goffart ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -198,6 +199,14 @@ public: inline void connectNotify(const QMetaMethod &signal); inline void disconnectNotify(const QMetaMethod &signal); + template + static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot, + Qt::ConnectionType type = Qt::AutoConnection); + + template + static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot); public: ExtraData *extraData; // extra data set by the user QThreadData *threadData; // id of the thread that owns the object @@ -267,6 +276,74 @@ inline void QObjectPrivate::disconnectNotify(const QMetaMethod &signal) q_ptr->disconnectNotify(signal); } +namespace QtPrivate { +template class QPrivateSlotObject : public QSlotObjectBase +{ + typedef QtPrivate::FunctionPointer FuncType; + Func function; + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + { + switch (which) { + case Destroy: + delete static_cast(this_); + break; + case Call: + FuncType::template call(static_cast(this_)->function, + static_cast(QObjectPrivate::get(r)), a); + break; + case Compare: + *ret = *reinterpret_cast(a) == static_cast(this_)->function; + break; + case NumOperations: ; + } + } +public: + explicit QPrivateSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} +}; +} //namespace QtPrivate + +template +inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot, + Qt::ConnectionType type) +{ + typedef QtPrivate::FunctionPointer SignalType; + typedef QtPrivate::FunctionPointer SlotType; + reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(0)); + + //compilation error if the arguments does not match. + Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount), + "The slot requires more arguments than the signal provides."); + Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments::value), + "Signal and slot arguments are not compatible."); + Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible::value), + "Return type of the slot is not compatible with the return type of the signal."); + + const int *types = 0; + if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) + types = QtPrivate::ConnectionTypes::types(); + + return QObject::connectImpl(sender, reinterpret_cast(&signal), + receiverPrivate->q_func(), reinterpret_cast(&slot), + new QtPrivate::QPrivateSlotObject::Value, + typename SignalType::ReturnType>(slot), + type, types, &SignalType::Object::staticMetaObject); +} + +template +bool QObjectPrivate::disconnect(const typename QtPrivate::FunctionPointer< Func1 >::Object* sender, Func1 signal, + const typename QtPrivate::FunctionPointer< Func2 >::Object* receiverPrivate, Func2 slot) +{ + typedef QtPrivate::FunctionPointer SignalType; + typedef QtPrivate::FunctionPointer SlotType; + reinterpret_cast(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast(0)); + //compilation error if the arguments does not match. + Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments::value), + "Signal and slot arguments are not compatible."); + return QObject::disconnectImpl(sender, reinterpret_cast(&signal), + receiverPrivate->q_func(), reinterpret_cast(&slot), + &SignalType::Object::staticMetaObject); +} Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_MOVABLE_TYPE); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index c3ecf419e0..0521c2277c 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -141,6 +141,7 @@ private slots: void returnValue2_data(); void returnValue2(); void connectVirtualSlots(); + void connectPrivateSlots(); void connectFunctorArgDifference(); void disconnectDoesNotLeakFunctor(); }; @@ -5565,6 +5566,72 @@ void tst_QObject::connectVirtualSlots() */ } +#ifndef QT_BUILD_INTERNAL +void tst_QObject::connectPrivateSlots() +{QSKIP("Needs QT_BUILD_INTERNAL");} +#else +class ConnectToPrivateSlotPrivate; + +class ConnectToPrivateSlot :public QObject { + Q_OBJECT +public: + ConnectToPrivateSlot(); + void test(SenderObject *obj1) ; + Q_DECLARE_PRIVATE(ConnectToPrivateSlot) +}; + +class ConnectToPrivateSlotPrivate : public QObjectPrivate { +public: + Q_DECLARE_PUBLIC(ConnectToPrivateSlot) + int receivedCount; + QVariant receivedValue; + + void thisIsAPrivateSlot() { + receivedCount++; + }; + + void thisIsAPrivateSlotWithArg(const QVariant &v) { + receivedCount++; + receivedValue = v; + }; +}; + +ConnectToPrivateSlot::ConnectToPrivateSlot(): QObject(*new ConnectToPrivateSlotPrivate) {} + +void ConnectToPrivateSlot::test(SenderObject* obj1) { + Q_D(ConnectToPrivateSlot); + d->receivedCount = 0; + QVERIFY(QObjectPrivate::connect(obj1, &SenderObject::signal1, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot)); + QVERIFY(QObjectPrivate::connect(obj1, &SenderObject::signal7, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlotWithArg)); + QCOMPARE(d->receivedCount, 0); + obj1->signal1(); + QCOMPARE(d->receivedCount, 1); + QCOMPARE(d->receivedValue, QVariant()); + obj1->signal7(666, QLatin1Literal("_")); + QCOMPARE(d->receivedCount, 2); + QCOMPARE(d->receivedValue, QVariant(666)); + QVERIFY(QObjectPrivate::connect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot, Qt::UniqueConnection)); + QVERIFY(!QObjectPrivate::connect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot, Qt::UniqueConnection)); + obj1->signal2(); + QCOMPARE(d->receivedCount, 3); + QVERIFY(QObjectPrivate::disconnect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot)); + obj1->signal2(); + QCOMPARE(d->receivedCount, 3); + QVERIFY(!QObjectPrivate::disconnect(obj1, &SenderObject::signal2, d, &ConnectToPrivateSlotPrivate::thisIsAPrivateSlot)); +} + +void tst_QObject::connectPrivateSlots() +{ + SenderObject sender; + { + ConnectToPrivateSlot o; + o.test(&sender); + } + sender.signal7(777, QLatin1String("check that deleting the object properly disconnected")); + sender.signal1(); +} +#endif + struct SlotFunctor { void operator()() {} -- cgit v1.2.3