diff options
-rw-r--r-- | doc/src/snippets/code/src_corelib_kernel_qobject.cpp | 23 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 164 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.h | 101 | ||||
-rw-r--r-- | src/corelib/kernel/qobject_impl.h | 354 | ||||
-rw-r--r-- | src/corelib/kernel/qobject_p.h | 11 | ||||
-rw-r--r-- | src/corelib/kernel/qobjectdefs.h | 4 | ||||
-rw-r--r-- | tests/auto/corelib/kernel/qobject/tst_qobject.cpp | 409 |
7 files changed, 1056 insertions, 10 deletions
diff --git a/doc/src/snippets/code/src_corelib_kernel_qobject.cpp b/doc/src/snippets/code/src_corelib_kernel_qobject.cpp index 086c62893d..11b70cc1ab 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<QListWidget *>(QString(), Qt::FindDi QList<QPushButton *> childButtons = parentWidget.findChildren<QPushButton *>(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 59282d3464..e8735d0c09 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<QObject::QSlotObjectBase> 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) @@ -4043,6 +4053,146 @@ void qDeleteInEventHandler(QObject *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<QObject *>(sender); + QObject *r = const_cast<QObject *>(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<Qt::ConnectionType>(type & (Qt::UniqueConnection - 1)); + } + + QScopedPointer<QObjectPrivate::Connection> 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. If the \a connection is invalid or has already been disconnected, do nothing @@ -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 038b59042b..93448b5ad1 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -52,6 +52,9 @@ #include <QtCore/qcoreevent.h> #endif #include <QtCore/qscopedpointer.h> +#include <QtCore/qmetatype.h> + +#include <QtCore/qobject_impl.h> 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 <typename Func1, typename Func2> + static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, + const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot, + Qt::ConnectionType type = Qt::AutoConnection) + { + typedef QtPrivate::FunctionPointer<Func1> SignalType; + typedef QtPrivate::FunctionPointer<Func2> SlotType; + reinterpret_cast<typename SignalType::Object *>(0)->qt_check_for_QOBJECT_macro(*reinterpret_cast<typename SignalType::Object *>(0)); + + //compilation error if the arguments does not match. + typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments; + + const int *types = 0; + return connectImpl(sender, reinterpret_cast<void **>(&signal), + receiver, new QSlotObject<Func2, + typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value, + typename SignalType::ReturnType>(slot), + type, types, &SignalType::Object::staticMetaObject); + } + + //connect to a function pointer (not a member) + template <typename Func1, typename Func2> + static inline typename QtPrivate::QEnableIf<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0, QMetaObject::Connection>::Type + connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot) + { + typedef QtPrivate::FunctionPointer<Func1> SignalType; + typedef QtPrivate::FunctionPointer<Func2> SlotType; + + //compilation error if the arguments does not match. + typedef typename QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::IncompatibleSignalSlotArguments EnsureCompatibleArguments; + typedef typename QtPrivate::QEnableIf<(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount))>::Type EnsureArgumentsCount; + + return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, + new QStaticSlotObject<Func2, + typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value, + typename SignalType::ReturnType>(slot), + Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject); + } + + //connect to a functor + template <typename Func1, typename Func2> + static inline typename QtPrivate::QEnableIf<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::Type + connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot) + { + typedef QtPrivate::FunctionPointer<Func1> SignalType; + + return connectImpl(sender, reinterpret_cast<void **>(&signal), + sender, new QFunctorSlotObject<Func2, SignalType::ArgumentCount, typename SignalType::Arguments, typename SignalType::ReturnType>(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<typename Func, typename Args, typename R> struct QSlotObject : QSlotObjectBase + { + typedef QtPrivate::FunctionPointer<Func> FuncType; + Func function; + QSlotObject(Func f) : function(f) {}; + virtual void call(QObject *receiver, void **a) { + FuncType::template call<Args, R>(function, static_cast<typename FuncType::Object *>(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<typename Func, typename Args, typename R> struct QStaticSlotObject : QSlotObjectBase + { + typedef QtPrivate::FunctionPointer<Func> FuncType; + Func function; + QStaticSlotObject(Func f) : function(f) {} + virtual void call(QObject *receiver, void **a) { + FuncType::template call<Args, R>(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<typename Func, int N, typename Args, typename R> struct QFunctorSlotObject : QSlotObjectBase + { + typedef QtPrivate::Functor<Func, N> FuncType; + Func function; + QFunctorSlotObject(const Func &f) : function(f) {} + virtual void call(QObject *receiver, void **a) { + FuncType::template call<Args, R>(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 0000000000..7d3dc1659b --- /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 <typename T> struct RemoveRef { typedef T Type; }; + template <typename T> struct RemoveRef<const T&> { typedef T Type; }; + template <typename T> struct RemoveRef<T&> { typedef T Type; }; + template <typename T> struct RemoveConstRef { typedef T Type; }; + template <typename T> struct RemoveConstRef<const T&> { 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<L,N> 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 <typename Head, typename Tail> struct List { typedef Head Car; typedef Tail Cdr; }; + template <typename L, int N> struct List_Left { typedef List<typename L::Car, typename List_Left<typename L::Cdr, N - 1>::Value > Value; }; + template <typename L> struct List_Left<L,0> { typedef void Value; }; +#else + // With variadic template, lists are represented using a variadic template argument instead of the lisp way + template <typename...> struct List {}; + template <typename Head, typename... Tail> struct List<Head, Tail...> { typedef Head Car; typedef List<Tail...> Cdr; }; + template <typename, typename> struct List_Append; + template <typename... L1, typename...L2> struct List_Append<List<L1...>, List<L2...>> { typedef List<L1..., L2...> Value; }; + template <typename L, int N> struct List_Left { + typedef typename List_Append<List<typename L::Car>,typename List_Left<typename L::Cdr, N - 1>::Value>::Value Value; + }; + template <typename L> struct List_Left<L, 0> { typedef List<> Value; }; +#endif + // List_Select<L,N> returns (via typedef Value) the Nth element of the list L + template <typename L, int N> struct List_Select { typedef typename List_Select<typename L::Cdr, N - 1>::Value Value; }; + template <typename L> struct List_Select<L,0> { 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<ReturnType>(&return_value) + if function() returns a value, the operator,(T, ApplyReturnValue<ReturnType>) is called, but if it + returns void, the builtin one is used without an error. + */ + template <typename T> + struct ApplyReturnValue { + void *data; + ApplyReturnValue(void *data) : data(data) {} + }; + template<typename T, typename U> + void operator,(const T &value, const ApplyReturnValue<U> &container) { + *reinterpret_cast<U*>(container.data) = value; + } +#ifdef Q_COMPILER_RVALUE_REFS + template<typename T, typename U> + void operator,(T &&value, const ApplyReturnValue<U> &container) { + *reinterpret_cast<U*>(container.data) = value; + } +#endif + template<typename T> + void operator,(T, const ApplyReturnValue<void> &) {} + + + /* + The FunctionPointer<Func> 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<Args, R>(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<Func,N> 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<typename Func> struct FunctionPointer { enum {ArgumentCount = -1}; }; + template<class Obj, typename Ret> struct FunctionPointer<Ret (Obj::*) ()> + { + typedef Obj Object; + typedef void Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (); + enum {ArgumentCount = 0}; + template <typename Args, typename R> + static void call(Function f, Obj *o, void **arg) { (o->*f)(), ApplyReturnValue<R>(arg[0]); } + }; + template<class Obj, typename Ret, typename Arg1> struct FunctionPointer<Ret (Obj::*) (Arg1)> + { + typedef Obj Object; + typedef List<Arg1, void> Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Arg1); + enum {ArgumentCount = 1}; + template <typename Args, typename R> + static void call(Function f, Obj *o, void **arg) { + (o->*f)((*reinterpret_cast<typename RemoveRef<typename Args::Car>::Type *>(arg[1]))), ApplyReturnValue<R>(arg[0]); + } + }; + template<class Obj, typename Ret, typename Arg1, typename Arg2> struct FunctionPointer<Ret (Obj::*) (Arg1, Arg2)> + { + typedef Obj Object; + typedef List<Arg1, List<Arg2, void> > Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Arg1, Arg2); + enum {ArgumentCount = 2}; + template <typename Args, typename R> + static void call(Function f, Obj *o, void **arg) { + (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 1>::Value>::Type *>(arg[2])), ApplyReturnValue<R>(arg[0]); + } + }; + template<class Obj, typename Ret, typename Arg1, typename Arg2, typename Arg3> struct FunctionPointer<Ret (Obj::*) (Arg1, Arg2, Arg3)> + { + typedef Obj Object; + typedef List<Arg1, List<Arg2, List<Arg3, void> > > Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Arg1, Arg2, Arg3); + enum {ArgumentCount = 3}; + template <typename Args, typename R> + static void call(Function f, Obj *o, void **arg) { + (o->*f)( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 1>::Value>::Type *>(arg[2]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 2>::Value>::Type *>(arg[3])), ApplyReturnValue<R>(arg[0]); + } + }; + + template<typename Ret> struct FunctionPointer<Ret (*) ()> + { + typedef void Arguments; + typedef Ret (*Function) (); + typedef Ret ReturnType; + enum {ArgumentCount = 0}; + template <typename Args, typename R> + static void call(Function f, void *, void **arg) { f(), ApplyReturnValue<R>(arg[0]); } + }; + template<typename Ret, typename Arg1> struct FunctionPointer<Ret (*) (Arg1)> + { + typedef List<Arg1, void> Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Arg1); + enum {ArgumentCount = 1}; + template <typename Args, typename R> + static void call(Function f, void *, void **arg) + { f(*reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1])), ApplyReturnValue<R>(arg[0]); } + }; + template<typename Ret, typename Arg1, typename Arg2> struct FunctionPointer<Ret (*) (Arg1, Arg2)> + { + typedef List<Arg1, List<Arg2, void> > Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Arg1, Arg2); + enum {ArgumentCount = 2}; + template <typename Args, typename R> + static void call(Function f, void *, void **arg) { + f(*reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 1>::Value>::Type *>(arg[2])), ApplyReturnValue<R>(arg[0]); } + }; + template<typename Ret, typename Arg1, typename Arg2, typename Arg3> struct FunctionPointer<Ret (*) (Arg1, Arg2, Arg3)> + { + typedef List<Arg1, List<Arg2, List<Arg3, void> > > Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Arg1, Arg2, Arg3); + enum {ArgumentCount = 3}; + template <typename Args, typename R> + static void call(Function f, void *, void **arg) { + f( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 1>::Value>::Type *>(arg[2]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 2>::Value>::Type *>(arg[3])), ApplyReturnValue<R>(arg[0]); + } + }; + + template<typename F, int N> struct Functor; + template<typename Function> struct Functor<Function, 0> + { + template <typename Args, typename R> + static void call(Function &f, void *, void **arg) { f(), ApplyReturnValue<R>(arg[0]); } + }; + template<typename Function> struct Functor<Function, 1> + { + template <typename Args, typename R> + static void call(Function &f, void *, void **arg) { + f(*reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1])), ApplyReturnValue<R>(arg[0]); + } + }; + template<typename Function> struct Functor<Function, 2> + { + template <typename Args, typename R> + static void call(Function &f, void *, void **arg) { + f( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 1>::Value>::Type *>(arg[2])), ApplyReturnValue<R>(arg[0]); + } + }; + template<typename Function> struct Functor<Function, 3> + { + template <typename Args, typename R> + static void call(Function &f, void *, void **arg) { + f( *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 0>::Value>::Type *>(arg[1]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 1>::Value>::Type *>(arg[2]), + *reinterpret_cast<typename RemoveRef<typename List_Select<Args, 2>::Value>::Type *>(arg[4])), ApplyReturnValue<R>(arg[0]); + } + }; +#else + template <int...> struct IndexesList {}; + template <typename IndexList, int Right> struct IndexesAppend; + template <int... Left, int Right> struct IndexesAppend<IndexesList<Left...>, Right> + { typedef IndexesList<Left..., Right> Value; }; + template <int N> struct Indexes + { typedef typename IndexesAppend<typename Indexes<N - 1>::Value, N - 1>::Value Value; }; + template <> struct Indexes<0> { typedef IndexesList<> Value; }; + template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1}; }; + + template <typename, typename, typename, typename> struct FunctorCall; + template <int... I, typename... SignalArgs, typename R, typename Function> + struct FunctorCall<IndexesList<I...>, List<SignalArgs...>, R, Function> { + static void call(Function f, void **arg) { + f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[I+1]))...), ApplyReturnValue<R>(arg[0]); + } + }; + template <int... I, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj> + struct FunctorCall<IndexesList<I...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> { + static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) { + (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[I+1]))...), ApplyReturnValue<R>(arg[0]); + } + }; + + template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)> + { + typedef Obj Object; + typedef List<Args...> Arguments; + typedef Ret ReturnType; + typedef Ret (Obj::*Function) (Args...); + enum {ArgumentCount = sizeof...(Args)}; + template <typename SignalArgs, typename R> + static void call(Function f, Obj *o, void **arg) { + FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, o, arg); + } + }; + + template<typename Ret, typename... Args> struct FunctionPointer<Ret (*) (Args...)> + { + typedef List<Args...> Arguments; + typedef Ret ReturnType; + typedef Ret (*Function) (Args...); + enum {ArgumentCount = sizeof...(Args)}; + template <typename SignalArgs, typename R> + static void call(Function f, void *, void **arg) { + FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Function>::call(f, arg); + } + }; + + template<typename Function, int N> struct Functor + { + template <typename SignalArgs, typename R> + static void call(Function &f, void *, void **arg) { + FunctorCall<typename Indexes<N>::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<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::IncompatibleSignalSlotArguments + The IncompatibleSignalSlotArguments type do not exist if the argument are incompatible and can + then produce error message. + */ + template<typename T, bool B> struct CheckCompatibleArgumentsHelper {}; + template<typename T> struct CheckCompatibleArgumentsHelper<T, true> : T {}; + template<typename A1, typename A2> struct AreArgumentsCompatible { + static int test(A2); + static char test(...); + static A2 dummy(); + enum { value = sizeof(test(dummy())) == sizeof(int) }; + }; + template<typename A1, typename A2> struct AreArgumentsCompatible<A1, A2&> { enum { value = false }; }; + template<typename A> struct AreArgumentsCompatible<A&, A&> { enum { value = true }; }; + +#ifndef Q_COMPILER_VARIADIC_TEMPLATES + template <typename List1, typename List2> struct CheckCompatibleArguments{}; + template <> struct CheckCompatibleArguments<void, void> { typedef bool IncompatibleSignalSlotArguments; }; + template <typename List1> struct CheckCompatibleArguments<List1, void> { typedef bool IncompatibleSignalSlotArguments; }; + template <typename Arg1, typename Arg2, typename Tail1, typename Tail2> struct CheckCompatibleArguments<List<Arg1, Tail1>, List<Arg2, Tail2> > + : CheckCompatibleArgumentsHelper<CheckCompatibleArguments<Tail1, Tail2>, AreArgumentsCompatible< + typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value > {}; +#else + template <typename List1, typename List2> struct CheckCompatibleArguments{}; + template <> struct CheckCompatibleArguments<List<>, List<>> { typedef bool IncompatibleSignalSlotArguments; }; + template <typename List1> struct CheckCompatibleArguments<List1, List<>> { typedef bool IncompatibleSignalSlotArguments; }; + template <typename Arg1, typename Arg2, typename... Tail1, typename... Tail2> + struct CheckCompatibleArguments<List<Arg1, Tail1...>, List<Arg2, Tail2...>> + : CheckCompatibleArgumentsHelper<CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>, AreArgumentsCompatible< + typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::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 2a9334ae8f..72cf5a8cca 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<int> argumentTypes; + QAtomicPointer<const int> 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 1ad24387e0..ce5a924e23 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 3d8bef5274..a00171571b 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" |