Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QtConcurrent::mapped() with a member function complaining about error: no match for 'operator=' (operand types are 'QFuture<MainWindow::IntStruct>' and 'QFuture<void>')



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


    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.



  • @CybeX A written in documentation ==> https://doc.qt.io/qt-5/qtconcurrentmap.html#concurrent-map

    When using a modifier function, the futur will return void, not the parameter type.
    You have to change you function signature to:

     MainWindow::IntStruct doubleValue(MainWindow::IntStruct &i);
    


  • So I have 2 options available to me. I could use:

    • map() which changes values in-place (in the intList QList<IntStruct I pass in) with a QFuture<IntStruct> or
    • mapped() which uses a QFuture<void> and requires a call to QFuture::result() (which is blocking if no results are ready, I suggest connecting a QFutureWatcher<>::onResultReady() to it).

    to use member functions, I can either

    1. leave my QFuture<void> as shown in the docs by @KroMignon. It is not explicitly said, but it should be extracted from the provided example - an easy thing to miss.
    2. Alternatively, I can use use std::bind while keeping my QFuture<IntStream>
    future = QtConcurrent::map(intList, std::bind( 
                                            &MainWindow::doubleValue, 
                                            this, 
                                            std::placeholders::_1
                                         )
                               );
    


  • The reason why there are no examples of QtConcurrent on non-static meber functions is because it's unsafe. If the object that has the method you are running gets deleted while QtConcurrent is running the program will segfault



  • @VRonin
    Quite right - infact 101% correct.

    However, and you can correct me if I am wrong, but these mapped functions can be used for more than just transforming the datatype into another datatype. It does not always need to be 'instance based'. Let me explain what I mean (and what my thought process is)

    In Java (lang level 8), there is the introduction of the Stream API with a bunch of additional features such as lambdas, etc. One particularly useful example is something like

    List<SomeClass>  list = ...
    list.stream().map(o -> o.someMember).filter(o -> o.value < 2).collect(Collectors.asList());
    

    Now this simply takes an object (of type List), and runs a mapping functions (returning a stream object) and running a filter function on it (returning another stream object) followed by collecting all objects into a list.

    Now, my intention, specifically in this case is to utilize the filter & map functions similarly, but due to its design (QtConcurrent functions), I am breaking these functions up with signals (and slots) everywhere.

    This isn't part of the question, but for my own curiosity, if one had to mimik functions like this above, is there a...shorter way of doing the same thing rather than

    QFuture<SomeStruct> future = ...
    QFutureWatcher<SomeStruct> ...
    connect( //futurewatcher signal started with whatever)
    connect( //futurewatcher signal finished with whatever)
    QFutureWatcher::setFuture(future)
    ...
    

    just for one part of the function (i.e. mapping or filter)