0

Is it possible to create variadic signal and connect generic lambda as slot? I mean something like (say, all definitions of involved functions are visible where needed (e.g. at points of instantiation)):

#include <QCoreApplication>
#include <QObject>
#include <QTime>

class A
    : public QObject
{
    Q_OBJECT

public :

    A(QObject * const parent = Q_NULLPTR)
        : QObject{parent}
    { ; }

signals :

    template< typename ...Ts >
    void infoMessage(Ts... args);

public slots :

    void run()
    {
        emit infoMessage("Started at ", QTime::currentTime());
    }

};

#include <QTimer>
#include <QtDebug>

#include "main.moc"

int main(int argc, char * argv [])
{
    QCoreApplication a{argc, argv};
    A a;
    auto printInfoMessage = [&] (auto... args)
    {
        (qInfo() << ... << args);
    };
    QObject::connect(&a, SIGNAL(infoMessage), printInfoMessage);
    QTimer::singleShot(0, &a, &A::run);
    return a.exec();
}

Currently it gives an error message:

AUTOGEN: error: process for main.cpp:18: Error: Template function as signal or slot

moc failed...

Here macro SLOT() instead of &A::infoMessage does not help a lot. Is there any workarounds to overcome this limitation?

I know, that some of the answers will contain a using of std::make_tuple and std::index_sequence stuff. But is there less verbose solution?

Community
  • 1
  • 1
Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169

1 Answers1

1

There is no direct workaround for having template. On of thea reason is that the moc indexes all signals and slots, and this cannot be done for function templates as function templates will generate several functions depending code that is generally not accessible from the moc.

I don't think you can make it work with tuple and such as these are also templates.

A solution could be to use QVariant and/or QVariantList for your arguments.


Please note that the error is not caused by the QObject::connect line, but the the signal declaration in class A.

Also, you cannot replace SIGNAL() and SLOT() at your will, it is either a signal or a slot, it cannot be both.

And finally you should be using this form:

QObject::connect(&a, &A::infoMessage, printInfoMessage);

And since printInfoMessage is using auto parameters, you might need to force the auto resolution using qOverload:

QObject::connect(&a, &A::infoMessage, qOverload<QVariantList>(printInfoMessage));
Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
Benjamin T
  • 8,120
  • 20
  • 37
  • Isn't it possible to collect all the variants of instantiation of member function on `emit someSignal(...);` points and then generate all the corresponding specializations right before linking? – Tomilov Anatoliy Apr 27 '17 at 09:17
  • No. The `moc` is called before any compilation is done and only process files which have the Q_OBJECT macro. The output of the moc is C++ code that is compiled at the same time your own C++ files are compiled. So link time is far too late for the moc to add any function. But link time is also far too soon to know any use cases of your template function (e.g is you are building a library, the binary that will use your library will generate there own functions based on your template) – Benjamin T Apr 27 '17 at 09:24
  • What do you think: is it possible to overcome all these limitations when static reflection became avaliable? Or maybe some parts of `clang` infrastructure can help here? – Tomilov Anatoliy Apr 27 '17 at 09:33
  • BTW it seems that there is no need to use `qOverload` at all. – Tomilov Anatoliy Apr 27 '17 at 09:44
  • I do not know, but even when static reflection will be available, you will need to wait for the Qt devs to change the way the moc works. And because of Qt backward compatibility of ABI, I do not see this happening in a minor Qt release. So it may take a few more years. – Benjamin T Apr 27 '17 at 09:45
  • "few more years" sounds bad =). Thank you. – Tomilov Anatoliy Apr 27 '17 at 09:50