Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding QTimer with Lambda and recursive function call

I have the following code:

void class::Testfunc()
{
    QTimer* timer = new QTimer;
    QObject::connect(timer, &QTimer::timeout, [this](){
        emit Log("Time out...");
        TestFunc(serverAddress, requestsFolderPath);
       // deleteLater(); //*** why does this crash if used to replace the connect below?
    });
    connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater);
    timer->setSingleShot(true);
    timer->start(1000);
}

A single shot timer is created with a timout connected to a lambda function that logs the entrance to the lambda function each second (prints text to stdout) and calls the function again.

This works without issue. However, if I remove the connect call to deleteLater (below the lambda function), but enable the deleteLater call in the lambda function, the function fails. It prints once and shortly after, crashes in trying to delete the timer object.

What is the difference between the two deleteLater calls in this instance and why would placing the deleteLater in the lambda function cause a problem here, whereas creating a separate connection works as expected, even though both are calling deleteLater in response to the Timer's timeout signal?

like image 507
TheDarkKnight Avatar asked Nov 03 '14 11:11

TheDarkKnight


People also ask

What is recursive function explain about lambda function?

A recursive lambda expression is the process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. Using a recursive algorithm, certain problems can be solved quite easily.

Can lambda be called recursively?

This is an example of a function that will recursively call itself. Warning It's possible to run into infinite loops with recursive calls.

Can lambda function call itself C++?

But a lambda cannot be recursive, it has no way to invoke itself. A lambda has no name and using this within the body of a lambda refers to a captured this (assuming the lambda is created in the body of a member function, otherwise it is an error).

How do you write a recursive lambda function in C++?

Recursive Lambdas in C++ auto fib = [](int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); }; auto i = fib(7);


2 Answers

Given that there is no typo or some information that I am not aware of, I think the reason is that because you are trying to delete your class instance later rather than the QTimer instance allocated on the heap in the aforementioned method.

If you take a look at the the non-lambda version, that calls the deleteLater on the QTimer instance as that is the receiver in the connect call.

connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater);

However, in the lambda variant, the timer instance is not captured and naturally, there would be no access to it in its current version, respectively. To make the two alternatives equivalent, this modification needs to be done to the code:

QObject::connect(timer, &QTimer::timeout, [this, timer](){
//                                               ^^^^^
    emit Log("Time out...");
    TestFunc(serverAddress, requestsFolderPath);
    timer->deleteLater();
//  ^^^^^^^
});
like image 95
lpapp Avatar answered Oct 19 '22 07:10

lpapp


The default approach should be not to do manual memory management. Qt can manage timer lifetime automatically. The code becomes quite simple in Qt 5.4 and later, and can be backported for Qt 5.0-5.3:

// https://github.com/KubaO/stackoverflown/tree/master/questions/qtimer-retrofit-26713879
#include <QtCore>

struct Class : QObject {
   void TestFunc();
   void Log(const char *str) { qDebug() << str; }
};

#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
namespace compat { using QT_PREPEND_NAMESPACE(QTimer); }
#else
QT_BEGIN_NAMESPACE
Q_CORE_EXPORT void qDeleteInEventHandler(QObject *o);
QT_END_NAMESPACE
namespace compat {
using QT_PREPEND_NAMESPACE(qDeleteInEventHandler);
template <class Fun> struct SingleShotHelper : QObject, Fun {
   QBasicTimer timer;
   template <class F> SingleShotHelper(int msec, QObject *context, F &&fun) :
      QObject(context ? context : QAbstractEventDispatcher::instance()),
      Fun(std::forward<F>(fun)) {
      timer.start(msec, this);
      if (!context)
         connect(qApp, &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
   }
   void timerEvent(QTimerEvent *ev) override {
      if (ev->timerId() != timer.timerId()) return;
      timer.stop();
      (*this)();
      qDeleteInEventHandler(this);
   }
};
using Q_QTimer = QT_PREPEND_NAMESPACE(QTimer);
class QTimer : public Q_QTimer {
   Q_OBJECT
public:
   QTimer(QObject *parent = {}) : Q_QTimer(parent) {} // C++17: using Q_QTimer::Q_QTimer;
   template <class Fun>
   inline static void singleShot(int msec, QObject *context, Fun &&fun) {
      new SingleShotHelper<Fun>(msec, context, std::forward<Fun>(fun));
   }
}; }
#endif

void Class::TestFunc() {
   compat::QTimer::singleShot(1000, this, [this]{
      emit Log("Timeout...");
      TestFunc();
   });
}
like image 24
Kuba hasn't forgotten Monica Avatar answered Oct 19 '22 07:10

Kuba hasn't forgotten Monica