c++multithreadingqtqtconcurrent

Qt::Concurrent mapped with a member function


I want to use the QtConcurrent::mapped() function, but going through the QtConcurrent examples provided and trying to find a usable example to extend to my use has been difficult.

In the examples, Qt maps to static functions only, no member functions are used (many simliar mapped variations are found in the examples, but no member functions are used).

The problem:

QtConcurrent::map() applies a function to a Sequence or Iterator w/o returning i.e. QFuture<void>. However I wish to have the results returned i.e. QFuture<SomeStructOrType> from a member function.

The example/scenario I want to use it in is an intensive workload in a UI application, but a simplied example could be:

class MainWindow : public QMainWindow {
     Q_OBJECT

  public:
     // The struct is to demonstrate a datatype more complex than int / QString
     struct IntStruct {
         int i;
     };

     MainWindow(QWidget* parent = nullptr);
     IntStruct doubleValue(IntStruct i);
     ~MainWindow();

private:
   Ui::MainWindow* ui;
   // To watch progress and act upon progress change
   QFutureWatcher<IntStruct> futureWatcher;

   // Member to keep track of mapped process
   QFuture<IntStruct> future;

   //...
}

The implementation being:

MainWindow::MainWindow(QWidget* parent)
     : QMainWindow(parent)
     , ui(new Ui::MainWindow)
{
     ui->setupUi(this);

     // Input Sequence/Iterator to perform doubleValue function on
     QList<IntStruct> intList = QList<IntStruct>();
     for (int i = 0; i < 1000; i++) {
         IntStruct s;
         s.i = qrand();
         intList.append(s);
     }

     // Watch future and report on result ready (process a result when done)
     connect(&futureWatcher, &QFutureWatcher<IntStruct>::resultReadyAt, this, [intList](int index){
         qDebug() << QString("[index = %1] value = %2").arg(QString::number(index), QString::number(intList.at(index).i));
     });

     //typedef IntStruct (MainWindow::*doubleValueFunction)(IntStruct);
     //doubleValueFunction memberFunction = this->doubleValue;             // this->doubleValue error: reference to non-static member function must be called ???

     // Run future - this doubleValue function should be a pointer to an instance function, how to do so?         
     future = QtConcurrent::mapped(intList, &MainWindow::doubleValue);
     futureWatcher.setFuture(future);
}

// Some complex member function
MainWindow::IntStruct MainWindow::doubleValue(MainWindow::IntStruct i)
{
    i.i *= i.i;
    return i;
}

I am aware that QtConcurrent allows lambdas, however I need to utilize a member function instead.

I also attempted using a pointer to the current instance with a lambda, but it gave my a schpiel of errors, which you can find here.

 future = QtConcurrent::mapped(intList, [this](IntStruct &i){
     this->doubleValue(i);
 });

TL;DR So basically, how can I use QtConcurrent::mapped() with a member function (in preferably a non-lambda version)?


UPDATE for Bounty question

Some extra information:

This is shown on screen

enter image description here

Error log when I try to compile:

..\ThreadTester\mainwindow.cpp: In constructor 'MainWindow::MainWindow(QWidget*)':
..\ThreadTester\mainwindow.cpp:27:69: error: no match for 'operator=' (operand types are 'QFuture<MainWindow::IntStruct>' and 'QFuture<void>')
      future = QtConcurrent::mapped(intList, &MainWindow::doubleValue);
                                                                     ^
In file included from F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtCore/QFuture:1:0,
                 from ..\ThreadTester\thread.h:10,
                 from ..\ThreadTester\mainwindow.h:6,
                 from ..\ThreadTester\mainwindow.cpp:1:
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtCore/qfuture.h:59:7: note: candidate: QFuture<MainWindow::IntStruct>& QFuture<MainWindow::IntStruct>::operator=(const QFuture<MainWindow::IntStruct>&)
 class QFuture
       ^~~~~~~
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtCore/qfuture.h:59:7: note:   no known conversion for argument 1 from 'QFuture<void>' to 'const QFuture<MainWindow::IntStruct>&'
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtCore/qfuture.h:59:7: note: candidate: QFuture<MainWindow::IntStruct>& QFuture<MainWindow::IntStruct>::operator=(QFuture<MainWindow::IntStruct>&&)
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtCore/qfuture.h:59:7: note:   no known conversion for argument 1 from 'QFuture<void>' to 'QFuture<MainWindow::IntStruct>&&'
In file included from F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include/QtConcurrent/qtconcurrentfilterkernel.h:48:0,
                 from F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtConcurrent/qtconcurrentfilter.h:47,
                 from F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtConcurrent/QtConcurrent:8,
                 from ..\ThreadTester\thread.h:11,
                 from ..\ThreadTester\mainwindow.h:6,
                 from ..\ThreadTester\mainwindow.cpp:1:
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include/QtConcurrent/qtconcurrentmapkernel.h: In instantiation of 'QtConcurrent::ThreadEngineStarter<T> QtConcurrent::startMapped(const Sequence&, Functor) [with T = void; Sequence = QList<MainWindow::IntStruct>; Functor = QtConcurrent::MemberFunctionWrapper1<MainWindow::IntStruct, MainWindow, MainWindow::IntStruct>]':
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtConcurrent/qtconcurrentmap.h:132:88:   required from 'QFuture<typename QtPrivate::MapResultType<void, MapFunctor>::ResultType> QtConcurrent::mapped(const Sequence&, MapFunctor) [with Sequence = QList<MainWindow::IntStruct>; MapFunctor = MainWindow::IntStruct (MainWindow::*)(MainWindow::IntStruct); typename QtPrivate::MapResultType<void, MapFunctor>::ResultType = void]'
..\ThreadTester\mainwindow.cpp:27:69:   required from here
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include/QtConcurrent/qtconcurrentmapkernel.h:237:71: error: could not convert 'QtConcurrent::startThreadEngine(ThreadEngine*) [with ThreadEngine = QtConcurrent::SequenceHolder1<QList<MainWindow::IntStruct>, QtConcurrent::MappedEachKernel<QList<MainWindow::IntStruct>::const_iterator, QtConcurrent::MemberFunctionWrapper1<MainWindow::IntStruct, MainWindow, MainWindow::IntStruct> >, QtConcurrent::MemberFunctionWrapper1<MainWindow::IntStruct, MainWindow, MainWindow::IntStruct> >; typename ThreadEngine::ResultType = MainWindow::IntStruct]()' from 'QtConcurrent::ThreadEngineStarter<MainWindow::IntStruct>' to 'QtConcurrent::ThreadEngineStarter<void>'
     return startThreadEngine(new SequenceHolderType(sequence, functor));
                                                                       ^
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include/QtConcurrent/qtconcurrentmapkernel.h: In instantiation of 'bool QtConcurrent::MappedEachKernel<Iterator, MapFunctor>::runIteration(Iterator, int, QtConcurrent::MappedEachKernel<Iterator, MapFunctor>::T*) [with Iterator = QList<MainWindow::IntStruct>::const_iterator; MapFunctor = QtConcurrent::MemberFunctionWrapper1<MainWindow::IntStruct, MainWindow, MainWindow::IntStruct>; QtConcurrent::MappedEachKernel<Iterator, MapFunctor>::T = MainWindow::IntStruct]':
..\ThreadTester\mainwindow.cpp:36:1:   required from here
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include/QtConcurrent/qtconcurrentmapkernel.h:175:17: error: no match for call to '(QtConcurrent::MemberFunctionWrapper1<MainWindow::IntStruct, MainWindow, MainWindow::IntStruct>) (const MainWindow::IntStruct&)'
         *result = map(*it);
In file included from F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtConcurrent/qtconcurrentfilter.h:48:0,
                 from F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include\QtConcurrent/QtConcurrent:8,
                 from ..\ThreadTester\thread.h:11,
                 from ..\ThreadTester\mainwindow.h:6,
                 from ..\ThreadTester\mainwindow.cpp:1:
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include/QtConcurrent/qtconcurrentfunctionwrappers.h:132:14: note: candidate: T QtConcurrent::MemberFunctionWrapper1<T, C, U>::operator()(C&, U) [with T = MainWindow::IntStruct; C = MainWindow; U = MainWindow::IntStruct]
     inline T operator()(C &c, U u)
              ^~~~~~~~
F:\Qt\Qt5.13.1\5.13.1\mingw73_32\include/QtConcurrent/qtconcurrentfunctionwrappers.h:132:14: note:   candidate expects 2 arguments, 1 provided
mingw32-make[1]: *** [Makefile.Debug:1117: debug/mainwindow.o] Error 1
mingw32-make[1]: *** Waiting for unfinished jobs....
mingw32-make[1]: Leaving directory 'C:/Users/CybeX/QtProjects/build-ThreadTester-Desktop_Qt_5_13_1_MinGW_32_bit-Debug'
mingw32-make: *** [Makefile:38: debug] Error 2

What does not make sense to me at all is mapped() returns some datatype U. If I changed the QFuture<IntStruct> to QFuture<void>, everyone is happy, but I cannot gather any results which defeats the purpose of the mapped() function in the first place.


Solution

  • You have to use std::bind:

    #include <functional>
    
    # ....
    
    future = QtConcurrent::mapped(intList,
                                  std::bind(&MainWindow::doubleValue,
                                            this,
                                            std::placeholders::_1));