Merge QSlotObject and QFunctorSlotObject, and use helpers to deduplicate
Now we can use QFunctorSlotObject for any kind of callable - free function, functor, or PMF. This allows us to fold the various overloads of QObject::connect together, removing duplicate code and error handling logic. Change-Id: I8842f5ddd29e86be07a422647a8fc1678fd534b1 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
76e25bdfdf
commit
642f799fc6
@ -192,121 +192,55 @@ public:
|
|||||||
template<typename PointerToMemberFunction, typename Functor>
|
template<typename PointerToMemberFunction, typename Functor>
|
||||||
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection);
|
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection);
|
||||||
#else
|
#else
|
||||||
//Connect a signal to a pointer to qobject member function
|
//connect with context
|
||||||
template <typename Func1, typename Func2>
|
template <typename Func1, typename Func2>
|
||||||
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
|
static inline QMetaObject::Connection
|
||||||
const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
|
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
|
||||||
Qt::ConnectionType type = Qt::AutoConnection)
|
const typename QtPrivate::ContextTypeForFunctor<Func2>::ContextType *context, Func2 &&slot,
|
||||||
{
|
Qt::ConnectionType type = Qt::AutoConnection)
|
||||||
typedef QtPrivate::FunctionPointer<Func1> SignalType;
|
|
||||||
typedef QtPrivate::FunctionPointer<Func2> SlotType;
|
|
||||||
|
|
||||||
static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
|
|
||||||
"No Q_OBJECT in the class with the signal");
|
|
||||||
|
|
||||||
//compilation error if the arguments does not match.
|
|
||||||
static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
|
|
||||||
"The slot requires more arguments than the signal provides.");
|
|
||||||
static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
|
|
||||||
"Signal and slot arguments are not compatible.");
|
|
||||||
static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
|
|
||||||
"Return type of the slot is not compatible with the return type of the signal.");
|
|
||||||
|
|
||||||
const int *types = nullptr;
|
|
||||||
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
|
|
||||||
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
|
|
||||||
|
|
||||||
return connectImpl(sender, reinterpret_cast<void **>(&signal),
|
|
||||||
receiver, reinterpret_cast<void **>(&slot),
|
|
||||||
new QtPrivate::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 std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0, QMetaObject::Connection>::type
|
|
||||||
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 &&slot)
|
|
||||||
{
|
|
||||||
return connect(sender, signal, sender, std::forward<Func2>(slot), Qt::DirectConnection);
|
|
||||||
}
|
|
||||||
|
|
||||||
//connect to a function pointer (not a member)
|
|
||||||
template <typename Func1, typename Func2>
|
|
||||||
static inline typename std::enable_if<int(QtPrivate::FunctionPointer<std::decay_t<Func2>>::ArgumentCount) >= 0 &&
|
|
||||||
!QtPrivate::FunctionPointer<std::decay_t<Func2>>::IsPointerToMemberFunction, QMetaObject::Connection>::type
|
|
||||||
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 &&slot,
|
|
||||||
Qt::ConnectionType type = Qt::AutoConnection)
|
|
||||||
{
|
{
|
||||||
typedef QtPrivate::FunctionPointer<Func1> SignalType;
|
typedef QtPrivate::FunctionPointer<Func1> SignalType;
|
||||||
typedef QtPrivate::FunctionPointer<std::decay_t<Func2>> SlotType;
|
typedef QtPrivate::FunctionPointer<std::decay_t<Func2>> SlotType;
|
||||||
|
|
||||||
|
if constexpr (SlotType::ArgumentCount != -1) {
|
||||||
|
static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
|
||||||
|
"Return type of the slot is not compatible with the return type of the signal.");
|
||||||
|
} else {
|
||||||
|
constexpr int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<std::decay_t<Func2>, typename SignalType::Arguments>::Value;
|
||||||
|
constexpr int SlotArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
|
||||||
|
typedef typename QtPrivate::FunctorReturnType<std::decay_t<Func2>, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value>::Value SlotReturnType;
|
||||||
|
|
||||||
|
static_assert((QtPrivate::AreArgumentsCompatible<SlotReturnType, typename SignalType::ReturnType>::value),
|
||||||
|
"Return type of the slot is not compatible with the return type of the signal.");
|
||||||
|
}
|
||||||
|
|
||||||
static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
|
static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
|
||||||
"No Q_OBJECT in the class with the signal");
|
"No Q_OBJECT in the class with the signal");
|
||||||
|
|
||||||
//compilation error if the arguments does not match.
|
//compilation error if the arguments does not match.
|
||||||
static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
|
static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
|
||||||
"The slot requires more arguments than the signal provides.");
|
"The slot requires more arguments than the signal provides.");
|
||||||
static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
|
|
||||||
"Signal and slot arguments are not compatible.");
|
|
||||||
static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
|
|
||||||
"Return type of the slot is not compatible with the return type of the signal.");
|
|
||||||
|
|
||||||
const int *types = nullptr;
|
const int *types = nullptr;
|
||||||
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
|
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
|
||||||
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
|
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
|
||||||
|
|
||||||
return connectImpl(sender, reinterpret_cast<void **>(&signal), context, nullptr,
|
void **pSlot = nullptr;
|
||||||
new QtPrivate::QFunctorSlotObject<Func2,
|
if constexpr (std::is_member_function_pointer_v<std::decay_t<Func2>>)
|
||||||
typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
|
pSlot = const_cast<void **>(reinterpret_cast<void *const *>(&slot));
|
||||||
typename SignalType::ReturnType>(std::forward<Func2>(slot)),
|
|
||||||
|
return connectImpl(sender, reinterpret_cast<void **>(&signal), context, pSlot,
|
||||||
|
QtPrivate::makeSlotObject<Func1>(std::forward<Func2>(slot)),
|
||||||
type, types, &SignalType::Object::staticMetaObject);
|
type, types, &SignalType::Object::staticMetaObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
//connect to a functor
|
//connect without context
|
||||||
template <typename Func1, typename Func2>
|
template <typename Func1, typename Func2>
|
||||||
static inline typename std::enable_if<
|
static inline QMetaObject::Connection
|
||||||
QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1 &&
|
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 &&slot)
|
||||||
!std::is_convertible_v<Func2, const char*>, // don't match old-style connect
|
|
||||||
QMetaObject::Connection>::type
|
|
||||||
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 &&slot)
|
|
||||||
{
|
{
|
||||||
return connect(sender, signal, sender, std::forward<Func2>(slot), Qt::DirectConnection);
|
return connect(sender, signal, sender, std::forward<Func2>(slot), Qt::DirectConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
//connect to a functor, with a "context" object defining in which event loop is going to be executed
|
|
||||||
template <typename Func1, typename Func2>
|
|
||||||
static inline typename std::enable_if<
|
|
||||||
QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1 &&
|
|
||||||
!std::is_convertible_v<Func2, const char*>, // don't match old-style connect
|
|
||||||
QMetaObject::Connection>::type
|
|
||||||
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
|
|
||||||
Qt::ConnectionType type = Qt::AutoConnection)
|
|
||||||
{
|
|
||||||
typedef QtPrivate::FunctionPointer<Func1> SignalType;
|
|
||||||
const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Func2 , typename SignalType::Arguments>::Value;
|
|
||||||
|
|
||||||
static_assert((FunctorArgumentCount >= 0),
|
|
||||||
"Signal and slot arguments are not compatible.");
|
|
||||||
const int SlotArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
|
|
||||||
typedef typename QtPrivate::FunctorReturnType<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value>::Value SlotReturnType;
|
|
||||||
|
|
||||||
static_assert((QtPrivate::AreArgumentsCompatible<SlotReturnType, typename SignalType::ReturnType>::value),
|
|
||||||
"Return type of the slot is not compatible with the return type of the signal.");
|
|
||||||
|
|
||||||
static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
|
|
||||||
"No Q_OBJECT in the class with the signal");
|
|
||||||
|
|
||||||
const int *types = nullptr;
|
|
||||||
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
|
|
||||||
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
|
|
||||||
|
|
||||||
return connectImpl(sender, reinterpret_cast<void **>(&signal), context, nullptr,
|
|
||||||
new QtPrivate::QFunctorSlotObject<Func2,
|
|
||||||
typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value,
|
|
||||||
typename SignalType::ReturnType>(std::move(slot)),
|
|
||||||
type, types, &SignalType::Object::staticMetaObject);
|
|
||||||
}
|
|
||||||
#endif //Q_QDOC
|
#endif //Q_QDOC
|
||||||
|
|
||||||
static bool disconnect(const QObject *sender, const char *signal,
|
static bool disconnect(const QObject *sender, const char *signal,
|
||||||
|
@ -401,35 +401,14 @@ namespace QtPrivate {
|
|||||||
Q_DISABLE_COPY_MOVE(QSlotObjectBase)
|
Q_DISABLE_COPY_MOVE(QSlotObjectBase)
|
||||||
};
|
};
|
||||||
|
|
||||||
// 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 return type of the signal to which the slot is connected.
|
|
||||||
template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase
|
|
||||||
{
|
|
||||||
typedef QtPrivate::FunctionPointer<Func> FuncType;
|
|
||||||
Func function;
|
|
||||||
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
|
|
||||||
{
|
|
||||||
switch (which) {
|
|
||||||
case Destroy:
|
|
||||||
delete static_cast<QSlotObject*>(this_);
|
|
||||||
break;
|
|
||||||
case Call:
|
|
||||||
FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
|
|
||||||
break;
|
|
||||||
case Compare:
|
|
||||||
*ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
|
|
||||||
break;
|
|
||||||
case NumOperations: ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
|
|
||||||
};
|
|
||||||
// implementation of QSlotObjectBase for which the slot is a functor (or lambda)
|
// implementation of QSlotObjectBase for which the slot is a functor (or lambda)
|
||||||
// Args and R are the List of arguments and the return type of the signal to which the slot is connected.
|
// Args and R are the List of arguments and the return type of the signal to which the slot is connected.
|
||||||
template<typename Func, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase
|
template<typename Func, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase
|
||||||
{
|
{
|
||||||
using FuncType = QtPrivate::Functor<Func, Args::size>;
|
using FuncType = std::conditional_t<std::is_member_function_pointer_v<std::decay_t<Func>>,
|
||||||
|
QtPrivate::FunctionPointer<std::decay_t<Func>>,
|
||||||
|
QtPrivate::Functor<Func, Args::size>
|
||||||
|
>;
|
||||||
Func function;
|
Func function;
|
||||||
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
|
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
|
||||||
{
|
{
|
||||||
@ -438,9 +417,18 @@ namespace QtPrivate {
|
|||||||
delete static_cast<QFunctorSlotObject*>(this_);
|
delete static_cast<QFunctorSlotObject*>(this_);
|
||||||
break;
|
break;
|
||||||
case Call:
|
case Call:
|
||||||
FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
|
if constexpr (std::is_member_function_pointer_v<std::decay_t<Func>>)
|
||||||
|
FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
|
||||||
|
else
|
||||||
|
FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
|
||||||
break;
|
break;
|
||||||
case Compare: // not implemented
|
case Compare:
|
||||||
|
if constexpr (std::is_member_function_pointer_v<std::decay_t<Func>>) {
|
||||||
|
*ret = *reinterpret_cast<std::decay_t<Func> *>(a) == static_cast<QFunctorSlotObject*>(this_)->function;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// not implemented otherwise
|
||||||
|
Q_FALLTHROUGH();
|
||||||
case NumOperations:
|
case NumOperations:
|
||||||
Q_UNUSED(ret);
|
Q_UNUSED(ret);
|
||||||
}
|
}
|
||||||
@ -451,7 +439,7 @@ namespace QtPrivate {
|
|||||||
|
|
||||||
// typedefs for readability for when there are no parameters
|
// typedefs for readability for when there are no parameters
|
||||||
template <typename Func>
|
template <typename Func>
|
||||||
using QSlotObjectWithNoArgs = QSlotObject<Func,
|
using QSlotObjectWithNoArgs = QFunctorSlotObject<Func,
|
||||||
QtPrivate::List<>,
|
QtPrivate::List<>,
|
||||||
typename QtPrivate::FunctionPointer<Func>::ReturnType>;
|
typename QtPrivate::FunctionPointer<Func>::ReturnType>;
|
||||||
|
|
||||||
@ -506,19 +494,15 @@ namespace QtPrivate {
|
|||||||
using ExpectedSignature = QtPrivate::FunctionPointer<Prototype>;
|
using ExpectedSignature = QtPrivate::FunctionPointer<Prototype>;
|
||||||
using ExpectedReturnType = typename ExpectedSignature::ReturnType;
|
using ExpectedReturnType = typename ExpectedSignature::ReturnType;
|
||||||
using ExpectedArguments = typename ExpectedSignature::Arguments;
|
using ExpectedArguments = typename ExpectedSignature::Arguments;
|
||||||
|
|
||||||
using ActualSignature = QtPrivate::FunctionPointer<Functor>;
|
using ActualSignature = QtPrivate::FunctionPointer<Functor>;
|
||||||
|
constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments<Prototype, Functor>();
|
||||||
|
using ActualArguments = typename QtPrivate::List_Left<ExpectedArguments, MatchingArgumentCount>::Value;
|
||||||
|
|
||||||
static_assert(int(ActualSignature::ArgumentCount) <= int(ExpectedSignature::ArgumentCount),
|
static_assert(int(ActualSignature::ArgumentCount) <= int(ExpectedSignature::ArgumentCount),
|
||||||
"Functor requires more arguments than what can be provided.");
|
"Functor requires more arguments than what can be provided.");
|
||||||
|
|
||||||
if constexpr (QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction) {
|
return new QtPrivate::QFunctorSlotObject<Functor, ActualArguments, ExpectedReturnType>(std::forward<Functor>(func));
|
||||||
using ActualArguments = typename ActualSignature::Arguments;
|
|
||||||
return new QtPrivate::QSlotObject<Functor, ActualArguments, ExpectedReturnType>(std::move(func));
|
|
||||||
} else {
|
|
||||||
constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments<Prototype, Functor>();
|
|
||||||
using ActualArguments = typename QtPrivate::List_Left<ExpectedArguments, MatchingArgumentCount>::Value;
|
|
||||||
return new QtPrivate::QFunctorSlotObject<Functor, ActualArguments, ExpectedReturnType>(std::move(func));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Prototype, typename Functor, typename = void>
|
template<typename Prototype, typename Functor, typename = void>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user