Повысить сериализацию с помощью указателей и конструктора не по умолчанию - PullRequest
6 голосов
/ 06 марта 2012

Как бы вы сериализовали / десериализовали этот класс, используя boost :: serialization?

#include <vector>

struct Foo {
    struct Bar {
        std::vector<int> * data; // Must point to Foo::data

        Bar( std::vector<int> * d ) : data(d) { }
    };

    std::vector<int> data;
    std::vector<Bar> elements;

    Foo() {
        // do very time consuming calculation to populate "data" and "elements"
    }
};

Конструктор в Foo не должен выполняться, когда возражение загружается из сериализованных данных, но если объект создан по умолчанию, конструктор должен быть оценен.

Можно добавить конструктор по умолчанию в Bar, но после сериализации Foo :: Bar :: data должна указывать на Foo :: data.

РЕДАКТИРОВАТЬ: Ниже приводится неработающая реализация моей попытки

Это моя попытка, основанная на подсказках @Matthieu. Проблема в том, что когда я десериализую Foo, у меня нет элементов в Foo :: data и Foo :: elements.

struct Foo {
    struct Bar {
        std::vector<int> * data;

        Bar( ) : data( 0 ) { }
        Bar( std::vector<int> * d ) : data(d) { }

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

    std::vector<int> data;
    std::vector<Bar> elements;

    Foo() {
        std::cerr << "Running default constructor" << std::endl;
        data.push_back(1);
        data.push_back(2);
        data.push_back(3);
        data.push_back(4);
        data.push_back(5);
        elements.push_back( Bar( &data ) );
        elements.push_back( Bar( &data ) );
        elements.push_back( Bar( &data ) );
    }

    template<class Archive>
    Foo( Archive & ar ) {
        ar >> data; // is this corrent?
        ar >> elements;
    }

private:
    BOOST_SERIALIZATION_SPLIT_MEMBER();
    friend class boost::serialization::access;

    template<class Archive>
    void save(Archive & ar, const unsigned int version) const {
        const std::vector<int> * data_ptr = &data;

        // should data be seriliazed as pointer...
        // it is used as a pointer in Bar
        ar << data_ptr;
        ar << elements;
    }
};

int main(int argc, const char *argv[])
{
#if 0
    // serialize
    Foo foo;
    boost::archive::text_oarchive oar(std::cout);
    oar << foo;

#else
    // deserialize
    boost::archive::text_iarchive oar(std::cin);
    Foo foo(oar);

#endif
    std::cerr << foo.data.size() << std::endl;
    std::cerr << foo.elements.size() << std::endl;

    std::cerr << (&foo.data) << std::endl;
    for( const auto& a : foo.data )
        std::cerr << a << " ";
    std::cerr << std::endl;

    for( const auto& a : foo.elements)
        std::cerr << a.data << " ";
    std::cerr << std::endl;

    return 0;
}

1 Ответ

2 голосов
/ 06 марта 2012

В документации есть раздел, в котором описывается, как (де) сериализовать классы с конструкторами не по умолчанию. Смотрите здесь .

По сути, вы должны реализовать две функции с именами save_construct_data и load_construct_data в пространстве имен boost::serialization, чтобы записывать и считывать данные, используемые для создания экземпляров вашего класса. Затем можно вызвать конструктор не по умолчанию Foo из функции load_construct_data с параметрами, необходимыми для восстановления объекта Foo.


Вот рабочий пример, основанный на вашем обновленном коде:

Обратите внимание, что я использовал shared_ptr, чтобы уточнить, что член data, сериализованный Foo и Bar, ссылается на одно и то же.

#include <vector>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <sstream>

struct Foo {
    struct Bar {
        boost::shared_ptr< std::vector<int> > data; // Must point to Foo::data

        Bar( boost::shared_ptr< std::vector<int> > d ) : data(d) { }

        template<class Archive>
        void serialize(Archive & ar, const unsigned int version)
        {
          // ** note that this is empty **
        }
    };

    boost::shared_ptr< std::vector<int> > data;
    std::vector<Bar> elements;

    Foo() : data( new std::vector<int>() ) {
        std::cerr << "Running default constructor" << std::endl;
        data->push_back(1);
        data->push_back(2);
        data->push_back(3);
        data->push_back(4);
        data->push_back(5);
        elements.push_back( Bar( data ) );
        elements.push_back( Bar( data ) );
        elements.push_back( Bar( data ) );
    }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
      // ** note that this is empty **
    }

    Foo(
      boost::shared_ptr< std::vector<int> > const & data_,
      std::vector<Bar> const & elements_ ) : data( data_ ), elements( elements_ )
    {
        std::cout << "cheap construction" << std::endl;
    }
};

namespace boost { namespace serialization {

template<class Archive>
inline void save_construct_data(
    Archive & ar, const Foo * foo, const unsigned int file_version
){
    ar << foo->data << foo->elements;
}

template<class Archive>
inline void load_construct_data(
    Archive & ar, Foo * foo, const unsigned int file_version
){
    boost::shared_ptr< std::vector<int> > data;
    std::vector<Foo::Bar> elements;

    ar >> data >> elements;

    ::new(foo)Foo(data, elements);
}

template<class Archive>
inline void save_construct_data(
    Archive & ar, const Foo::Bar * bar, const unsigned int file_version
){
    ar << bar->data;
}

template<class Archive>
inline void load_construct_data(
    Archive & ar, Foo::Bar * bar, const unsigned int file_version
){
    boost::shared_ptr< std::vector<int> > data;

    ar >> data;

    ::new(bar)Foo::Bar(data);
}

}}

int main()
{
  std::stringstream ss;

  {
    boost::scoped_ptr< Foo > foo( new Foo() );

    std::cout << "size before serialization is: " << foo->data->size() << std::endl;

    boost::archive::text_oarchive oa(ss);
    oa << foo;
  }

  {
    boost::scoped_ptr< Foo > foo;

    boost::archive::text_iarchive is(ss);
    is >> foo;

    std::cout << "size after deserialization is: " << foo->data->size() << std::endl;
  }

  return 0;
}
...