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 :
A
(base class), and B
and C
(derived from A
)Bar
that realize parallel computation (through compute()
function) using OpenMP and do some actions (from a B
or C
class) by calling operator()
.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 :
Manager
class (uncomment working section in main()
)or if :
#pragma
)However, these are not conceivable options.
Thanks for your answers.
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.