summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOlivier Goffart <ogoffart@woboq.com>2013-03-01 10:18:36 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-19 23:16:38 +0100
commitbc98bba2f302922761209c0e91485e809354abc1 (patch)
tree82854fc70dd695c27abe9bbe8c61d07e9b8f314d
parente66159cfc7f78b8ad617175bbef6b70edbf9da6b (diff)
Support connection to functor with multiple operator()
When variadic templates and decltype are supported, detect the best overload of operator() to call. Currently, the code takes the type of the operator(), which requires that the functor only has one, and that it has no template parameter. This feature is required if we want to connect to c++1y generic lambda (N3418) Change-Id: Ifa957da6955ea39ab804b58f320da9f98ff47d63 Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@digia.com>
-rw-r--r--src/corelib/kernel/qobject.h34
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h36
-rw-r--r--tests/auto/corelib/kernel/qobject/tst_qobject.cpp126
3 files changed, 190 insertions, 6 deletions
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index 7beaa32855..aaa09fac50 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -265,27 +266,50 @@ public:
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)
{
+#if defined (Q_COMPILER_DECLTYPE) && defined (Q_COMPILER_VARIADIC_TEMPLATES)
+ typedef QtPrivate::FunctionPointer<Func1> SignalType;
+ const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Func2 , typename SignalType::Arguments>::Value;
+
+ Q_STATIC_ASSERT_X((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;
+#else
+ // Without variadic template, we don't detect the best overload of operator(). We just
+ // assume there is only one simple operator() and connect to &Func2::operator()
+
+ /* If you get an error such as:
+ couldn't deduce template parameter 'Func2Operator'
+ or
+ cannot resolve address of overloaded function
+ It means the functor does not have a single operator().
+ Functors with overloaded or templated operator() are only supported if the compiler supports
+ C++11 variadic templates
+ */
#ifndef Q_COMPILER_DECLTYPE //Workaround the lack of decltype using another function as indirection
return connect_functor(sender, signal, slot, &Func2::operator()); }
template <typename Func1, typename Func2, typename Func2Operator>
static inline QMetaObject::Connection connect_functor(const QObject *sender, Func1 signal, Func2 slot, Func2Operator) {
typedef QtPrivate::FunctionPointer<Func2Operator> SlotType ;
#else
-
typedef QtPrivate::FunctionPointer<decltype(&Func2::operator())> SlotType ;
#endif
typedef QtPrivate::FunctionPointer<Func1> SignalType;
+ typedef typename SlotType::ReturnType SlotReturnType;
+ const int SlotArgumentCount = SlotType::ArgumentCount;
- Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
+ Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= SlotArgumentCount,
"The slot requires more arguments than the signal provides.");
Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
"Signal and slot arguments are not compatible.");
- Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
+#endif
+
+ Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<SlotReturnType, typename SignalType::ReturnType>::value),
"Return type of the slot is not compatible with the return type of the signal.");
return connectImpl(sender, reinterpret_cast<void **>(&signal), sender, 0,
- new QtPrivate::QFunctorSlotObject<Func2, SlotType::ArgumentCount,
- typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
+ new QtPrivate::QFunctorSlotObject<Func2, SlotArgumentCount,
+ typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value,
typename SignalType::ReturnType>(slot),
Qt::DirectConnection, 0, &SignalType::Object::staticMetaObject);
}
diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h
index 46a6eab253..4f44d9204e 100644
--- a/src/corelib/kernel/qobjectdefs_impl.h
+++ b/src/corelib/kernel/qobjectdefs_impl.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -597,10 +598,43 @@ namespace QtPrivate {
enum { value = AreArgumentsCompatible<typename RemoveConstRef<Arg1>::Type, typename RemoveConstRef<Arg2>::Type>::value
&& CheckCompatibleArguments<List<Tail1...>, List<Tail2...>>::value };
};
+#endif
+
+#if defined(Q_COMPILER_DECLTYPE) && defined(Q_COMPILER_VARIADIC_TEMPLATES)
+ /*
+ Find the maximum number of arguments a functor object can take and be still compatible with
+ the arguments from the signal.
+ Value is the number of arguments, or -1 if nothing matches.
+ */
+ template <typename Functor, typename ArgList> struct ComputeFunctorArgumentCount;
+
+ template <typename Functor, typename ArgList, bool Done> struct ComputeFunctorArgumentCountHelper
+ { enum { Value = -1 }; };
+ template <typename Functor, typename First, typename... ArgList>
+ struct ComputeFunctorArgumentCountHelper<Functor, List<First, ArgList...>, false>
+ : ComputeFunctorArgumentCount<Functor,
+ typename List_Left<List<First, ArgList...>, sizeof...(ArgList)>::Value> {};
+
+ template <typename Functor, typename... ArgList> struct ComputeFunctorArgumentCount<Functor, List<ArgList...>>
+ {
+ template <typename D> static D dummy();
+ template <typename F> static auto test(F f) -> decltype(((f.operator()((dummy<ArgList>())...)), int()));
+ static char test(...);
+ enum {
+ Ok = sizeof(test(dummy<Functor>())) == sizeof(int),
+ Value = Ok ? sizeof...(ArgList) : int(ComputeFunctorArgumentCountHelper<Functor, List<ArgList...>, Ok>::Value)
+ };
+ };
+ /* get the return type of a functor, given the signal argument list */
+ template <typename Functor, typename ArgList> struct FunctorReturnType;
+ template <typename Functor, typename ... ArgList> struct FunctorReturnType<Functor, List<ArgList...>> {
+ template <typename D> static D dummy();
+ typedef decltype(dummy<Functor>().operator()((dummy<ArgList>())...)) Value;
+ };
#endif
-}
+}
QT_END_NAMESPACE
diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
index dd88d863c9..06fc89f657 100644
--- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
+++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp
@@ -143,6 +143,7 @@ private slots:
void connectVirtualSlots();
void connectPrivateSlots();
void connectFunctorArgDifference();
+ void connectFunctorOverloads();
void disconnectDoesNotLeakFunctor();
};
@@ -5599,6 +5600,131 @@ void tst_QObject::connectFunctorArgDifference()
QVERIFY(true);
}
+struct ComplexFunctor {
+ ComplexFunctor(int &overload, QList<QVariant> &result) : overload(overload), result(result) {}
+ void operator()(int a, int b) {
+ overload = 1;
+ result << a << b;
+ }
+ void operator()(double a, double b) {
+ overload = 2;
+ result << a << b;
+ }
+ void operator()(const QString &s) {
+ overload = 3;
+ result << s;
+ }
+ void operator()(const QString &) const {
+ Q_ASSERT(!"Should not be called because the non-const one should");
+ overload = -1;
+ }
+ template<typename T1, typename T2, typename T3, typename T4>
+ void operator()(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) {
+ overload = 4;
+ result << QVariant::fromValue(t1) << QVariant::fromValue(t2) << QVariant::fromValue(t3) << QVariant::fromValue(t4);
+ }
+ int &overload;
+ QList<QVariant> &result;
+protected:
+ void operator()() const {
+ Q_ASSERT(!"Should not be called because it is protected");
+ overload = -1;
+ }
+};
+
+struct ComplexFunctorDeriv : ComplexFunctor {
+ ComplexFunctorDeriv(int &overload, QList<QVariant> &result) : ComplexFunctor(overload, result) {}
+
+ void operator()() const {
+ overload = 10;
+ }
+ void operator()(int a, int b) {
+ overload = 11;
+ result << a << b;
+ }
+ using ComplexFunctor::operator();
+private:
+ void operator()(int) {
+ Q_ASSERT(!"Should not be called because it is private");
+ overload = -1;
+ }
+};
+
+class FunctorArgDifferenceObject : public QObject
+{
+ Q_OBJECT
+signals:
+ void signal_ii(int,int);
+ void signal_iiS(int,int, const QString &);
+ void signal_dd(double,double);
+ void signal_ddS(double,double, const QString &);
+ void signal_S(const QString &);
+ void signal_SSSS(const QString &, const QString &, const QString &, const QString &);
+ void signal_iiSS(int, int, const QString &, const QString &);
+ void signal_VV(const QVariant &, const QVariant &);
+};
+
+template<typename Functor, typename Signal>
+void connectFunctorOverload_impl(Signal signal, int expOverload, QList<QVariant> expResult)
+{
+ FunctorArgDifferenceObject obj;
+ int overload;
+ QList<QVariant> result;
+ QVERIFY(QObject::connect(&obj, signal, Functor(overload, result)));
+
+ obj.signal_ii(1,2);
+ obj.signal_iiS(3,4,"5");
+ obj.signal_dd(6.6,7.7);
+ obj.signal_ddS(8.8,9.9,"10");
+ obj.signal_S("11");
+ obj.signal_SSSS("12", "13", "14", "15");
+ obj.signal_iiSS(16, 17, "18", "19");
+ obj.signal_VV(20,21);
+
+ QCOMPARE(overload, expOverload);
+ QCOMPARE(result, expResult);
+}
+
+void tst_QObject::connectFunctorOverloads()
+{
+#if defined (Q_COMPILER_DECLTYPE) && defined (Q_COMPILER_VARIADIC_TEMPLATES)
+ connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_ii, 1,
+ (QList<QVariant>() << 1 << 2));
+ connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_iiS, 1,
+ (QList<QVariant>() << 3 << 4));
+ connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_dd, 2,
+ (QList<QVariant>() << 6.6 << 7.7));
+ connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_ddS, 2,
+ (QList<QVariant>() << 8.8 << 9.9));
+ connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_S, 3,
+ (QList<QVariant>() << QString("11")));
+ connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_SSSS, 4,
+ (QList<QVariant>() << QString("12") << QString("13") << QString("14") << QString("15")));
+ connectFunctorOverload_impl<ComplexFunctor>(&FunctorArgDifferenceObject::signal_iiSS, 4,
+ (QList<QVariant>() << 16 << 17 << QString("18") << QString("19")));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_ii, 11,
+ (QList<QVariant>() << 1 << 2));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_iiS, 11,
+ (QList<QVariant>() << 3 << 4));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_dd, 2,
+ (QList<QVariant>() << 6.6 << 7.7));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_ddS, 2,
+ (QList<QVariant>() << 8.8 << 9.9));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_S, 3,
+ (QList<QVariant>() << QString("11")));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_SSSS, 4,
+ (QList<QVariant>() << QString("12") << QString("13") << QString("14") << QString("15")));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_iiSS, 4,
+ (QList<QVariant>() << 16 << 17 << QString("18") << QString("19")));
+ connectFunctorOverload_impl<ComplexFunctorDeriv>(&FunctorArgDifferenceObject::signal_VV, 10,
+ (QList<QVariant>()));
+
+
+#else
+ QSKIP("Does not compile without C++11 variadic template");
+#endif
+}
+
static int countedStructObjectsCount = 0;
struct CountedStruct
{