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:
parent
79f675a1e0
commit
583c55b243
@ -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.
|
||||
|
@ -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"
|
||||
|
@ -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,
|
||||
|
354
src/corelib/kernel/qobject_impl.h
Normal file
354
src/corelib/kernel/qobject_impl.h
Normal 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
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user