Discussion:
[Development] [Interest] Qt5: Problem connecting QTimer::timeout() to lambda
Stephen Chu
2012-11-01 17:03:56 UTC
Permalink
I find out what QPrivateSignal is for but it's not clear if lambda can
still be used to connect to such private signals.

If I connect QTimer::timeout() to a slot that takes no argument, it
compiles fine. But when I try to connect to a lambda function without
any argument, the results in the mentioned error.

Is this designed behavior? It makes using lambda function as slot really
hard since there's no mention in documentation which signals are limited
this way.

FYI. I am using clang 3.1.
QTimer timer;
connect(&timer, &QTimer::timeout, [=](){});
error: no matching function for call to object of type
'PageLogPanel::<lambda at ../../Dropbox/Client2012/PageLogPanel.cpp:47:36>'
f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type
*>(arg[I+1]))...), ApplyReturnValue<R>(arg[0]);
^
note: in instantiation of member function
'QtPrivate::FunctorCall<QtPrivate::IndexesList<0>,
QtPrivate::List<QTimer::QPrivateSignal>, void, PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36> >::call' requested here
FunctorCall<typename Indexes<N>::Value, SignalArgs, R,
Function>::call(f, arg);
^
note: in instantiation of function template specialization
'QtPrivate::Functor<PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36>,
1>::call<QtPrivate::List<QTimer::QPrivateSignal>, void>' requested here
FuncType::template call<Args,
R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
^
note: in instantiation of member function
'QtPrivate::QFunctorSlotObject<PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36>, 1,
QtPrivate::List<QTimer::QPrivateSignal>, void>::impl' requested here
QSlotObjectBase(&impl), function(f) {}
^
note: in instantiation of member function
'QtPrivate::QFunctorSlotObject<PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36>, 1,
QtPrivate::List<QTimer::QPrivateSignal>, void>::QFunctorSlotObject'
requested here
new QtPrivate::QFunctorSlotObject<Func2,
SignalType::ArgumentCount, typename SignalType::Arguments, typename
SignalType::ReturnType>(slot),
^
../../Dropbox/Client2012/PageLogPanel.cpp:47:2: note: in instantiation
of function template specialization 'QObject::connect<void
(QTimer::*)(), PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36> >' requested here
connect(&timer, &QTimer::timeout, [=](){});
^
../../Dropbox/Client2012/PageLogPanel.cpp:47:36: note: candidate
function not viable: requires 0 arguments, but 1 was provided
connect(&timer, &QTimer::timeout, [=](){});
^
It seems
http://qt.gitorious.org/qt/qtbase/commit/dee57bc91080740201a0bf0b8c42eb374ee696f3
added QPrivateSignal to the signal signature?
_______________________________________________
Interest mailing list
http://lists.qt-project.org/mailman/listinfo/interest
Thiago Macieira
2012-11-01 17:17:43 UTC
Permalink
Post by Stephen Chu
I find out what QPrivateSignal is for but it's not clear if lambda can
still be used to connect to such private signals.
If I connect QTimer::timeout() to a slot that takes no argument, it
compiles fine. But when I try to connect to a lambda function without
any argument, the results in the mentioned error.
Can you rephrase and/or give us code samples?

"takes no arguments" and "without any argument" are semantically the same in
English.
Post by Stephen Chu
Is this designed behavior? It makes using lambda function as slot really
hard since there's no mention in documentation which signals are limited
this way.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Stephen Chu
2012-11-01 18:49:27 UTC
Permalink
Post by Thiago Macieira
Post by Stephen Chu
I find out what QPrivateSignal is for but it's not clear if lambda can
still be used to connect to such private signals.
If I connect QTimer::timeout() to a slot that takes no argument, it
compiles fine. But when I try to connect to a lambda function without
any argument, the results in the mentioned error.
Can you rephrase and/or give us code samples?
"takes no arguments" and "without any argument" are semantically the same in
English.
This doesn't work (compile error):

QTimer timer;
QObject::connect(&timer, &QTimer::timeout, [=](){});

This does:

QTimer timer;
auto label = new QLabel;
QObject::connect(&timer, &QTimer::timeout, label, &QWidget::show);


Again. This is using clang 3.1 with QMAKE_CXXFLAGS += -std=c++11
Thiago Macieira
2012-11-01 20:13:34 UTC
Permalink
Post by Stephen Chu
Again. This is using clang 3.1 with QMAKE_CXXFLAGS += -std=c++11
Is that the real clang 3.1, or the 3.1 version of Apple clang? They are not
the same, they have different C++11 support status and known bugs.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Stephen Chu
2012-11-01 20:19:20 UTC
Permalink
Post by Thiago Macieira
Post by Stephen Chu
Again. This is using clang 3.1 with QMAKE_CXXFLAGS += -std=c++11
Is that the real clang 3.1, or the 3.1 version of Apple clang? They are not
the same, they have different C++11 support status and known bugs.
I am sorry for not being clear. It's mainline (real) 3.1.
Thiago Macieira
2012-11-01 20:28:32 UTC
Permalink
Post by Thiago Macieira
Post by Stephen Chu
Again. This is using clang 3.1 with QMAKE_CXXFLAGS += -std=c++11
Is that the real clang 3.1, or the 3.1 version of Apple clang? They are not
the same, they have different C++11 support status and known bugs.
The code was expected to work. We need to figure out if it's a compiler bug or
whether the C++ standard says it shouldn't work.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Stephen Chu
2012-11-01 21:14:55 UTC
Permalink
Post by Thiago Macieira
Post by Thiago Macieira
Post by Stephen Chu
Again. This is using clang 3.1 with QMAKE_CXXFLAGS += -std=c++11
Is that the real clang 3.1, or the 3.1 version of Apple clang? They are not
the same, they have different C++11 support status and known bugs.
The code was expected to work. We need to figure out if it's a compiler bug or
whether the C++ standard says it shouldn't work.
Bug reported: https://bugreports.qt-project.org/browse/QTBUG-27813
Olivier Goffart
2012-11-01 23:23:43 UTC
Permalink
Hi,

It is the QPrivateSignal that is breaking it.

It is only possible to connect to functor (lambda are functor) if they have
the same number of arguments. The number need to match exactly, and it is not
possible to omit any. (so you may not omit the QPrivateSignal argument)
Using C++11 new SFINAE rules, it should be possible to change that.

One hack could be to remove the QPrivateSignal type from the Argument list in
the second template parametter of QFunctorSlotObject.
--
Olivier

Woboq - Qt services and support - http://woboq.com
Post by Stephen Chu
I find out what QPrivateSignal is for but it's not clear if lambda can
still be used to connect to such private signals.
If I connect QTimer::timeout() to a slot that takes no argument, it
compiles fine. But when I try to connect to a lambda function without
any argument, the results in the mentioned error.
Is this designed behavior? It makes using lambda function as slot really
hard since there's no mention in documentation which signals are limited
this way.
FYI. I am using clang 3.1.
QTimer timer;
connect(&timer, &QTimer::timeout, [=](){});
395:13: error: no matching function for call to object of type
'PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36>'
f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type
*>(arg[I+1]))...), ApplyReturnValue<R>(arg[0]);
^
434:79: note: in instantiation of member function
'QtPrivate::FunctorCall<QtPrivate::IndexesList<0>,
QtPrivate::List<QTimer::QPrivateSignal>, void, PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36> >::call' requested here
FunctorCall<typename Indexes<N>::Value, SignalArgs, R,
Function>::call(f, arg);
^
17: note: in instantiation of function template specialization
'QtPrivate::Functor<PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36>,
1>::call<QtPrivate::List<QTimer::QPrivateSignal>, void>' requested here
FuncType::template call<Args,
R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
^
71: note: in instantiation of member function
'QtPrivate::QFunctorSlotObject<PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36>, 1,
QtPrivate::List<QTimer::QPrivateSignal>, void>::impl' requested here
QSlotObjectBase(&impl), function(f) {}
^
note: in instantiation of member function
'QtPrivate::QFunctorSlotObject<PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36>, 1,
QtPrivate::List<QTimer::QPrivateSignal>, void>::QFunctorSlotObject'
requested here
new QtPrivate::QFunctorSlotObject<Func2,
SignalType::ArgumentCount, typename SignalType::Arguments, typename
SignalType::ReturnType>(slot),
^
../../Dropbox/Client2012/PageLogPanel.cpp:47:2: note: in instantiation
of function template specialization 'QObject::connect<void
(QTimer::*)(), PageLogPanel::<lambda at
../../Dropbox/Client2012/PageLogPanel.cpp:47:36> >' requested here
connect(&timer, &QTimer::timeout, [=](){});
^
../../Dropbox/Client2012/PageLogPanel.cpp:47:36: note: candidate
function not viable: requires 0 arguments, but 1 was provided
connect(&timer, &QTimer::timeout, [=](){});
^
It seems
http://qt.gitorious.org/qt/qtbase/commit/dee57bc91080740201a0bf0b8c42eb374
ee696f3 added QPrivateSignal to the signal signature?
_______________________________________________
Interest mailing list
http://lists.qt-project.org/mailman/listinfo/interest
_______________________________________________
Development mailing list
http://lists.qt-project.org/mailman/listinfo/development
Thiago Macieira
2012-11-02 00:32:20 UTC
Permalink
Post by Olivier Goffart
Hi,
It is the QPrivateSignal that is breaking it.
It is only possible to connect to functor (lambda are functor) if they have
the same number of arguments. The number need to match exactly, and it is
not possible to omit any. (so you may not omit the QPrivateSignal argument)
Using C++11 new SFINAE rules, it should be possible to change that.
One hack could be to remove the QPrivateSignal type from the Argument list
in the second template parametter of QFunctorSlotObject.
Why doesn't the code use the number of arguments from the receiver instead of
the sender? We did that for regular functions and member functions.
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Olivier Goffart
2012-11-02 08:41:06 UTC
Permalink
Post by Thiago Macieira
Post by Olivier Goffart
Hi,
It is the QPrivateSignal that is breaking it.
It is only possible to connect to functor (lambda are functor) if they have
the same number of arguments. The number need to match exactly, and it is
not possible to omit any. (so you may not omit the QPrivateSignal argument)
Using C++11 new SFINAE rules, it should be possible to change that.
One hack could be to remove the QPrivateSignal type from the Argument list
in the second template parametter of QFunctorSlotObject.
Why doesn't the code use the number of arguments from the receiver instead
of the sender? We did that for regular functions and member functions.
Because for regular functions slot, I can knw that. The arguments are in
the template argument.

When I do QtPrivate::FunctionPointer<&MyObject::mySlot>::AgumentCount, the
partial template specialisation is chosen (imagine mySlot has two arguments)

template<class Obj, typename Ret, typename Arg1, typename Arg2>
struct FunctionPointer<Ret (Obj::*) (Arg1, Arg2)> {
enum {ArgumentCount = 2}; ... }

Hence, I know I can call the function with just the two first argument of the
signal.


However, in the case of a functor, I have not found a way.
struct MyFunctor{ void operator()(); };

then one call QObject::connect<void(QTimer::*)(QTimer::QPrivateSignal),
MyFunctor>(....)
Then I can know the number of argument of the signal type (using
QtPrivate::FunctionPointer), but I don't know how to know the number of
argument of the slot type.
QtPrivate::FunctionPointer<&MyFunctor::operator()> will not work in case there
are several operator().

With C++11, it would be possible to do something like

template<typename Functor, typename ... SignalArgs>
test(Functor&f, SignalArgs... &args) -> enable_if<(sizeof(f(args...))>0),yes>;

And if it does not exist, try removing the last argument from signal args and
see if that exist.


Something like that could be done for Qt 5.1 (or even maybe Qt 5.0) But I
don't know how to do it without C++11.

And connecting to functor works without C++11 (now, it is probably ok to say
that connecting to functor with less arguments than the signal is only working
with C++11)
--
Olivier

Woboq - Qt services and support - http://woboq.com
Thiago Macieira
2012-11-02 15:01:58 UTC
Permalink
Post by Olivier Goffart
Post by Thiago Macieira
Why doesn't the code use the number of arguments from the receiver instead
of the sender? We did that for regular functions and member functions.
Because for regular functions slot, I can knw that. The arguments are in
the template argument.
When I do QtPrivate::FunctionPointer<&MyObject::mySlot>::AgumentCount, the
partial template specialisation is chosen (imagine mySlot has two arguments)
template<class Obj, typename Ret, typename Arg1, typename Arg2>
struct FunctionPointer<Ret (Obj::*) (Arg1, Arg2)> {
enum {ArgumentCount = 2}; ... }
Hence, I know I can call the function with just the two first argument of
the signal.
I don't care about C++98 for this functionality. It's provided as a "best
effort" basis, with known limitations. Nominally, the new syntax of the
connection mechanism is a feature that requires C++11.

So please tell me how the above works with variadic templates.
Post by Olivier Goffart
However, in the case of a functor, I have not found a way.
struct MyFunctor{ void operator()(); };
There is a function, it's just MyFunctor::operator(). Why can't we do the
same?
Post by Olivier Goffart
QtPrivate::FunctionPointer<&MyFunctor::operator()> will not work in case
there are several operator().
That is a good point, but an acceptable compromise.
Post by Olivier Goffart
With C++11, it would be possible to do something like
template<typename Functor, typename ... SignalArgs>
test(Functor&f, SignalArgs... &args) ->
enable_if<(sizeof(f(args...))>0),yes>;
And if it does not exist, try removing the last argument from signal args
and see if that exist.
Something like that could be done for Qt 5.1 (or even maybe Qt 5.0) But I
don't know how to do it without C++11.
Then let's do it, since we don't need C++98 support for this.
Post by Olivier Goffart
And connecting to functor works without C++11 (now, it is probably ok to
say that connecting to functor with less arguments than the signal is only
working with C++11)
--
Thiago Macieira - thiago.macieira (AT) intel.com
Software Architect - Intel Open Source Technology Center
Stephen Kelly
2012-11-02 13:54:42 UTC
Permalink
Post by Olivier Goffart
One hack could be to remove the QPrivateSignal type from the Argument list
in the second template parametter of QFunctorSlotObject.
The candidate fix for this is here:

https://codereview.qt-project.org/#change,38703

Thanks,
--
Stephen Kelly <***@kdab.com> | Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
www.kdab.com || Germany +49-30-521325470 || Sweden (HQ) +46-563-540090
KDAB - Qt Experts - Platform-Independent Software Solutions
** Qt Developer Conference: http://qtconference.kdab.com/ **
Loading...