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 <thiago.macieira@intel.com>
This commit is contained in:
Olivier Goffart 2011-11-23 15:06:30 +01:00 committed by Qt by Nokia
parent 79f675a1e0
commit 583c55b243
7 changed files with 1056 additions and 10 deletions

View File

@ -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.

View File

@ -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)
@ -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<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.
@ -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"

View File

@ -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,

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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"