Как сериализовать вектор броненосца

Как я могу сериализовать arma::Col? Ниже приведены MWE и вывод ошибок.

MWE:

#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#include <iostream>
#include "armadillo"

namespace mpi = boost::mpi;

struct S
{   
    int i;
    arma::Col<double>::fixed<3> cvector;

    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive& ar, const unsigned int version) 
    {
        ar& i;
        ar& cvector;
    }
};

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

    S s;

    if (world.rank() == 0)
    {
        s.cvector[0] = 2;
        s.cvector[1] = 2;
        world.send(1, 0, s);
    }
    else
    {
        world.recv(0, 0, s);
        std::cout << s.cvector[0] << std::endl;
        std::cout << s.cvector[1] << std::endl;
    }

    return 0;
}

Вывод ошибки (пропуск "требуемого от"):

error: ‘class arma::Col<double>::fixed<3ull>’ has no member named ‘serialize’; did you mean ‘set_size’? t.serialize(ar, file_version);

Изменить: Это сообщение кажется относится к моему вопросу и, к сожалению, на него нет ответа.


person Shibli    schedule 06.10.2016    source источник


Ответы (3)


Настоящая суть проблемы здесь в том, что вы хотите добавить функцию-член serialize() к различным объектам Armadillo, но это не представляется возможным... за исключением того, что благодаря умному использованию препроцессора в Armadillo, это !

Взгляните на Mat_bones.hpp и Col_bones.hpp... вы увидите что-то подобное внутри определений классов Mat и Col:

public:

#ifdef ARMA_EXTRA_COL_PROTO
  #include ARMA_INCFILE_WRAP(ARMA_EXTRA_COL_PROTO)
#endif

Я очень обрадовался, когда нашел это, потому что теперь я могу сделать что-то вроде определения файла с именем Mat_extra_bones.hpp:

//! Add a serialization operator.
template<typename Archive>
void serialize(Archive& ar, const unsigned int version);

а затем Mat_extra_meat.hpp:

// Add a serialization operator.
template<typename eT>
template<typename Archive>
void Mat<eT>::serialize(Archive& ar, const unsigned int /* version */)
{
  using boost::serialization::make_nvp;
  using boost::serialization::make_array;

  const uword old_n_elem = n_elem;

  // This is accurate from Armadillo 3.6.0 onwards.
  // We can't use BOOST_SERIALIZATION_NVP() because of the access::rw() call.
  ar & make_nvp("n_rows", access::rw(n_rows));
  ar & make_nvp("n_cols", access::rw(n_cols));
  ar & make_nvp("n_elem", access::rw(n_elem));
  ar & make_nvp("vec_state", access::rw(vec_state));

  // mem_state will always be 0 on load, so we don't need to save it.
  if (Archive::is_loading::value)
  {
    // Don't free if local memory is being used.
    if (mem_state == 0 && mem != NULL && old_n_elem > arma_config::mat_prealloc)
    {
      memory::release(access::rw(mem));
    }

    access::rw(mem_state) = 0;

    // We also need to allocate the memory we're using.
    init_cold();
  }

  ar & make_array(access::rwp(mem), n_elem);
}

Затем в вашей программе все, что вам нужно сделать, это

#define ARMA_EXTRA_MAT_PROTO mat_extra_bones.hpp
#define ARMA_EXTRA_MAT_MEAT mat_extra_meat.hpp

а функция serialize() будет членом класса Mat. Вы можете легко адаптировать это решение для других типов броненосцев.

На самом деле это именно то, что делает библиотека mlpack (http://www.mlpack.org/), поэтому если вам интересно, вы можете поближе взглянуть на точное решение, которое я там реализовал:

https://github.com/mlpack/mlpack/tree/master/src/mlpack/core/arma_extend

person ryan    schedule 12.10.2016
comment
Я должен добавить, что это было написано для Col, а не Col::fixed, как вы используете. Поэтому может потребоваться небольшая модификация. - person ryan; 12.10.2016
comment
Я знаю, что это довольно старый вопрос... Я нахожу ваше решение очень элегантным, однако у меня возникают некоторые проблемы с его компиляцией, в частности, я получаю сообщение об ошибке boost::сериализация не была объявлена, если я добавляю эти файлы как есть вместо этого, если я включу все необходимые заголовки в Mat_extra_meat.hpp, я получу ошибку /usr/local/include/boost/type_traits/integral_constant.hpp:42:15: error: '::mpl_' не был объявлен с использованием: :mpl_::bool_; Есть ли у вас какие-либо советы о том, как скомпилировать и связать эти файлы? - person mariob6; 16.08.2019
comment
Определенно вы не можете включать заголовки в Mat_extra_meat.hpp, потому что все в этом файле включается непосредственно как часть класса Mat. Так что, может быть, прежде чем включать <armadillo>, нужно включить <boost/serialization.hpp>? - person ryan; 20.08.2019

Поскольку arm::Col::fixed не поддерживает саму сериализацию, вы можете либо написать ее в своем S-классе, либо написать класс, который ее обертывает и сериализует. Я бы рекомендовал второй вариант, так как он позволит вам использовать arm::Col::fixed во всем, что вы хотите сериализовать без повторения.

person UKMonkey    schedule 06.10.2016
comment
Спасибо, я написал то, что вы предложили в новом ответе. - person Shibli; 06.10.2016

Согласно ответу @UKMonkey, я написал рабочий пример. На самом деле для этого случая нет необходимости разбивать serialize на save и load.

#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
//#include <boost/serialization/split_free.hpp>
#include <iostream>
#include "armadillo"

namespace mpi = boost::mpi;

typedef arma::Col<double>::fixed<3> cvector;

//BOOST_SERIALIZATION_SPLIT_FREE(cvector)

namespace boost
{   
    namespace serialization
    {
        /*template<class Archive>
        void save(Archive& ar, const cvector& cv, unsigned int)
        {
            std::cout << "saving" << std::endl;
            ar& cv[0];
            ar& cv[1];
            ar& cv[2];
        }
        template<class Archive>
        void load(Archive& ar, cvector& cv, unsigned int)
        {
            std::cout << "loading" << std::endl;
            ar& cv[0];
            ar& cv[1];
            ar& cv[2];
        }*/      
        template<class Archive>
        inlide void serialize(Archive& ar, cvector& cv, unsigned int)
        {
            ar& cv[0];
            ar& cv[1];
            ar& cv[2];
        }
    }
} 

struct S
{
    int i;
    cvector c;

    friend class boost::serialization::access;

    template<class Archive>
    void serialize(Archive& ar, const unsigned int) 
    {
        ar& i;
        ar& c;
    }
};

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

    S s;

    if (world.rank() == 0)
    {
        s.i = 3;
        s.c[0] = 2.;
        s.c[1] = 4.;
        world.send(1, 0, s);
    }
    else
    {
        world.recv(0, 0, s);
        std::cout << s.i << std::endl;
        std::cout << s.c[0] << std::endl;
        std::cout << s.c[1] << std::endl;
    }

    return 0;
}
person Shibli    schedule 06.10.2016
comment
Вы можете не помещать что-либо в пространство имен boost, если только вы не планируете помещать это в boost... вряд ли это вызовет у вас головную боль, но может. При загрузке в вектор вы можете учитывать, что он может быть в любом состоянии... (может быть любого размера, может иметь данные и т. д.). В остальном - похоже, он должен выполнять эту работу. - person UKMonkey; 06.10.2016
comment
Но save и load определены в boost::serialization. - person Shibli; 06.10.2016
comment
@Shibli: И они вызываются таким образом, чтобы использовать ADL, поэтому они должны находиться в том же пространстве имен, что и тип, с которым они работают. - person ildjarn; 07.10.2016