From 583c55b243d9894d93d32fbe15bece2a9beb1d10 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Wed, 23 Nov 2011 15:06:30 +0100 Subject: [PATCH] New QObject connection syntax In addition to the old connection syntax, you can now connect using function pointers. connect(sender, &Sender::valueChanged, receiver, &Receiver::updateValue ); You can connect also to functor or C++11 lambdas The connections are now type safe (no more problems with namespaces or typedefs). Implicit type conversion is also supported. The new syntax forces us to change the meaning of signal form protected to public, in order to be able to access the signal's address everywhere The way it works is by introducing new overload of QObject::connect that take function pointer as parametter. Those new overload are template function, that are implemented inline. The actual implementation is in QObject::connectImpl which take a QObject::QSlotObject* as parametter for the slot. That slot object contains a virtual function which call the slot which has to be implemented in the header as it depends on the template parametter. So the internals of QObjectPrivate::Connection will store this QObjectSlot* in order to be able to make the call. You can read a full description here: http://developer.qt.nokia.com/wiki/New_Signal_Slot_Syntax History of commits before it was imported on gerrit: https://qt.gitorious.org/~ogoffart/qt/ogoffarts-qtbase/commits/qobject_connect_ptr Thread on the mailing list: http://lists.qt.nokia.com/pipermail/qt5-feedback/2011-August/000796.html http://lists.qt.nokia.com/pipermail/qt5-feedback/2011-September/001248.html (The discussions on the mailing list were about trying to find a solution that do not need making signals public, but no user friendly solution was found) Note: support for QueuedConnection, and the symetric QObject::disconnect is added in another commit. Qt::UniqueConnection is not supported yet in the new overload. Change-Id: I67d08436b0720e7f2992be9f7e34770960fa58fa Reviewed-by: Thiago Macieira --- .../code/src_corelib_kernel_qobject.cpp | 23 + src/corelib/kernel/qobject.cpp | 164 ++++++- src/corelib/kernel/qobject.h | 101 +++++ src/corelib/kernel/qobject_impl.h | 354 +++++++++++++++ src/corelib/kernel/qobject_p.h | 11 +- src/corelib/kernel/qobjectdefs.h | 4 +- .../corelib/kernel/qobject/tst_qobject.cpp | 409 ++++++++++++++++++ 7 files changed, 1056 insertions(+), 10 deletions(-) create mode 100644 src/corelib/kernel/qobject_impl.h diff --git a/doc/src/snippets/code/src_corelib_kernel_qobject.cpp b/doc/src/snippets/code/src_corelib_kernel_qobject.cpp index 086c62893df..11b70cc1ab5 100644 --- a/doc/src/snippets/code/src_corelib_kernel_qobject.cpp +++ b/doc/src/snippets/code/src_corelib_kernel_qobject.cpp @@ -448,6 +448,29 @@ QListWidget *list = parentWidget->findChild(QString(), Qt::FindDi QList childButtons = parentWidget.findChildren(QString(), Qt::FindDirectChildOnly); //! [43] +//! [44] +QLabel *label = new QLabel; +QLineEdit *lineEdit = new QLineEdit; +QObject::connect(lineEdit, &QLineEdit::textChanged, + label, &QLabel::setText); +//! [44] + +//! [45] +void someFunction(); +QPushButton *button = new QPushButton; +QObject::connect(button, &QPushButton::clicked, someFunction); +//! [45] + +//! [46] +QByteArray page = ...; +QTcpSocket *socket = new QTcpSocket; +socket->connectToHost("qt-project.org", 80); +QObject::connect(socket, &QTcpSocket::connected, [=] () { + socket->write("GET " + page + "\r\n"); + }); +//! [46] + + //! [meta data] //: This is a comment for the translator. diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 59282d3464c..e8735d0c092 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -923,9 +923,13 @@ QObject::~QObject() QObjectPrivate::Connection::~Connection() { - int *v = argumentTypes.load(); - if (v != &DIRECT_CONNECTION_ONLY) - delete [] v; + if (ownArgumentTypes) { + const int *v = argumentTypes.load(); + if (v != &DIRECT_CONNECTION_ONLY) + delete [] v; + } + if (isSlotObject && !slotObj->ref.deref()) + delete slotObj; } @@ -3022,6 +3026,7 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, i c->method_relative = method_index; c->method_offset = method_offset; c->connectionType = type; + c->isSlotObject = false; c->argumentTypes.store(types); c->nextConnectionList = 0; c->callFunction = callFunction; @@ -3231,7 +3236,7 @@ void QMetaObject::connectSlotsByName(QObject *o) static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv) { - int *argumentTypes = c->argumentTypes.load(); + const int *argumentTypes = c->argumentTypes.load(); if (!argumentTypes && argumentTypes != &DIRECT_CONNECTION_ONLY) { QMetaMethod m = sender->metaObject()->method(signal); argumentTypes = queuedConnectionTypes(m.parameterTypes()); @@ -3392,7 +3397,12 @@ void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_sign } const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction; const int method_relative = c->method_relative; - if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) { + if (c->isSlotObject) { + QExplicitlySharedDataPointer obj(c->slotObj); + locker.unlock(); + obj->call(receiver, argv ? argv : empty_argv); + locker.relock(); + } else if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) { //we compare the vtable to make sure we are not in the destructor of the object. locker.unlock(); if (qt_signal_spy_callback_set.slot_begin_callback != 0) @@ -4042,6 +4052,146 @@ void qDeleteInEventHandler(QObject *o) delete o; } +/*! + \fn QMetaObject::Connection QObject::connect(const QObject *sender, (T::*signal)(...), const QObject *receiver, (T::*method)(...), Qt::ConnectionType type) + + \threadsafe + + Creates a connection of the given \a type from the \a signal in + the \a sender object to the \a method in the \a receiver object. + Returns a handle to the connection that can be used to disconnect + it later. + + The signal must be a function declared as a signal in the header. + The slot function can be any member function that can be connected + to the signal. + A slot can be connected to a given signal if the signal has at + least as many arguments as the slot, and there is an implicit + conversion between the types of the corresponding arguments in the + signal and the slot. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 44 + + This example ensures that the label always displays the current + line edit text. + + A signal can be connected to many slots and signals. Many signals + can be connected to one slot. + + If a signal is connected to several slots, the slots are activated + in the same order as the order the connection was made, when the + signal is emitted + + The function returns an handle to a connection if it successfully + connects the signal to the slot. The Connection handle will be invalid + if it cannot create the connection, for example, if QObject is unable + to verify the existence of \a signal (if it was not declared as a signal) + You can check if the QMetaObject::Connection is valid by casting it to a bool. + + The optional \a type parameter describes the type of connection + to establish. In particular, it determines whether a particular + signal is delivered to a slot immediately or queued for delivery + at a later time. If the signal is queued, the parameters must be + of types that are known to Qt's meta-object system, because Qt + needs to copy the arguments to store them in an event behind the + scenes. If you try to use a queued connection and get the error + message + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 25 + + make sure to declare the argument type with Q_DECLARE_METATYPE + + A signal is emitted for every connection you make; + two signals are emitted for duplicate connections. + This overload does not support the type Qt::UniqueConnection + */ + + +/*! + \fn QMetaObject::Connection QObject::connect(const QObject *sender, (T::*signal)(...), Functor functor) + + \threadsafe + \overload + + Creates a connection of the given \a type from the \a signal in + the \a sender object to the \a functor and returns a handle to the connection + + The signal must be a function declared as a signal in the header. + The slot function can be any function or functor that can be connected + to the signal. + A function can be connected to a given signal if the signal as at + least as many argument as the slot. A functor can be connected to a signal + if they have exactly the same number of arguments. There must exist implicit + conversion between the types of the corresponding arguments in the + signal and the slot. + + Example: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 45 + + If your compiler support C++11 lambda expressions, you can use them: + + \snippet doc/src/snippets/code/src_corelib_kernel_qobject.cpp 46 + + The connection will automatically disconnect if the sender is destroyed. + */ +QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal, const QObject *receiver, QObject::QSlotObjectBase *slotObj, + Qt::ConnectionType type, const int* types, const QMetaObject* senderMetaObject) +{ + int signal_index = -1; + void *args[] = { &signal_index, signal }; + senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); + if (signal_index < 0 || signal_index >= QMetaObjectPrivate::get(senderMetaObject)->signalCount) { + qWarning("QObject::connect: signal not found in %s", senderMetaObject->className()); + return QMetaObject::Connection(0); + } + int signalOffset, methodOffset; + computeOffsets(senderMetaObject, &signalOffset, &methodOffset); + signal_index += signalOffset; + + // duplicated from QMetaObjectPrivate::connect + QObject *s = const_cast(sender); + QObject *r = const_cast(receiver); + + QOrderedMutexLocker locker(signalSlotLock(sender), + signalSlotLock(receiver)); + + if (type & Qt::UniqueConnection) { + qWarning() << "QObject::connect: Qt::UniqueConnection not supported when connecting function pointers"; + type = static_cast(type & (Qt::UniqueConnection - 1)); + } + + QScopedPointer c(new QObjectPrivate::Connection); + c->sender = s; + c->receiver = r; + c->slotObj = slotObj; + c->connectionType = type; + c->isSlotObject = true; + if (types) { + c->argumentTypes.store(types); + c->ownArgumentTypes = false; + } + + QObjectPrivate::get(s)->addConnection(signal_index, c.data()); + + c->prev = &(QObjectPrivate::get(r)->senders); + c->next = *c->prev; + *c->prev = c.data(); + if (c->next) + c->next->prev = &c->next; + + QObjectPrivate *const sender_d = QObjectPrivate::get(s); + if (signal_index < 0) { + sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0; + } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) { + sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f)); + } + + return QMetaObject::Connection(c.take()); +} + /*! Disconnect a connection. @@ -4119,6 +4269,10 @@ QMetaObject::Connection::~Connection() the signal or the slot, or if the arguments do not match. */ +QObject::QSlotObjectBase::~QSlotObjectBase() +{ +} + QT_END_NAMESPACE #include "moc_qobject.cpp" diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 038b59042be..93448b5ad10 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -52,6 +52,9 @@ #include #endif #include +#include + +#include QT_BEGIN_HEADER @@ -204,6 +207,58 @@ public: inline QMetaObject::Connection connect(const QObject *sender, const char *signal, const char *member, Qt::ConnectionType type = Qt::AutoConnection) const; + //Connect a signal to a pointer to qobject member function + template + static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object *receiver, Func2 slot, + Qt::ConnectionType type = Qt::AutoConnection) + { + 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. + typedef typename QtPrivate::CheckCompatibleArguments::IncompatibleSignalSlotArguments EnsureCompatibleArguments; + + const int *types = 0; + return connectImpl(sender, reinterpret_cast(&signal), + receiver, new QSlotObject::Value, + typename SignalType::ReturnType>(slot), + type, types, &SignalType::Object::staticMetaObject); + } + + //connect to a function pointer (not a member) + template + static inline typename QtPrivate::QEnableIf::ArgumentCount) >= 0, QMetaObject::Connection>::Type + connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, Func2 slot) + { + typedef QtPrivate::FunctionPointer SignalType; + typedef QtPrivate::FunctionPointer SlotType; + + //compilation error if the arguments does not match. + typedef typename QtPrivate::CheckCompatibleArguments::IncompatibleSignalSlotArguments EnsureCompatibleArguments; + typedef typename QtPrivate::QEnableIf<(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount))>::Type EnsureArgumentsCount; + + return connectImpl(sender, reinterpret_cast(&signal), sender, + new QStaticSlotObject::Value, + typename SignalType::ReturnType>(slot), + Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject); + } + + //connect to a functor + template + static inline typename QtPrivate::QEnableIf::ArgumentCount == -1, QMetaObject::Connection>::Type + connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, Func2 slot) + { + typedef QtPrivate::FunctionPointer SignalType; + + return connectImpl(sender, reinterpret_cast(&signal), + sender, new QFunctorSlotObject(slot), + Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject); + } + static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member); static bool disconnect(const QObject *sender, const QMetaMethod &signal, @@ -273,6 +328,52 @@ protected: private: Q_DISABLE_COPY(QObject) Q_PRIVATE_SLOT(d_func(), void _q_reregisterTimers(void *)) + + private: + // internal base class (interface) containing functions required to call a slot managed by a pointer to function. + struct QSlotObjectBase { + QAtomicInt ref; + QSlotObjectBase() : ref(1) {} + virtual ~QSlotObjectBase(); + virtual void call(QObject *receiver, void **a) = 0; + }; + // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject + // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. + template struct QSlotObject : QSlotObjectBase + { + typedef QtPrivate::FunctionPointer FuncType; + Func function; + QSlotObject(Func f) : function(f) {}; + virtual void call(QObject *receiver, void **a) { + FuncType::template call(function, static_cast(receiver), a); + } + }; + // implementation of QSlotObjectBase for which the slot is a static function + // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. + template struct QStaticSlotObject : QSlotObjectBase + { + typedef QtPrivate::FunctionPointer FuncType; + Func function; + QStaticSlotObject(Func f) : function(f) {} + virtual void call(QObject *receiver, void **a) { + FuncType::template call(function, receiver, a); + } + }; + // implementation of QSlotObjectBase for which the slot is a functor (or lambda) + // N is the number of arguments + // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. + template struct QFunctorSlotObject : QSlotObjectBase + { + typedef QtPrivate::Functor FuncType; + Func function; + QFunctorSlotObject(const Func &f) : function(f) {} + virtual void call(QObject *receiver, void **a) { + FuncType::template call(function, receiver, a); + } + }; + + static QMetaObject::Connection connectImpl(const QObject *sender, void **signal, const QObject *receiver, QSlotObjectBase *slot, + Qt::ConnectionType type, const int *types, const QMetaObject *senderMetaObject); }; inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal, diff --git a/src/corelib/kernel/qobject_impl.h b/src/corelib/kernel/qobject_impl.h new file mode 100644 index 00000000000..7d3dc1659bd --- /dev/null +++ b/src/corelib/kernel/qobject_impl.h @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef Q_QDOC + +#ifndef QOBJECT_H +#error Do not include qobject_impl.h directly +#endif + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +namespace QtPrivate { + template struct RemoveRef { typedef T Type; }; + template struct RemoveRef { typedef T Type; }; + template struct RemoveRef { typedef T Type; }; + template struct RemoveConstRef { typedef T Type; }; + template struct RemoveConstRef { typedef T Type; }; + + /* + The following List classes are used to help to handle the list of arguments. + It follow the same principles as the lisp lists. + List_Left take a list and a number as a parametter and returns (via the Value typedef, + the list composed of the first N element of the list + */ +#ifndef Q_COMPILER_VARIADIC_TEMPLATES + template struct List { typedef Head Car; typedef Tail Cdr; }; + template struct List_Left { typedef List::Value > Value; }; + template struct List_Left { typedef void Value; }; +#else + // With variadic template, lists are represented using a variadic template argument instead of the lisp way + template struct List {}; + template struct List { typedef Head Car; typedef List Cdr; }; + template struct List_Append; + template struct List_Append, List> { typedef List Value; }; + template struct List_Left { + typedef typename List_Append,typename List_Left::Value>::Value Value; + }; + template struct List_Left { typedef List<> Value; }; +#endif + // List_Select returns (via typedef Value) the Nth element of the list L + template struct List_Select { typedef typename List_Select::Value Value; }; + template struct List_Select { typedef typename L::Car Value; }; + + /* + trick to set the return value of a slot that works even if the signal or the slot returns void + to be used like function(), ApplyReturnValue(&return_value) + if function() returns a value, the operator,(T, ApplyReturnValue) is called, but if it + returns void, the builtin one is used without an error. + */ + template + struct ApplyReturnValue { + void *data; + ApplyReturnValue(void *data) : data(data) {} + }; + template + void operator,(const T &value, const ApplyReturnValue &container) { + *reinterpret_cast(container.data) = value; + } +#ifdef Q_COMPILER_RVALUE_REFS + template + void operator,(T &&value, const ApplyReturnValue &container) { + *reinterpret_cast(container.data) = value; + } +#endif + template + void operator,(T, const ApplyReturnValue &) {} + + + /* + The FunctionPointer struct is a type trait for function pointer. + - ArgumentCount is the number of argument, or -1 if it is unknown + - the Object typedef is the Object of a pointer to member function + - the Arguments typedef is the list of argument (in a QtPrivate::List) + - the Function typedef is an alias to the template parametter Func + - the call(f,o,args) method is used to call that slot + Args is the list of argument of the signal + R is the return type of the signal + f is the function pointer + o is the receiver object + and args is the array of pointer to arguments, as used in qt_metacall + + The Functor struct is the helper to call a functor of N argument. + its call function is the same as the FunctionPointer::call function. + */ +#ifndef Q_COMPILER_VARIADIC_TEMPLATES + template struct FunctionPointer { enum {ArgumentCount = -1}; }; + template struct FunctionPointer + { + typedef Obj Object; + typedef void Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (); + enum {ArgumentCount = 0}; + template + static void call(Function f, Obj *o, void **arg) { (o->*f)(), ApplyReturnValue(arg[0]); } + }; + template struct FunctionPointer + { + typedef Obj Object; + typedef List Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Arg1); + enum {ArgumentCount = 1}; + template + static void call(Function f, Obj *o, void **arg) { + (o->*f)((*reinterpret_cast::Type *>(arg[1]))), ApplyReturnValue(arg[0]); + } + }; + template struct FunctionPointer + { + typedef Obj Object; + typedef List > Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Arg1, Arg2); + enum {ArgumentCount = 2}; + template + static void call(Function f, Obj *o, void **arg) { + (o->*f)( *reinterpret_cast::Value>::Type *>(arg[1]), + *reinterpret_cast::Value>::Type *>(arg[2])), ApplyReturnValue(arg[0]); + } + }; + template struct FunctionPointer + { + typedef Obj Object; + typedef List > > Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3); + enum {ArgumentCount = 3}; + template + static void call(Function f, Obj *o, void **arg) { + (o->*f)( *reinterpret_cast::Value>::Type *>(arg[1]), + *reinterpret_cast::Value>::Type *>(arg[2]), + *reinterpret_cast::Value>::Type *>(arg[3])), ApplyReturnValue(arg[0]); + } + }; + + template struct FunctionPointer + { + typedef void Arguments; + typedef Ret (*Function) (); + typedef Ret ReturnType; + enum {ArgumentCount = 0}; + template + static void call(Function f, void *, void **arg) { f(), ApplyReturnValue(arg[0]); } + }; + template struct FunctionPointer + { + typedef List Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Arg1); + enum {ArgumentCount = 1}; + template + static void call(Function f, void *, void **arg) + { f(*reinterpret_cast::Value>::Type *>(arg[1])), ApplyReturnValue(arg[0]); } + }; + template struct FunctionPointer + { + typedef List > Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Arg1, Arg2); + enum {ArgumentCount = 2}; + template + static void call(Function f, void *, void **arg) { + f(*reinterpret_cast::Value>::Type *>(arg[1]), + *reinterpret_cast::Value>::Type *>(arg[2])), ApplyReturnValue(arg[0]); } + }; + template struct FunctionPointer + { + typedef List > > Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Arg1, Arg2, Arg3); + enum {ArgumentCount = 3}; + template + static void call(Function f, void *, void **arg) { + f( *reinterpret_cast::Value>::Type *>(arg[1]), + *reinterpret_cast::Value>::Type *>(arg[2]), + *reinterpret_cast::Value>::Type *>(arg[3])), ApplyReturnValue(arg[0]); + } + }; + + template struct Functor; + template struct Functor + { + template + static void call(Function &f, void *, void **arg) { f(), ApplyReturnValue(arg[0]); } + }; + template struct Functor + { + template + static void call(Function &f, void *, void **arg) { + f(*reinterpret_cast::Value>::Type *>(arg[1])), ApplyReturnValue(arg[0]); + } + }; + template struct Functor + { + template + static void call(Function &f, void *, void **arg) { + f( *reinterpret_cast::Value>::Type *>(arg[1]), + *reinterpret_cast::Value>::Type *>(arg[2])), ApplyReturnValue(arg[0]); + } + }; + template struct Functor + { + template + static void call(Function &f, void *, void **arg) { + f( *reinterpret_cast::Value>::Type *>(arg[1]), + *reinterpret_cast::Value>::Type *>(arg[2]), + *reinterpret_cast::Value>::Type *>(arg[4])), ApplyReturnValue(arg[0]); + } + }; +#else + template struct IndexesList {}; + template struct IndexesAppend; + template struct IndexesAppend, Right> + { typedef IndexesList Value; }; + template struct Indexes + { typedef typename IndexesAppend::Value, N - 1>::Value Value; }; + template <> struct Indexes<0> { typedef IndexesList<> Value; }; + template struct FunctionPointer { enum {ArgumentCount = -1}; }; + + template struct FunctorCall; + template + struct FunctorCall, List, R, Function> { + static void call(Function f, void **arg) { + f((*reinterpret_cast::Type *>(arg[I+1]))...), ApplyReturnValue(arg[0]); + } + }; + template + struct FunctorCall, List, R, SlotRet (Obj::*)(SlotArgs...)> { + static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) { + (o->*f)((*reinterpret_cast::Type *>(arg[I+1]))...), ApplyReturnValue(arg[0]); + } + }; + + template struct FunctionPointer + { + typedef Obj Object; + typedef List Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Args...); + enum {ArgumentCount = sizeof...(Args)}; + template + static void call(Function f, Obj *o, void **arg) { + FunctorCall::Value, SignalArgs, R, Function>::call(f, o, arg); + } + }; + + template struct FunctionPointer + { + typedef List Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Args...); + enum {ArgumentCount = sizeof...(Args)}; + template + static void call(Function f, void *, void **arg) { + FunctorCall::Value, SignalArgs, R, Function>::call(f, arg); + } + }; + + template struct Functor + { + template + static void call(Function &f, void *, void **arg) { + FunctorCall::Value, SignalArgs, R, Function>::call(f, arg); + } + }; +#endif + + /* + Logic that check if the arguments of the slot matches the argument of the signal. + To be used like this: + CheckCompatibleArguments::Arguments, FunctionPointer::Arguments>::IncompatibleSignalSlotArguments + The IncompatibleSignalSlotArguments type do not exist if the argument are incompatible and can + then produce error message. + */ + template struct CheckCompatibleArgumentsHelper {}; + template struct CheckCompatibleArgumentsHelper : T {}; + template struct AreArgumentsCompatible { + static int test(A2); + static char test(...); + static A2 dummy(); + enum { value = sizeof(test(dummy())) == sizeof(int) }; + }; + template struct AreArgumentsCompatible { enum { value = false }; }; + template struct AreArgumentsCompatible { enum { value = true }; }; + +#ifndef Q_COMPILER_VARIADIC_TEMPLATES + template struct CheckCompatibleArguments{}; + template <> struct CheckCompatibleArguments { typedef bool IncompatibleSignalSlotArguments; }; + template struct CheckCompatibleArguments { typedef bool IncompatibleSignalSlotArguments; }; + template struct CheckCompatibleArguments, List > + : CheckCompatibleArgumentsHelper, AreArgumentsCompatible< + typename RemoveConstRef::Type, typename RemoveConstRef::Type>::value > {}; +#else + template struct CheckCompatibleArguments{}; + template <> struct CheckCompatibleArguments, List<>> { typedef bool IncompatibleSignalSlotArguments; }; + template struct CheckCompatibleArguments> { typedef bool IncompatibleSignalSlotArguments; }; + template + struct CheckCompatibleArguments, List> + : CheckCompatibleArgumentsHelper, List>, AreArgumentsCompatible< + typename RemoveConstRef::Type, typename RemoveConstRef::Type>::value > {}; + +#endif + +} + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 2a9334ae8f9..72cf5a8ccab 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -114,18 +114,23 @@ public: { QObject *sender; QObject *receiver; - StaticMetaCallFunction callFunction; + union { + StaticMetaCallFunction callFunction; + QObject::QSlotObjectBase *slotObj; + }; // The next pointer for the singly-linked ConnectionList Connection *nextConnectionList; //senders linked list Connection *next; Connection **prev; - QAtomicPointer argumentTypes; + QAtomicPointer argumentTypes; QAtomicInt ref_; ushort method_offset; ushort method_relative; ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking - Connection() : nextConnectionList(0), ref_(2) { + ushort isSlotObject : 1; + ushort ownArgumentTypes : 1; + Connection() : nextConnectionList(0), ref_(2), ownArgumentTypes(true) { //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection } ~Connection(); diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 1ad24387e04..ce5a924e234 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -66,10 +66,10 @@ class QString; # define QT_NO_EMIT # else # define slots -# define signals protected +# define signals public # endif # define Q_SLOTS -# define Q_SIGNALS protected +# define Q_SIGNALS public # define Q_PRIVATE_SLOT(d, signature) # define Q_EMIT #ifndef QT_NO_EMIT diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 3d8bef52744..a00171571bc 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -121,6 +121,12 @@ private slots: void disconnectNotSignalMetaMethod(); void autoConnectionBehavior(); void baseDestroyed(); + void pointerConnect(); + void emitInDefinedOrderPointer(); + void customTypesPointer(); + void connectConvert(); + void connectWithReference(); + void connectManyArguments(); }; class SenderObject : public QObject @@ -1094,6 +1100,14 @@ void tst_QObject::emitInDefinedOrder() static int instanceCount = 0; +struct CheckInstanceCount +{ + const int saved; + CheckInstanceCount() : saved(instanceCount) {} + ~CheckInstanceCount() { QCOMPARE(saved, instanceCount); } +}; + + struct CustomType { CustomType(int l1 = 0, int l2 = 0, int l3 = 0): i1(l1), i2(l2), i3(l3) @@ -1107,6 +1121,7 @@ struct CustomType }; Q_DECLARE_METATYPE(CustomType*) +Q_DECLARE_METATYPE(CustomType) class QCustomTypeChecker: public QObject { @@ -4010,5 +4025,399 @@ void tst_QObject::baseDestroyed() // already been destroyed while ~QObject emit destroyed } +void tst_QObject::pointerConnect() +{ + SenderObject *s = new SenderObject; + ReceiverObject *r1 = new ReceiverObject; + ReceiverObject *r2 = new ReceiverObject; + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + QVERIFY( connect( s, &SenderObject::signal1 , r1, &ReceiverObject::slot1 ) ); + QVERIFY( connect( s, &SenderObject::signal1 , r2, &ReceiverObject::slot1 ) ); + QVERIFY( connect( s, &SenderObject::signal1 , r1, &ReceiverObject::slot3 ) ); + QVERIFY( connect( s, &SenderObject::signal3 , r1, &ReceiverObject::slot3 ) ); + + s->emitSignal1(); + s->emitSignal2(); + s->emitSignal3(); + s->emitSignal4(); + + QCOMPARE( r1->count_slot1, 1 ); + QCOMPARE( r1->count_slot2, 0 ); + QCOMPARE( r1->count_slot3, 2 ); + QCOMPARE( r1->count_slot4, 0 ); + QCOMPARE( r2->count_slot1, 1 ); + QCOMPARE( r2->count_slot2, 0 ); + QCOMPARE( r2->count_slot3, 0 ); + QCOMPARE( r2->count_slot4, 0 ); + QCOMPARE( r1->sequence_slot1, 1 ); + QCOMPARE( r2->sequence_slot1, 2 ); + QCOMPARE( r1->sequence_slot3, 4 ); + + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + QVERIFY( connect( s, &SenderObject::signal4, r1, &ReceiverObject::slot4 ) ); + QVERIFY( connect( s, &SenderObject::signal4, r2, &ReceiverObject::slot4 ) ); + QVERIFY( connect( s, &SenderObject::signal1, r2, &ReceiverObject::slot4 ) ); + + s->emitSignal4(); + QCOMPARE( r1->count_slot4, 1 ); + QCOMPARE( r2->count_slot4, 1 ); + QCOMPARE( r1->sequence_slot4, 1 ); + QCOMPARE( r2->sequence_slot4, 2 ); + + r1->reset(); + r2->reset(); + ReceiverObject::sequence = 0; + + connect( s, &SenderObject::signal4 , r1, &ReceiverObject::slot4 ); + + s->emitSignal4(); + QCOMPARE( r1->count_slot4, 2 ); + QCOMPARE( r2->count_slot4, 1 ); + QCOMPARE( r1->sequence_slot4, 3 ); + QCOMPARE( r2->sequence_slot4, 2 ); + + QMetaObject::Connection con; + QVERIFY(!con); + QVERIFY(!QObject::disconnect(con)); + + //connect a slot to a signal (== error) + QTest::ignoreMessage(QtWarningMsg, "QObject::connect: signal not found in ReceiverObject"); + con = connect(r1, &ReceiverObject::slot4 , s, &SenderObject::signal4 ); + QVERIFY(!con); + QVERIFY(!QObject::disconnect(con)); + + delete s; + delete r1; + delete r2; +} + +void tst_QObject::emitInDefinedOrderPointer() +{ + SenderObject sender; + ReceiverObject receiver1, receiver2, receiver3, receiver4; + + QMetaObject::Connection h0 = connect(&sender, &SenderObject::signal1, &receiver1, &SequenceObject::slot1); + QMetaObject::Connection h1 = connect(&sender, &SenderObject::signal1, &receiver2, &SequenceObject::slot1); + QVERIFY(h0); + QVERIFY(h1); + connect(&sender, &SenderObject::signal1, &receiver3, &SequenceObject::slot1); + connect(&sender, &SenderObject::signal1, &receiver4, &SequenceObject::slot1); + connect(&sender, &SenderObject::signal1, &receiver1, &SequenceObject::slot2); + connect(&sender, &SenderObject::signal1, &receiver2, &SequenceObject::slot2); + connect(&sender, &SenderObject::signal1, &receiver3, &SequenceObject::slot2); + connect(&sender, &SenderObject::signal1, &receiver4, &SequenceObject::slot2); + + int sequence; + ReceiverObject::sequence = sequence = 0; + sender.emitSignal1(); + QCOMPARE(receiver1.sequence_slot1, ++sequence); + QCOMPARE(receiver2.sequence_slot1, ++sequence); + QCOMPARE(receiver3.sequence_slot1, ++sequence); + QCOMPARE(receiver4.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot2, ++sequence); + QCOMPARE(receiver3.sequence_slot2, ++sequence); + QCOMPARE(receiver4.sequence_slot2, ++sequence); + + QObject::disconnect(h1); + h1 = connect(&sender, &SenderObject::signal1, &receiver2, &SequenceObject::slot1); + + ReceiverObject::sequence = sequence = 0; + sender.emitSignal1(); + QCOMPARE(receiver1.sequence_slot1, ++sequence); + QCOMPARE(receiver3.sequence_slot1, ++sequence); + QCOMPARE(receiver4.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot2, ++sequence); + QCOMPARE(receiver3.sequence_slot2, ++sequence); + QCOMPARE(receiver4.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot1, ++sequence); + + QObject::disconnect(h0); + h0 = connect(&sender, &SenderObject::signal1, &receiver1, &SequenceObject::slot1); + + ReceiverObject::sequence = sequence = 0; + sender.emitSignal1(); + QCOMPARE(receiver3.sequence_slot1, ++sequence); + QCOMPARE(receiver4.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot2, ++sequence); + QCOMPARE(receiver3.sequence_slot2, ++sequence); + QCOMPARE(receiver4.sequence_slot2, ++sequence); + QCOMPARE(receiver2.sequence_slot1, ++sequence); + QCOMPARE(receiver1.sequence_slot1, ++sequence); + + QVERIFY(QObject::disconnect(h0)); + QVERIFY(!QObject::disconnect(h0)); +} + + +void tst_QObject::customTypesPointer() +{ + CustomType t0; + CustomType t1(1, 2, 3); + CustomType t2(2, 3, 4); + + { + QCustomTypeChecker checker; + QCOMPARE(instanceCount, 4); + + connect(&checker, &QCustomTypeChecker::signal1, &checker, &QCustomTypeChecker::slot1, + Qt::DirectConnection); + QCOMPARE(checker.received.value(), 0); + checker.doEmit(t1); + QCOMPARE(checker.received.value(), t1.value()); + } + QCOMPARE(instanceCount, 3); +} + +class StringVariant : public QObject +{ Q_OBJECT +signals: + void stringSignal(const QString &str); +public slots: + void variantSlot(const QVariant &v) { var = v; } +public: + QVariant var; + friend class tst_QObject; +}; + +struct Functor { + QVariant *var; + void operator() (const QVariant &v) { + *var = v; + } +}; + +void tst_QObject::connectConvert() +{ + StringVariant obj; + QVERIFY(connect(&obj, &StringVariant::stringSignal, &obj, &StringVariant::variantSlot)); + QString s = QString::fromLatin1("Hello World"); + emit obj.stringSignal(s); + QCOMPARE(obj.var.toString(), s); + QVERIFY(obj.var.toString().isSharedWith(s)); + + QVariant var; + Functor f; + f.var = &var; + QVERIFY(connect(&obj, &StringVariant::stringSignal, f)); + s = QString::fromLatin1("GoodBye"); + emit obj.stringSignal(s); + QCOMPARE(obj.var.toString(), s); + QVERIFY(obj.var.toString().isSharedWith(s)); + QCOMPARE(var, obj.var); +} + +class ConnectWithReferenceObject : public QObject { + Q_OBJECT + friend class tst_QObject; +signals: + void boolRef(bool &, bool); + void stringRef(QString &, const QString &); + void boolPtr(bool *, bool); + void stringPtr(QString *, const QString &); +public slots: + void boolRefSlot(bool &b1, bool b2) { b1 = b2; } + void stringRefSlot(QString &s1, const QString &s2) { s1 = s2; } + void boolPtrSlot(bool *b1, bool b2) { *b1 = b2; } + void stringPtrSlot(QString *s1, const QString &s2) { *s1 = s2; } + + void stringSlot1(QString s) { last = s; } + void stringSlot2(const QString &s) { last = s; } + void stringSlot3(QString &s) { last = s; } +public: + QString last; +}; + +void tst_QObject::connectWithReference() +{ + ConnectWithReferenceObject o; + bool b1 = true; + QString s1 = QString::fromLatin1("str1"); + const QString s2 = QString::fromLatin1("str2"); + const QString s3 = QString::fromLatin1("str3"); + o.boolRef(b1, false); + o.stringRef(s1, s2); + QCOMPARE(b1, true); + QCOMPARE(s1, QString::fromLatin1("str1")); + o.boolPtr(&b1, false); + o.stringPtr(&s1, s2); + QCOMPARE(b1, true); + QCOMPARE(s1, QString::fromLatin1("str1")); + + QVERIFY(connect(&o, &ConnectWithReferenceObject::boolRef, &o, &ConnectWithReferenceObject::boolRefSlot)); + QVERIFY(connect(&o, &ConnectWithReferenceObject::stringRef, &o, &ConnectWithReferenceObject::stringRefSlot)); + QVERIFY(connect(&o, &ConnectWithReferenceObject::boolPtr, &o, &ConnectWithReferenceObject::boolPtrSlot)); + QVERIFY(connect(&o, &ConnectWithReferenceObject::stringPtr, &o, &ConnectWithReferenceObject::stringPtrSlot)); + o.boolRef(b1, false); + o.stringRef(s1, s2); + QCOMPARE(b1, false); + QCOMPARE(s1, QString::fromLatin1("str2")); + + o.boolPtr(&b1, true); + o.stringPtr(&s1, s3); + QCOMPARE(b1, true); + QCOMPARE(s1, QString::fromLatin1("str3")); + + { + ConnectWithReferenceObject o2; + QVERIFY(connect(&o2, &ConnectWithReferenceObject::stringRef, &o2, &ConnectWithReferenceObject::stringSlot1)); + o2.stringRef(s1, s2); + QCOMPARE(s1, s3); + QCOMPARE(o2.last, s3); + } + { + ConnectWithReferenceObject o2; + QVERIFY(connect(&o2, &ConnectWithReferenceObject::stringRef, &o2, &ConnectWithReferenceObject::stringSlot2)); + o2.stringRef(s1, s2); + QCOMPARE(s1, s3); + QCOMPARE(o2.last, s3); + } + { + ConnectWithReferenceObject o2; + QVERIFY(connect(&o2, &ConnectWithReferenceObject::stringRef, &o2, &ConnectWithReferenceObject::stringSlot3)); + o2.stringRef(s1, s2); + QCOMPARE(s1, s3); + QCOMPARE(o2.last, s3); + } +} + +class ManyArgumentObject : public QObject { + Q_OBJECT +signals: + void signal1(const QString &); + void signal2(const QString &, const QString &); + void signal3(const QString &, const QString &, const QString &); + void signal4(const QString &, const QString &, const QString &, const QString&); + void signal5(const QString &, const QString &, const QString &, const QString&, const QString&); + void signal6(const QString &, const QString &, const QString &, const QString&, const QString&, const QString&); + +public slots: +#define MANYARGUMENT_COMPARE(L) QCOMPARE(L, QString(#L)) + void slot1(const QString &a) { + MANYARGUMENT_COMPARE(a); + count++; + } + void slot2(const QString &a, const QString &b) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); + count++; + } + void slot3(const QString &a, const QString &b, const QString &c) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + count++; + } + void slot4(const QString &a, const QString &b, const QString &c, const QString&d) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + MANYARGUMENT_COMPARE(d); + count++; + } + void slot5(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); + count++; + } + void slot6(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e, const QString&f) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); MANYARGUMENT_COMPARE(f); + count++; + } +public: + int count; + +}; + +namespace ManyArgumentNamespace { + int count; + void slot1(const QString &a) { + MANYARGUMENT_COMPARE(a); + count++; + } + void slot2(const QString &a, const QString &b) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); + count++; + } + void slot3(const QString &a, const QString &b, const QString &c) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + count++; + } + void slot4(const QString &a, const QString &b, const QString &c, const QString&d) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + MANYARGUMENT_COMPARE(d); + count++; + } + void slot5(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); + count++; + } + void slot6(const QString &a, const QString &b, const QString &c, const QString&d, const QString&e, const QString&f) { + MANYARGUMENT_COMPARE(a); MANYARGUMENT_COMPARE(b); MANYARGUMENT_COMPARE(c); + MANYARGUMENT_COMPARE(d); MANYARGUMENT_COMPARE(e); MANYARGUMENT_COMPARE(f); + count++; + } +} + +void tst_QObject::connectManyArguments() +{ +#ifdef Q_COMPILER_VARIADIC_TEMPLATES + ManyArgumentObject ob; + ob.count = 0; + ManyArgumentNamespace::count = 0; + connect(&ob, &ManyArgumentObject::signal1, &ob, &ManyArgumentObject::slot1); + connect(&ob, &ManyArgumentObject::signal2, &ob, &ManyArgumentObject::slot2); + connect(&ob, &ManyArgumentObject::signal3, &ob, &ManyArgumentObject::slot3); + connect(&ob, &ManyArgumentObject::signal4, &ob, &ManyArgumentObject::slot4); + connect(&ob, &ManyArgumentObject::signal5, &ob, &ManyArgumentObject::slot5); + connect(&ob, &ManyArgumentObject::signal6, &ob, &ManyArgumentObject::slot6); + connect(&ob, &ManyArgumentObject::signal1, ManyArgumentNamespace::slot1); + connect(&ob, &ManyArgumentObject::signal2, ManyArgumentNamespace::slot2); + connect(&ob, &ManyArgumentObject::signal3, ManyArgumentNamespace::slot3); + connect(&ob, &ManyArgumentObject::signal4, ManyArgumentNamespace::slot4); + connect(&ob, &ManyArgumentObject::signal5, ManyArgumentNamespace::slot5); + connect(&ob, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot6); + + + connect(&ob, &ManyArgumentObject::signal6, &ob, &ManyArgumentObject::signal5); + connect(&ob, &ManyArgumentObject::signal5, &ob, &ManyArgumentObject::signal4); + connect(&ob, &ManyArgumentObject::signal4, &ob, &ManyArgumentObject::signal3); + connect(&ob, &ManyArgumentObject::signal3, &ob, &ManyArgumentObject::signal2); + connect(&ob, &ManyArgumentObject::signal2, &ob, &ManyArgumentObject::signal1); + + emit ob.signal6("a", "b", "c", "d", "e", "f"); + QCOMPARE(ob.count, 6); + QCOMPARE(ManyArgumentNamespace::count, 6); + + + ManyArgumentObject ob2; + ob2.count = 0; + ManyArgumentNamespace::count = 0; + connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot1); + connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot2); + connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot3); + connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot4); + connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot5); + connect(&ob2, &ManyArgumentObject::signal6, &ob2, &ManyArgumentObject::slot6); + connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot1); + connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot2); + connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot3); + connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot4); + connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot5); + connect(&ob2, &ManyArgumentObject::signal6, ManyArgumentNamespace::slot6); + + emit ob2.signal6("a", "b", "c", "d", "e", "f"); + QCOMPARE(ob2.count, 6); + QCOMPARE(ManyArgumentNamespace::count, 6); +#endif +} + + + QTEST_MAIN(tst_QObject) #include "tst_qobject.moc"