1

I like the new syntax for QObject::connect

connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
    receiver->updateValue("senderValue", newValue);
});

I want to implement my own method that gets a lambda expression as argument.

In my particular case I want to recreate the Javascript Function setTimeout().

How does the correct syntax look like to have a lambda as argument of my method? I want the lambda not to have any arguments or another return type than void.

void setTimeout(/* SOME_TYPE callback */, int timeout)
{
    QTimer *timer = new QTimer(this);
    timer->setInterval(timeout);
    timer->setSingleShot(true);
    connect(timer, &QTimer::timeout, (){
        //This lambda should be an argument
    });
    //connect(timer, &QTimer::timeout, callback);
    connect(timer, &QTimer::timeout, timer, &QObject::deleteLater);
    timer->start();
}

I can't find the methods defined in QObject. Some are only defined for documentation generation. (#ifdef Q_QDOC)

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
feedc0de
  • 3,646
  • 8
  • 30
  • 55

3 Answers3

1

You don't have to reimplement it: QTimer::singleShot does it all.

If you wanted to wrap singleShot or connect, you don't really care what the callable is, as long as target method accepts it. There's no reason for onTimeout to be a class member. It should be a free function: we're writing C++, not Java.

// https://github.com/KubaO/stackoverflown/tree/master/questions/forward-callable-38126723
#include <QtCore>

template <typename C>
void onTimeout(int msec, C && callable) {
   QTimer::singleShot(msec, std::forward<C>(callable));
}

template <typename C>
void onTimeout(int msec, QObject * context, C && callable) {
   QTimer::singleShot(msec, context, std::forward<C>(callable));
}

int main(int argc, char ** argv) {
   QCoreApplication app{argc, argv};
   QObject context;
   QThread thread;
   context.moveToThread(&thread);
   thread.start();
   onTimeout(1000, []{ qDebug() << "T+1s"; });
   onTimeout(2000, &context, [&]{ qDebug() << "T+2s"; thread.quit(); });
   QObject::connect(&thread, &QThread::finished, &app, &QCoreApplication::quit);
   return app.exec();
}

The first timeout is handled from the main thread's event loop. The second timeout is handled from the worker thread's event loop - that's what the context argument is for. When implementing a function/method that accepts a callable, you should always optionally accept a context object that dictates what thread the callable will execute in. The default is to execute it in the event loop of the current thread.

See also that question about the purpose of std::forward.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
0

You might either have template<typename T> and take const T&, which has the advantage of being quick an working with functors, too - or you might use std::function<>, which is a bit slower but can be taken by a virtual function as well.

lorro
  • 10,687
  • 23
  • 36
0

Make the parameter that should accept the lambda of the appropriate template type std::function<Res(Arg,...)>' from the headerfunctional`.

Thomas B Preusser
  • 1,159
  • 5
  • 10