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:
Volker Hilsheimer 2023-04-30 01:44:44 +02:00
parent 76e25bdfdf
commit 642f799fc6
2 changed files with 46 additions and 128 deletions

View File

@ -192,121 +192,55 @@ public:
template<typename PointerToMemberFunction, typename Functor>
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection);
#else
//Connect a signal to a pointer to qobject member function
//connect with context
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;
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)
static inline QMetaObject::Connection
connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
const typename QtPrivate::ContextTypeForFunctor<Func2>::ContextType *context, Func2 &&slot,
Qt::ConnectionType type = Qt::AutoConnection)
{
typedef QtPrivate::FunctionPointer<Func1> SignalType;
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,
"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), context, nullptr,
new QtPrivate::QFunctorSlotObject<Func2,
typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
typename SignalType::ReturnType>(std::forward<Func2>(slot)),
void **pSlot = nullptr;
if constexpr (std::is_member_function_pointer_v<std::decay_t<Func2>>)
pSlot = const_cast<void **>(reinterpret_cast<void *const *>(&slot));
return connectImpl(sender, reinterpret_cast<void **>(&signal), context, pSlot,
QtPrivate::makeSlotObject<Func1>(std::forward<Func2>(slot)),
type, types, &SignalType::Object::staticMetaObject);
}
//connect to a functor
//connect without context
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, Func2 &&slot)
static inline QMetaObject::Connection
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 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
static bool disconnect(const QObject *sender, const char *signal,

View File

@ -401,35 +401,14 @@ namespace QtPrivate {
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)
// 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
{
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;
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
{
@ -438,9 +417,18 @@ namespace QtPrivate {
delete static_cast<QFunctorSlotObject*>(this_);
break;
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;
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:
Q_UNUSED(ret);
}
@ -451,7 +439,7 @@ namespace QtPrivate {
// typedefs for readability for when there are no parameters
template <typename Func>
using QSlotObjectWithNoArgs = QSlotObject<Func,
using QSlotObjectWithNoArgs = QFunctorSlotObject<Func,
QtPrivate::List<>,
typename QtPrivate::FunctionPointer<Func>::ReturnType>;
@ -506,19 +494,15 @@ namespace QtPrivate {
using ExpectedSignature = QtPrivate::FunctionPointer<Prototype>;
using ExpectedReturnType = typename ExpectedSignature::ReturnType;
using ExpectedArguments = typename ExpectedSignature::Arguments;
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),
"Functor requires more arguments than what can be provided.");
if constexpr (QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction) {
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));
}
return new QtPrivate::QFunctorSlotObject<Functor, ActualArguments, ExpectedReturnType>(std::forward<Functor>(func));
}
template<typename Prototype, typename Functor, typename = void>