Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Pass a method to QtConcurrent::map
Forum Updated to NodeBB v4.3 + New Features

Pass a method to QtConcurrent::map

Scheduled Pinned Locked Moved Solved General and Desktop
25 Posts 2 Posters 8.6k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • nulluseN Offline
    nulluseN Offline
    nulluse
    wrote on last edited by nulluse
    #3

    Something I do not understand: why is it possible to pass QString::squeeze() to map() but not a method of my class? So this example does not compile (typed by memory just to illustrate the issue):

    class newDlg: QDialog {
    public
      newDlg();
      virtual ~newDlg();
    private:
      a int;
      QVector<int> vec;
    private slot:
      void handleBtn();
    private:
      int calculate(int& i);
    }
    
    newDlg::newDlg(){
      a = 10;
    }
    
    int newDlg::calculate(int& i) {
      // do some intensive calculations based on the fact that a=10;
    }
    void newDlg::handleBtn() {
      QFuture f;
      f.setFuture(QtConcurrent::map(vec, calculate)); // this won't compile 
    }
    
    

    But if calculate() is a regular function, then the above works.

    How is QString::squeeze() different from newDlg::calculate()?

    kshegunovK 1 Reply Last reply
    0
    • nulluseN nulluse

      Something I do not understand: why is it possible to pass QString::squeeze() to map() but not a method of my class? So this example does not compile (typed by memory just to illustrate the issue):

      class newDlg: QDialog {
      public
        newDlg();
        virtual ~newDlg();
      private:
        a int;
        QVector<int> vec;
      private slot:
        void handleBtn();
      private:
        int calculate(int& i);
      }
      
      newDlg::newDlg(){
        a = 10;
      }
      
      int newDlg::calculate(int& i) {
        // do some intensive calculations based on the fact that a=10;
      }
      void newDlg::handleBtn() {
        QFuture f;
        f.setFuture(QtConcurrent::map(vec, calculate)); // this won't compile 
      }
      
      

      But if calculate() is a regular function, then the above works.

      How is QString::squeeze() different from newDlg::calculate()?

      kshegunovK Away
      kshegunovK Away
      kshegunov
      Moderators
      wrote on last edited by kshegunov
      #4

      @nulluse
      Hello,
      Let's take it from the example in the doc:

      QStringList strings; //< This is basically QList<QString>
      QFuture<void> squeezedStrings = QtConcurrent::map(strings, &QString::squeeze);
      

      So, when you pass the pointer to member it's a function address and have no notion of objects. However it knows what class the object should be. That's why QString::squeeze works for QList<QString> but will not work for QList<QImage>, because QString has a squeeze method, while QImage doesn't.

      Unfortunately from your example I don't know what types the vector holds (you've omitted the template specialization), so I can't say why it's not working per se. Is vec of type QVector<newDlg>? If not, then it won't be able to match the object to the method pointer.

      Additional, remark:

      int newDlg::calculate(int& i);
      

      Is not fully qualified when you pass it to QtConcurrent::map, so if you intend on using it you have to pass it like this:

      QVector<newDlg> vec;  //< Just for completeness
      
      QtConcurrent::map(vec, newDlg::calculate);
      

      Additional remark 2:
      You shouldn't use multithreading with GUI classes, it's not supported, and explicitly forbidden in the documentation. The GUI can always run in one thread and one thread alone. The GUI classes are neither thread-safe nor reentrant so even if this code compiled it wouldn't be working correctly (possibly crashing).

      Kind regards.

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      1
      • nulluseN Offline
        nulluseN Offline
        nulluse
        wrote on last edited by nulluse
        #5

        It's a vector of integers. QVector<int>

        Your remark about missing class qualifier is correct, I am typing this while sitting in a meeting without the actual code in front of me, so I am missing little things here and there, but the important part is that when using a fully qualified method QtConcurrent::map(vec, newDlg::calculate); the unit does not compile.

        It only compiles and runs when calculate() is a standalone function. But then it does not know anything about the calculation parameters known only from user input in the newDlg.

        Can only the static class member be passed to map()?

        kshegunovK 1 Reply Last reply
        0
        • nulluseN nulluse

          It's a vector of integers. QVector<int>

          Your remark about missing class qualifier is correct, I am typing this while sitting in a meeting without the actual code in front of me, so I am missing little things here and there, but the important part is that when using a fully qualified method QtConcurrent::map(vec, newDlg::calculate); the unit does not compile.

          It only compiles and runs when calculate() is a standalone function. But then it does not know anything about the calculation parameters known only from user input in the newDlg.

          Can only the static class member be passed to map()?

          kshegunovK Away
          kshegunovK Away
          kshegunov
          Moderators
          wrote on last edited by kshegunov
          #6

          @nulluse

          It's a vector of integers. QVector<int>
          ... but the important part is that when using a fully qualified method QtConcurrent::map(vec, newDlg::calculate); the unit does not compile.

          Then you can't use pointer to member for this container (see the beginning of my previous post). int is not an object so there is no way to match newDlg::calculate to have an object of type int.

          It only compiles and runs when calculate() is a standalone function. But then it does not know anything about the calculation parameters known only from user input in the newDlg.

          Naturally, you can use a lambda to capture those parameters and do the calculation. Something like this:

          auto calculateFunction = [calcParameter1, calcParameter2] (const int & element) -> int  {
              //< Do calculations here
          };
          QtConcurrent::map(vec, calculateFunction);
          

          Can only the static class member be passed to map()?

          No, but the pointer to member's class must match the objects held in the container. So QString::* is applicable to containers holding QString instances.

          Note that a static function is no different than a globally defined plain C function.

          Kind regards.

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply
          1
          • nulluseN Offline
            nulluseN Offline
            nulluse
            wrote on last edited by nulluse
            #7

            Does the parameter have to be const for this to work?
            calculateFunction() has to change the vector elements based on their initial value.

            kshegunovK 1 Reply Last reply
            0
            • nulluseN nulluse

              Does the parameter have to be const for this to work?
              calculateFunction() has to change the vector elements based on their initial value.

              kshegunovK Away
              kshegunovK Away
              kshegunov
              Moderators
              wrote on last edited by
              #8

              @nulluse

              Does the parameter have to be const for this to work?
              calculateFunction() has to change the vector elements based on their initial value.

              Yes, but this is not a problem; you return the modified value from the function.

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              1
              • nulluseN Offline
                nulluseN Offline
                nulluse
                wrote on last edited by nulluse
                #9

                Sorry, please bear with me!
                Just out of curiosity, why this can be passed to map() in order to receive an element of QVector<int> and return int

                auto calculateFunction = [calcParameter1, calcParameter2] (const int & element) -> int  {
                    //< Do calculations here
                };
                

                but not this:

                int newForm::calculateFunction(int& i) {
                    //< Do calculations here
                }
                

                What is the principal difference between a new lambda function and a method of the same class newForm as the method where I call map()?

                kshegunovK 1 Reply Last reply
                0
                • nulluseN nulluse

                  Sorry, please bear with me!
                  Just out of curiosity, why this can be passed to map() in order to receive an element of QVector<int> and return int

                  auto calculateFunction = [calcParameter1, calcParameter2] (const int & element) -> int  {
                      //< Do calculations here
                  };
                  

                  but not this:

                  int newForm::calculateFunction(int& i) {
                      //< Do calculations here
                  }
                  

                  What is the principal difference between a new lambda function and a method of the same class newForm as the method where I call map()?

                  kshegunovK Away
                  kshegunovK Away
                  kshegunov
                  Moderators
                  wrote on last edited by
                  #10

                  @nulluse

                  ,Just out of curiosity, why this can be passed to map() in order to receive an element of QVector<int> and return int

                  Because the first one is actually an object, while the second one is a pointer. So defining the lambda as shown above is expanding to a functor, or more concretely something like this:

                  class __some_anonymous_class
                  {
                  public:
                      __some_anonymous_class(int cp1, int cp2)
                          : calcParameter1(cp1), calcParameter2(cp2)
                      {
                      }
                  
                      int operator () (const int & element) const  //< The return type and function parameter you specified go here
                      {
                           // And here's the body you defined.
                      }
                  
                  private:
                      int calcParameter1, calcParameter2;  //< The types depend on the capture of the lambda
                  } calculateFunction; //<< This is the instance of that class that we conveniently named "calculateFunction"
                  

                  Actually, before having C++11 this was the conventional way of doing "the lambda".


                  Now for the second code, you want to pass newForm::calculateFunction but this is not an object, it's not even a function it's a pointer to member. It's of type int (newForm::*)(int &) and to call that particular member you need an object to bind it to. So consider how this works:

                  typedef int (newForm::*newFormCalculate)(int &); //< Just naming the type for convenience.
                  
                  newFormCalculate myMethodPtr = newForm::calculateFunction; //< Assign the address of the method from the class newForm to a pointer variable.
                  
                  // To call that member we need an object, so suppose myForm is our object:
                  newForm myForm;
                  myForm.*myMethodPtr; //< This binds the myForm object and calls the member we have a pointer to.
                  

                  Ultimately, when you pass newForm::calculateFunction to QtConcurrent::map it tries to call the pointer to member for each object of the container. However, since your container contains integers that's simply not possible. It'd mean that the concurrent framework is trying to do this (following the notation from above example):

                  // This is what's happening
                  int elementFromContainer;
                  elementFromContainer.*myMethodPtr; //< Just can't call a method for a variable that's not an object, so compilation fails.
                  

                  I hope that answers your question.
                  Kind regards.

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  2
                  • nulluseN Offline
                    nulluseN Offline
                    nulluse
                    wrote on last edited by
                    #11

                    I will not pretend I understood everything, but did implement the lambda (without const though, as using const int& and returning a result simply did not work - the vector was not updated at the end).

                    So instead of

                    auto calculateFunction = [calcParameter1, calcParameter2] (const int & element) -> int  {
                        int result = 0;
                        //< Do calculations here
                        return result;
                    };
                    

                    I ended up using

                    auto calculateFunction = [calcParameter1, calcParameter2] (int & element) -> int  {
                        int result = 0;
                        //< Do calculations here
                        element = result;
                    };
                    

                    and then it worked.

                    kshegunovK 1 Reply Last reply
                    0
                    • nulluseN nulluse

                      I will not pretend I understood everything, but did implement the lambda (without const though, as using const int& and returning a result simply did not work - the vector was not updated at the end).

                      So instead of

                      auto calculateFunction = [calcParameter1, calcParameter2] (const int & element) -> int  {
                          int result = 0;
                          //< Do calculations here
                          return result;
                      };
                      

                      I ended up using

                      auto calculateFunction = [calcParameter1, calcParameter2] (int & element) -> int  {
                          int result = 0;
                          //< Do calculations here
                          element = result;
                      };
                      

                      and then it worked.

                      kshegunovK Away
                      kshegunovK Away
                      kshegunov
                      Moderators
                      wrote on last edited by
                      #12

                      @nulluse

                      I will not pretend I understood everything

                      Don't worry about it, C++ is riddled with implementation details anyway and to really understand what this low-level stuff does you have to understand quite well how the binary is generated by the compiler and linker, and how the classes/functions/variables are placed in the final binary image and in memory (which also implies a decent knowledge of assembly). While knowing this might be helpful at times it's almost never actually needed in practice.

                      but did implement the lambda (without const though, as using const int& and returning a result simply did not work - the vector was not updated at the end).

                      Yes, that was a mistake on my part thinking about QtConcurrent::mapped, sorry about that. Indeed you can (and should) drop the const modifier of the parameter if you want the sequence to be updated and can simply have your lambda return void.

                      Kind regards.

                      Read and abide by the Qt Code of Conduct

                      1 Reply Last reply
                      1
                      • nulluseN Offline
                        nulluseN Offline
                        nulluse
                        wrote on last edited by
                        #13

                        I thought so much! At some point I need to get my nose off the ground and look at the assembly listings of my code to really understand behind the scenes. That helped me a lot years ago when I was learning C++ using Stroustrup's 1st book and Borland C++ 2.0 compiler. Unfortunately I ended up doing almost nothing but Delphi and C# ever since and C++ was well forgotten by now.

                        Your help and patience with my silly questions is certainly appreciated!

                        kshegunovK 1 Reply Last reply
                        0
                        • nulluseN nulluse

                          I thought so much! At some point I need to get my nose off the ground and look at the assembly listings of my code to really understand behind the scenes. That helped me a lot years ago when I was learning C++ using Stroustrup's 1st book and Borland C++ 2.0 compiler. Unfortunately I ended up doing almost nothing but Delphi and C# ever since and C++ was well forgotten by now.

                          Your help and patience with my silly questions is certainly appreciated!

                          kshegunovK Away
                          kshegunovK Away
                          kshegunov
                          Moderators
                          wrote on last edited by
                          #14

                          @nulluse said:

                          Your help and patience with my silly questions is certainly appreciated!

                          I certainly wouldn't call them silly, this is pretty specific matter and as I said, in practice it mostly doesn't come up.

                          Read and abide by the Qt Code of Conduct

                          1 Reply Last reply
                          1
                          • nulluseN Offline
                            nulluseN Offline
                            nulluse
                            wrote on last edited by
                            #15

                            When I was using Netbeans with GCC under Linux and FreeBSD, the below lambda declared in a descendant of QDialog worked fine:

                            auto func = [ // 96
                            	x0 = widget.spX0->value(), // 97
                            	y0 = widget.spY0->value(),
                            ] (int& iteration) -> int { // 102
                            	return iteration;
                            };
                            

                            widget.spX0 etc are the double spin boxes on the dialog.

                            But now I tried Qt Creator under Win7 and this lambda is throwing a bunch of errors:

                            newform.cpp:97: error: C2143: syntax error : missing ']' before '='
                            newform.cpp:97: error: C3481: 'x0': lambda capture variable not found
                            newform.cpp:102: error: C2059: syntax error : ']'
                            newform.cpp:97: error: C2059: syntax error : '='
                            

                            Do I need to switch the language version to C++11 anywhere?

                            kshegunovK 1 Reply Last reply
                            0
                            • nulluseN nulluse

                              When I was using Netbeans with GCC under Linux and FreeBSD, the below lambda declared in a descendant of QDialog worked fine:

                              auto func = [ // 96
                              	x0 = widget.spX0->value(), // 97
                              	y0 = widget.spY0->value(),
                              ] (int& iteration) -> int { // 102
                              	return iteration;
                              };
                              

                              widget.spX0 etc are the double spin boxes on the dialog.

                              But now I tried Qt Creator under Win7 and this lambda is throwing a bunch of errors:

                              newform.cpp:97: error: C2143: syntax error : missing ']' before '='
                              newform.cpp:97: error: C3481: 'x0': lambda capture variable not found
                              newform.cpp:102: error: C2059: syntax error : ']'
                              newform.cpp:97: error: C2059: syntax error : '='
                              

                              Do I need to switch the language version to C++11 anywhere?

                              kshegunovK Away
                              kshegunovK Away
                              kshegunov
                              Moderators
                              wrote on last edited by
                              #16

                              @nulluse said:

                              Do I need to switch the language version to C++11 anywhere?

                              Depending on the compiler and Qt version, probably, yes. You can add CONFIG += c++11 to your project file and if the compiler supports it it'll be enabled.

                              Read and abide by the Qt Code of Conduct

                              1 Reply Last reply
                              1
                              • nulluseN Offline
                                nulluseN Offline
                                nulluse
                                wrote on last edited by
                                #17

                                That did not help.
                                This is what I just downloaded and installed with Qt 5.6 as the only version checked off in the setup:

                                Qt Creator 3.6.1
                                Based on Qt 5.6.0 (MSVC 2013, 32 bit)
                                Built on Mar 14 2016 09:57:09
                                From revision d502727b2c
                                Copyright 2008-2016 The Qt Company Ltd. All rights reserved.
                                The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                                

                                It has Desktop Qt 5.6.0 MSVC2013 64bit (default) under the Build&Run/Kits
                                Compiler is set to Microsoft Visual C++ Compiler 12.0 (amd64).

                                kshegunovK 1 Reply Last reply
                                0
                                • nulluseN nulluse

                                  That did not help.
                                  This is what I just downloaded and installed with Qt 5.6 as the only version checked off in the setup:

                                  Qt Creator 3.6.1
                                  Based on Qt 5.6.0 (MSVC 2013, 32 bit)
                                  Built on Mar 14 2016 09:57:09
                                  From revision d502727b2c
                                  Copyright 2008-2016 The Qt Company Ltd. All rights reserved.
                                  The program is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
                                  

                                  It has Desktop Qt 5.6.0 MSVC2013 64bit (default) under the Build&Run/Kits
                                  Compiler is set to Microsoft Visual C++ Compiler 12.0 (amd64).

                                  kshegunovK Away
                                  kshegunovK Away
                                  kshegunov
                                  Moderators
                                  wrote on last edited by kshegunov
                                  #18

                                  @nulluse
                                  Hm. It should've worked. As far as I remember VS 2013 should support most of the C++11 features. Could you post the full compile line for the file you're getting the error at?

                                  PS.
                                  Also try declaring the variables for the capture explicitly and see how it goes:

                                  int x0 = widget.spX0->value(), y0 = widget.spY0->value();
                                  auto func = [ x0, y0] (int& iteration) -> int { // 102
                                      return iteration;
                                  };
                                  

                                  Read and abide by the Qt Code of Conduct

                                  1 Reply Last reply
                                  1
                                  • nulluseN Offline
                                    nulluseN Offline
                                    nulluse
                                    wrote on last edited by nulluse
                                    #19
                                    14:41:29: Running steps for project MandelbrotQt...
                                    14:41:29: Configuration unchanged, skipping qmake step.
                                    14:41:29: Starting: "C:\Qt\Tools\QtCreator\bin\jom.exe" 
                                    	C:\Qt\Tools\QtCreator\bin\jom.exe -f Makefile.Debug
                                    	cl -c -nologo -Zc:wchar_t -FS -Zi -MDd -GR -W3 -w34100 -w34189 -w44996 -EHsc /Fddebug\MandelbrotQt.pdb -DUNICODE -DWIN32 -DWIN64 -DQT_QML_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I..\..\MandelbrotQt -I. -IC:\Qt\5.6\msvc2013_64\include -IC:\Qt\5.6\msvc2013_64\include\QtWidgets -IC:\Qt\5.6\msvc2013_64\include\QtGui -IC:\Qt\5.6\msvc2013_64\include\QtANGLE -IC:\Qt\5.6\msvc2013_64\include\QtCore -Idebug -I. -IC:\Qt\5.6\msvc2013_64\mkspecs\win32-msvc2013 -Fodebug\ @C:\Users\user0\AppData\Local\Temp\newform.obj.11028.16.jom
                                    newform.cpp
                                    ..\..\MandelbrotQt\newform.cpp(97) : error C2143: syntax error : missing ']' before '='
                                    ..\..\MandelbrotQt\newform.cpp(97) : error C3481: 'x0': lambda capture variable not found
                                    ..\..\MandelbrotQt\newform.cpp(102) : error C2059: syntax error : ']'
                                    ..\..\MandelbrotQt\newform.cpp(97) : error C2059: syntax error : '='
                                    ..\..\MandelbrotQt\newform.cpp(102) : error C2143: syntax error : missing ';' before '{'
                                    ..\..\MandelbrotQt\newform.cpp(105) : error C2065: 'x0' : undeclared identifier
                                    

                                    Interesting enough, I started re-factoring the project to move the computations out of the GUI class, and my new class compiles fine when all I capture is [this], then refer to the member variables inside the lambda's body.

                                    PS: Your workaround works. I will wrap that with #if defined (Q_OS_WIN) etc.

                                    PPS: But now I am getting lots of linker errors similar to this:

                                    newform.obj:-1: error: LNK2019: unresolved external symbol "__declspec(dllimport) public: __cdecl QtConcurrent::ThreadEngineBase::ThreadEngineBase(void)" (__imp_??0ThreadEngineBase@QtConcurrent@@QEAA@XZ) referenced in function "public: __cdecl QtConcurrent::IterateKernel<int *,void>::IterateKernel<int *,void>(int *,int *)" (??0?$IterateKernel@PEAHX@QtConcurrent@@QEAA@PEAH0@Z)
                                    

                                    Do I need to add linking against a library when working under Windows?

                                    PPPS: This does not make any sense: I am editing the .pro file adding and removing concurrent from QT but still getting this line in the output every time:

                                    15:01:05: Configuration unchanged, skipping qmake step.
                                    
                                    1 Reply Last reply
                                    0
                                    • kshegunovK Away
                                      kshegunovK Away
                                      kshegunov
                                      Moderators
                                      wrote on last edited by kshegunov
                                      #20

                                      @nulluse said:

                                      But now I am getting lots of linker errors similar to this:
                                      Do I need to add linking against a library when working under Windows?

                                      Only to the Qt modules as far as I know. Qt += core gui widgets concurrent etc. It should be working out of the box.

                                      Read and abide by the Qt Code of Conduct

                                      1 Reply Last reply
                                      1
                                      • nulluseN Offline
                                        nulluseN Offline
                                        nulluse
                                        wrote on last edited by
                                        #21

                                        See my last update - adding QT += concurrent does not change anything as the project thinks configuration is not changing.

                                        kshegunovK 1 Reply Last reply
                                        0
                                        • nulluseN nulluse

                                          See my last update - adding QT += concurrent does not change anything as the project thinks configuration is not changing.

                                          kshegunovK Away
                                          kshegunovK Away
                                          kshegunov
                                          Moderators
                                          wrote on last edited by
                                          #22

                                          @nulluse
                                          Sometimes the IDE is not as smart and it doesn't understand the .pro has changed. Run qmake explicitly (somewhere in the build menu) and do a rebuild after that.

                                          Read and abide by the Qt Code of Conduct

                                          1 Reply Last reply
                                          1

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved