2016-06-13 9 views
1

Вот «минимальный» нерабочий пример того, что я пытаюсь сделать.Использование полиморфного типа при восстановлении OpenMP

Этот код должен компилироваться с флагом -fopenmp.

#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; 
} 

У меня есть 3 вида классов:

  • действия: A (базовый класс), а также B и C (производный от A)
  • Компьютер: Bar, реализующие параллельно вычисление (через compute()) с использованием OpenMP и выполнение некоторых действий (от B или C), позвонив по телефону operator().
  • Менеджер: который управляет запуском вычислений и устанавливает различные действия.

Сейчас у меня есть эта ошибка infortunate, что говорит мне, что я cannot declare variable 'omp_priv' to be of abstract type 'A'. Это понятно. Мой класс A на самом деле абстрактен, но я хочу, чтобы OpenMP смог понять, что мой атрибут A * action_ от Manager класс - B или C. Но как я мог это сделать?


Странная вещь, что этот код работает, если:

  • я обойти (раскомментируйте рабочую секцию в main()) Manager класса

или если:

  • Я отказываюсь от параллелизма и строки комментариев 34/35 (те, которые начинаются с #pragma)

Однако это не возможные варианты.

Спасибо за ваши ответы.

ответ

1

спользование A& не работает, но с использованием A* делает:

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)(); 
} 

Таким образом, вы можете пропустить весь B_or_C_type шаблон вещь и просто использовать A. В качестве дополнительной сырой альтернативы, вы можете использовать сделать переключатель для всех известных подклассов 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); 
} 

Я не совсем точно знаю, почему это не работает. Кстати, он компилируется с компилятором Intel, но сбой при вызове чистой виртуальной функции.Я бы подумал, что это должно быть сделано:

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

Но это не так. Стандарт кажется мне немного расплывчатым относительно того, какой тип типа s разрешен в typename-list. Мне кажется, что ссылки поддерживаются некорректно.