Search code examples
c++inheritancepolymorphismopenmpreduction

Using polymorphic type in OpenMP reduction


Here is a "minimal" non-working example of what I'm trying to do.

This code is to compile with -fopenmp flag.

#include <omp.h>
#include <iostream>

class A {
public:
    virtual void operator() () = 0 ;

    void combine(const A & rhs) {
        // do reduction stuff
        std::cout << "Combine thread : " << omp_get_thread_num() << std::endl;
    }
};

class B : public A {
public:
    void operator() () {
        // do some B specific stuff
        std::cout << "B " ;
    }
} ;

class C: public A {
public:
    void operator() () {
        // do some C specific stuff
        std::cout << "C " ;
    }
} ;

class Computer {
public:
    template<typename B_or_C_type>
    void compute( B_or_C_type & action ) {
        #pragma omp declare reduction (combine_actions : B_or_C_type  : omp_out.combine(omp_in) ) initializer ( omp_priv(omp_orig) )
        #pragma omp parallel for schedule(dynamic) reduction(combine_actions : action )
        for( unsigned i =  0; i < 100; ++i ) {
            // do long computation
            action() ;
        }
        std::cout << std::endl;
    }
} ;

class Manager {
public:
    Manager(Computer * computer) : computer_(computer), action_(NULL)
    {}

    template<typename B_or_C_type>
    void set_action(B_or_C_type * action)
    {
        action_ = action ;
    }

    void run()
    {
        computer_->compute(*action_) ;
    }

private:
    Computer * computer_ ;
    A * action_ ;
} ;


int main() {
    Computer computer;
    B b ;
    C c ;

    // Not working
    Manager manager(&computer) ;
    manager.set_action(&b) ;
    manager.run() ;
    manager.set_action(&c) ;
    manager.run() ;

    //Working
    // computer.compute(b) ;
    // computer.compute(c) ;

    return 0;
}

I have 3 types of classes :

  • Actions : A (base class), and B and C (derived from A)
  • Computer : Bar that realize parallel computation (through compute() function) using OpenMP and do some actions (from a B or C class) by calling operator().
  • Manager : that manages the launching of computation and sets the differents actions.

Right now I have this infortunate error that tells me that I cannot declare variable 'omp_priv' to be of abstract type 'A'. This is well understandable. My A class is in fact abstract, but I wish OpenMP was able to understand that my A * action_ attribute from Manager class is B or C type. But how could I do that ?


The weird thing is that this code works if :

  • I bypass the Manager class (uncomment working section in main())

or if :

  • I renounce to parallelism and comment line 34 / 35 (the ones that starts with #pragma)

However, these are not conceivable options.

Thanks for your answers.


Solution

  • Using an A& doesn't work, but using an A* does:

    B_or_C_type * action_ptr = &action;
    #pragma omp declare reduction (combine_actions : B_or_C_type* : omp_out->combine(*omp_in) ) initializer ( omp_priv(omp_orig) )
    #pragma omp parallel for schedule(dynamic) reduction(combine_actions : action_ptr )
    for( unsigned i =  0; i < 100; ++i ) {
        // do long computation
        (*action_ptr)();
    }
    

    That way you can skip the whole B_or_C_type template thing and just use A. As a more crude alternative, you could utilize make a switch for all known subclasses of A:

    void compute( A & action ) {
        B * pb = dynamic_cast<B*>( &action );
        if ( pb ) compute( *pb );
        C * pc = dynamic_cast<C*>( &action );
        if ( pc ) compute( *pc );
    }
    

    I'm not quite exactly sure why this doesn't work. By the way it compiles with the Intel compiler, but crashes with a pure virtual function call. I would have thought this should do:

    #pragma omp declare reduction (combine_actions : B_or_C_type& : omp_out->combine(*omp_in) ) initializer ( omp_priv(omp_orig) )
    

    But it doesn't. The standard seems a bit vague to me as to what kind of type names are allowed in the typename-list. It would seem to me that references are just not correctly supported.