2016-03-30 11 views
1

Я пытаюсь передать класс неизвестного подкласса, но известный базовый класс.Передача MPI неизвестного подкласса с boost :: mpi :: packaging_oarchive и packed_iarchive

Я считаю, что это должно быть возможно с помощью boost::serialization, BOOST_CLASS_EXPORT_GUID и boost::mpi, но я довольно новыми для C++ в общем

Это код, который у меня есть:

#include <boost/mpi.hpp> 
#include <boost/mpi/environment.hpp> 
#include <boost/mpi/communicator.hpp> 
#include <boost/serialization/string.hpp> 
#include <boost/serialization/serialization.hpp> 
#include <boost/serialization/export.hpp> 
#include <boost/serialization/vector.hpp> 
#include <iostream> 

namespace mpi = boost::mpi; 


class Action { 
protected: 
    int start_rank; 
    std::string greeting; 

    Action(std::string greeting) { 
     mpi::communicator world; 
     this->start_rank = world.rank(); 
     this->greeting = greeting; 
    }; 

private: 

    friend class boost::serialization::access; 
    template<class Archive> void serialize(Archive &ar, const unsigned int version) { 
     ar & this->start_rank; 
     ar & this->greeting; 
    }; 

public: 

    Action() = default; 

    void invoke() { 
     mpi::communicator world; 
     std::cout << this->greeting << "! I am process " << world.rank() << " of " << world.size() 
      << ". I was created on " << this->start_rank << "." << std::endl; 
    }; 
}; 


class HelloAction : public Action { 

public: 
    HelloAction() : Action("Hello") {}; 

}; 

class GoodByeAction : public Action { 

public: 
    GoodByeAction() : Action("Good bye") {}; 

}; 

BOOST_CLASS_EXPORT_GUID(Action, "Action"); 
BOOST_CLASS_EXPORT_GUID(HelloAction, "HelloAction"); 
BOOST_CLASS_EXPORT_GUID(GoodByeAction, "GoodByeAction"); 

int main() { 
    mpi::environment env; 
    mpi::communicator world; 

    HelloAction *hello = new HelloAction(); 
    mpi::broadcast(world, hello, 0); 
    hello->invoke(); 

    GoodByeAction *bye = new GoodByeAction(); 
    mpi::broadcast(world, bye, 1); 
    bye->invoke(); 

    world.barrier(); 

    if (world.rank() == 0) { 
     std::cout << "sending unknown action classes!" << std::endl; 
     HelloAction *yup = new HelloAction(); 
     boost::mpi::packed_oarchive oar(world); 
     oar << yup; 
    } 
    else { 
     std::cout << "receiving unknown action classes!" << std::endl; 
     Action *action = NULL; 
     boost::mpi::packed_iarchive iar(world); 
     iar >> action; 
     action->invoke(); 
    } 

    return 0; 
} 

компилирование/работает с :

mpic++ -g -std=c++1y hello.cpp -lboost_serialization -lmpi -lboost_mpi 
mpiexec -np 2 ./a.out 

Это кажется работать нормально:

Hello! I am process 0 of 2. I was created on 0. 
Hello! I am process 1 of 2. I was created on 0. 
Good bye! I am process 1 of 2. I was created on 1. 
Good bye! I am process 0 of 2. I was created on 1. 

... до получения до "отправки/приема классов Неизвестное действие", где я получаю ошибки во время выполнения:

receiving unknown action classes! 
sending unknown action classes! 
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::mpi::exception> >' 
    what(): MPI_Unpack: MPI_ERR_ARG: invalid argument of some other kind 
[machine-name:20194] *** Process received signal *** 
[machine-name:20194] Signal: Aborted (6) 
[machine-name:20194] Signal code: (-6) 
[machine-name:20194] [ 0] /lib/x86_64-linux-gnu/libc.so.6(+0x352f0) [0x7fa685cc22f0] 
[machine-name:20194] [ 1] /lib/x86_64-linux-gnu/libc.so.6(gsignal+0x37) [0x7fa685cc2267] 
[machine-name:20194] [ 2] /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a) [0x7fa685cc3eca] 
[machine-name:20194] [ 3] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(_ZN9__gnu_cxx27__verbose_terminate_handlerEv+0x16d) [0x7fa6862fdb7d] 
[machine-name:20194] [ 4] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8d9c6) [0x7fa6862fb9c6] 
[machine-name:20194] [ 5] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8da11) [0x7fa6862fba11] 
[machine-name:20194] [ 6] /usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x8dc29) [0x7fa6862fbc29] 
[machine-name:20194] [ 7] ./a.out(_ZN5boost15throw_exceptionINS_3mpi9exceptionEEEvRKT_+0x80) [0x41426d] 
[machine-name:20194] [ 8] ./a.out() [0x413802] 
[machine-name:20194] [15] ./a.out() [0x4306e6] 
[machine-name:20194] [16] /usr/lib/x86_64-linux-gnu/libboost_serialization.so.1.58.0(_ZN5boost7archive6detail19basic_iarchive_impl12load_pointerERNS1_14basic_iarchiveERPvPKNS1_25basic_pointer_iserializerEPFS9_RKNS_13serialization18extended_type_infoEE+0x4d) [0x7fa686df5b8d] 
[machine-name:20194] [17] ./a.out() [0x41d08d] 
[machine-name:20194] [23] ./a.out() [0x40d293] 
[machine-name:20194] [24] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7fa685cada40] 
[machine-name:20194] [25] ./a.out() [0x40cfc9] 
[machine-name:20194] *** End of error message *** 
-------------------------------------------------------------------------- 
mpiexec noticed that process rank 1 with PID 20194 on node machine-name exited on signal 6 (Aborted). 
-------------------------------------------------------------------------- 

Вопросы:

  1. Хотя вопрос помечен boost , возможно ли вообще передавать неизвестный подкласс с или без повышения? Я понимаю, что я мог бы иметь сортировку по имени строки и инверсию управляющего парня для создания, но я думал, что это было целью BOOST_CLASS_EXPORT_GUID.
  2. Есть ли способ сделать эту работу тем, что у меня есть?
  3. Есть ли достаточно простая альтернатива (не используя boost), чтобы сделать эту работу обычным ol 'MPI?

ответ

1

Вы совсем близко: во-первых, у вас отсутствуют фактические функции связи для упакованных архивов. Вы пытаетесь извлечь из packed_iarchive, не получив ничего в этом архиве. Например, вы можете использовать в качестве broadcastdescribed:

с упакованными архивами, корень посылает packed_oarchive [...] в то время как другие процессы получают acked_iarchive [...].

Однако, просто добавив вызовы на примере будет экземпляр Action вместо HelloAction. Требуется немного больше усилий:

  • Использование указателей базового класса для не виртуальных классов - не очень хорошая идея. Это может работать в вашем случае, потому что подклассы - это не что иное, как разные конструкторы, но это становится проблематичным для более сложных примеров.
  • Boost ожидает, что вызовы (de) -сериализации будут использовать симметричные типы. Вы не можете сериализовать HelloAction*, а затем unserialize Action*, независимо от того, является ли он виртуальным классом . Если вам необходимо использовать Action* в обоих случаях, вам, конечно же, также придется использовать виртуальные классы, поэтому boost действительно знает тип выполнения. Технически вы можете сериализовать Action* объекту HelloAction, а затем десериализовать Action*, который задает slicedAction-часть оригинала. Опять же, это работает для вашего примера, но это не подходит для более сложных и, следовательно, это не очень хорошая идея.
  • Вы также можете выбрать serialize для подкатегорий .

Собирает все вместе рабочий пример выглядит следующим образом: Примечания: Я добавил немного больше продукции, чтобы убедиться, что объекты создаются с правильными типами.

#include <boost/mpi.hpp> 
#include <boost/mpi/environment.hpp> 
#include <boost/mpi/communicator.hpp> 
#include <boost/serialization/string.hpp> 
#include <boost/serialization/serialization.hpp> 
#include <boost/serialization/export.hpp> 
#include <boost/serialization/vector.hpp> 
#include <iostream> 

namespace mpi = boost::mpi; 


class Action { 
protected: 
    int start_rank; 
    std::string greeting; 

    Action(std::string greeting) { 
     mpi::communicator world; 
     std::cout << world.rank() << ": Creating a Action: " << greeting << std::endl; 
     this->start_rank = world.rank(); 
     this->greeting = greeting; 
    }; 
private: 

    friend class boost::serialization::access; 
    template<class Archive> void serialize(Archive &ar, const unsigned int version) { 
     ar & this->start_rank; 
     ar & this->greeting; 
    }; 

public: 

    virtual ~Action() = default; 

    Action() { 
     mpi::communicator world; 
     std::cout << world.rank() << ": Creating a naked Action" << std::endl; 
    } 

    void invoke() { 
     mpi::communicator world; 
     std::cout << this->greeting << "! I am process " << world.rank() << " of " << world.size() 
      << ". I was created on " << this->start_rank << "." << std::endl; 
    }; 
}; 


class HelloAction : public Action { 

public: 
    HelloAction() : Action("Hello") { 
     mpi::communicator world; 
     std::cout << world.rank() << ": Creating an HelloAction" << std::endl; 
    } 

    template<typename Archive> 
    void serialize(Archive & ar, const unsigned int file_version) { 
     ar & boost::serialization::base_object<Action>(*this); 
    } 
}; 

class GoodByeAction : public Action { 

public: 
    GoodByeAction() : Action("Good bye") { 
     mpi::communicator world; 
     std::cout << world.rank() << ": Creating an GoodByeAction" << std::endl; 
    } 

    template<typename Archive> 
    void serialize(Archive & ar, const unsigned int file_version) { 
     ar & boost::serialization::base_object<Action>(*this); 
    } 
}; 

// It is totally fine to use the EXPORT (without GUID) for MPI 
BOOST_CLASS_EXPORT(GoodByeAction) 
BOOST_CLASS_EXPORT(HelloAction) 

int main() { 
    mpi::environment env; 
    mpi::communicator world; 

    HelloAction *hello = new HelloAction(); 
    mpi::broadcast(world, hello, 0); 
    hello->invoke(); 

    GoodByeAction *bye = new GoodByeAction(); 
    mpi::broadcast(world, bye, 1); 
    bye->invoke(); 

    world.barrier(); 

    if (world.rank() == 0) { 
     std::cout << "sending unknown action classes!" << std::endl; 
     Action *yup = new HelloAction(); 
     boost::mpi::packed_oarchive oar(world); 
     oar << yup; 
     mpi::broadcast(world, oar, 0); 
    } 
    else { 
     std::cout << "receiving unknown action classes!" << std::endl; 
     boost::mpi::packed_iarchive iar(world); 
     mpi::broadcast(world, iar, 0); 
     Action *action = nullptr; 
     iar >> action; 
     std::cout << "RTTI: " << typeid(*action).name() << std::endl; 
     action->invoke(); 
    } 

    return 0; 
} 

Относительно простой старый MPI: Похоже, изобретая колесо для меня - конечно, возможно, но не обязательно желательно.

Я не могу найти определенное заявление об этом в документации. Это основано на вещи идут ужасно неправильно, если вы попробуете иначе. Я был бы признателен за комментарий/лучший ответ по этому поводу.

+0

спасибо за ответ, по какой-то причине я думал, что, поскольку я инициализировал 'упакованный_архив' 'с' миром', сообщение было каким-то образом обработано (что было глупо, есть больше, чем просто '.bcast'!). Что касается вашего вопроса о виртуальном подклассе, я нашел это, но опять же, я довольно новичок в C++, поэтому я не уверен, что понимаю это: http://www.boost.org/doc/libs/1_60_0/libs /serialization/doc/serialization.html (искать 'output_one') – SimplyKnownAsG

+1

Да, документация позволяет сериализовать указатели не виртуального базового класса. Но это только сериализует нарезанный базовый класс-часть объекта. Взгляните на различные ответы на [этот вопрос] (http://stackoverflow.com/q/274626/620382), чтобы понять, почему нарезка является опасной. Я также уточнил свой ответ. То, что я до сих пор не читаю из документации, - это то, что я пометил 1 в своем ответе – Zulan

+0

, потому что по какой-то причине у меня создалось впечатление, что каким-то образом, когда происходит deserilaization, он будет создавать правильный подкласс и только переназначить действие указателя 'указать на любой экземпляр, который он создал, но я думаю, это то, что я получаю, чтобы выяснить, как это сделать! – SimplyKnownAsG