0

I am trying to learn how to use TBB, so I'm modifying a sample program I found that is designed to compute powers of an array of complex numbers. Originally, it was passing an array into the parallel_for loop, but I am trying to change it so that it passes in a vector. However, I cannot get my code to compile; I get the following error (compiled using g++ -g program_name.cpp -ltbb):

error: passing ‘const std::complex<double>’ as ‘this’ argument
       discards qualifiers [-fpermissive] result[i] = z;

My code is below:

#include <cstdlib>
#include <cmath>
#include <complex>
#include <ctime>
#include <iostream>
#include <iomanip>
#include "tbb/tbb.h"
#include "tbb/blocked_range.h"
#include "tbb/parallel_for.h"
#include "tbb/parallel_for_each.h"
#include "tbb/task_scheduler_init.h"

using namespace std;
using namespace tbb;
typedef complex<double> dcmplx;

dcmplx random_dcmplx ( void )
{
   double e = 2*M_PI*((double) rand())/RAND_MAX;
   dcmplx c(cos(e),sin(e));
   return c;
}

class ComputePowers
{
   vector<dcmplx>  c; // numbers on input
   int d;           // degree
   vector<dcmplx>  result;  // output
   public:
      ComputePowers(vector<dcmplx> x, int deg, vector<dcmplx> y): c(x), d(deg), result(y) { }

      void operator() ( const blocked_range<size_t>& r ) const
      {
         for(int i=r.begin(); i!=r.end(); ++i)
         {
            dcmplx z(1.0,0.0);
            for(int j=0; j < d; j++) {
                z = z*c[i];
            };
            result[i] = z;
         }
      }
};

int main ( int argc, char *argv[] )
{
   int deg = 100;
   int dim = 10;

   vector<dcmplx> r;
   for(int i=0; i<dim; i++)
     r.push_back(random_dcmplx());

   vector<dcmplx> s(dim);

   task_scheduler_init init(task_scheduler_init::automatic);

   parallel_for(blocked_range<size_t>(0,dim),
                ComputePowers(r,deg,s));
   for(int i=0; i<dim; i++)
       cout << scientific << setprecision(4)
       << "x[" << i << "] = ( " << s[i].real()
       << " , " << s[i].imag() << ")\n";
   return 0;
}
Student
  • 3
  • 4
  • In case, you still did not understand the error, in your `operator()` method you are doing `result[I] = z;` . The method is marked as `const` and you are not supposed to modify the data members in methods marked as `const`. – Jagannath Jul 29 '16 at 22:24

1 Answers1

1

You're trying to modify non-mutable member field result in const-qualified operator().

Resolve this discrepancy.

Edit #1: I have already mentioned the two keywords above. Either:

  1. Remove const qualifier from operator():

    void operator() ( const blocked_range<size_t>& r ) { ... }
    
  2. Make result mutable:

    mutable vector<dcmplx> result;
    

Additional erorrs may emerge after applying (although strongly preferred) variant no. 1. No. 2 is just for completeness and is used only in marginal situations.

It indeed results in an error with the 1st variant. This is because tbb::parallel_for takes Func by const&, so it can call only const-qualified member functions on your functor. Why? TBB doesn't want to waste performance by copying large functors (STL passes them by value).

I don't know what's the common practice here, I've never used this library.


Edit #2: Probably all you were missing was that result wasn't a reference:

vector<dcmplx> &result;

Now you'll be able to modify it, even in const-qualified operator(). Modifying a member that gets destructed afterwards wouldn't make sense.

Don't forget to change the constructor's signature to pass y by reference, too.


Off topic issues in your code:

  • Not included <vector> header

  • using namespace bulky_namespace globally

  • not using size_t for i in the for-loop

  • maybe more...

Community
  • 1
  • 1
LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • Thank you very much for your response. I'm new to C++. How do I do this? – Student Jul 29 '16 at 21:49
  • Thanks for your continued response. Does that mean that there's no way to make parallel_for modify a vector? – Student Jul 29 '16 at 22:13
  • @Student This falls into *marginal cases*. `mutable` should work. – LogicStuff Jul 29 '16 at 22:15
  • When I use `mutable` as you suggest, the code compiles and the modifications to the vector happen. However, the vector does not stay modified after the parallel_for loop finishes. – Student Jul 29 '16 at 22:22
  • Oh, I see. `ComputePowers` with its members gets destructed shortly after... Which `std::vector` outside `ComputePowers` should be modified? – LogicStuff Jul 29 '16 at 22:24
  • The `vector` outside that should be modified is `s`. I added in printing code to the original question to show that. – Student Jul 29 '16 at 22:29
  • @Student Edit #2 is definitely your answer. However, I'll keep previous attempts of answering available for future reference. – LogicStuff Jul 29 '16 at 22:29
  • 1
    The whole point of the const requirement is to be a compile-time reminder that `tbb::parallel_for` might copy the functor an arbitrary number of times, depending on the implementation. Declaring it `mutable` just circumvents the reminder :-(. Declare both `result` AND `c` as reference members, so that neither is copied each time tbb::parallel_for copies the functor. Otherwise, the updates won't be seen in the original `result`, and each copying will cost O(N) time to copy an N-element vector. – Arch D. Robison Jul 31 '16 at 21:32
  • 2
    By the way, I strongly recommend learning how to use C++11 lambda expressions with capture-by-reference (i.e. `[&]`) with `tbb::parallel_for`. Doing so makes the functors *much* easier to write. – Arch D. Robison Jul 31 '16 at 21:34