1

I'm new to the C++ and QT world. I need to do some modifications on an existing console application.

I have the following problem: I'm running some functions (which take some time) concurrently and show a wait indicator during this time. The setup looks like this:

QFuture<void> doStuff = QtConcurrent::run(longCalc, param1, param2);
showWaitIndicator(&doStuff);

// ....

void showWaitIndicator(QFuture<void> *future)
{
    while (future->isRunning()) {
        // Show some nice indicator and so on.
    }
}

This setup works just fine, but now I want to run some other tasks concurrently which have another return type and I need to access the result. Instead of QFuture<void>these are mostly QFuture<double>, QFuture<int>, etc: QFuture<double> doStuff = QtConcurrent::run(doubleCalc);

I also want to display my nice wait indicator, but the different return types mean I can't use my current showWaitIndicator() function.

Is there a good way to improve this "setup"? I'm new to C++, so I'm pretty sure there must be a way. My first idea was function overloading but this didn't work because the parameters have the same type (QFuture).

TL;DR: I need to inform my showWaitIndicator() function that QFuture finished.

LuMa
  • 1,673
  • 3
  • 19
  • 41
  • Just check about the SIGNAL and SLOT on Qt. When the Qfuture finish the task, it should be trigger a SIGNAL, and you can connect it with SLOT – Apin Jun 10 '17 at 02:37

1 Answers1

2

You can emit a custom signal from the function that runs concurrently, or use a QFutureWatcher as a source of such signal.

E.g. when longCalc is in the same class:

MyClass::MyClass() {
  Q_OBJECT
  Q_SIGNAL void longCalcDone();
  connect(this, &MyClass::longCalcDone, this, [this]{
    ...
  });
}
void MyClass::longCalc(int arg1, int arg2) {
  ...
  emit MyClass::longCalcDone();
}

E.g. when longCalc is a free function or is in another class:

void longCalc(int, int);

MyClass::MyClass() {
  Q_OBJECT
  Q_SIGNAL void longCalcDone();
  connect(this, &MyClass::longCalcDone, this, [this]{
    ...
  });
  void doStuff() {
    QtConcurrent::run([=]{
      longCalc(param1, param2);
      emit longCalcDone();
    });
  }
}

E.g. with a future watcher instead:

class MyClass : public QObject {
  QFutureWatcher watcher;

  MyClass() {
    connect(&watcher, &QFutureWatcher::finished, this, [this]{
      ...
    });
  }

  void doStuff() {
    auto future = QtConcurrent::run(longCalc, this, param1, param2);
    watcher.setFuture(&future);
  }
};

The while (future->isRunning()) synchronous code is an anti-pattern. Presumably you invoke QCoreApplication::processEvents within that loop. The problem is - the world is not like that, you can't take the locus of control away from the event loop and pretend that the world revolves around you. Instead, invert the control flow and have your code (a slot, a method or a functor) invoked when the future finishes.

See also this question.

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