Search code examples
qtc++11signals-slotsc++17generic-lambda

Variadic signals and generic lambdas


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?


Solution

  • 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));