2012-03-06 13 views
6

¿Cómo serializar/deserializar esta clase usando boost :: serialization?Boost serialización con punteros y constructor no predeterminado

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

El constructor en Foo no debe ejecutarse cuando el opuesto está cargado partir de los datos serializados, pero si el objeto es predeterminado construyó el constructor debe ser evaluado.

Está bien agregar un constructor predeterminado a Bar, pero después de la serialización, los datos Foo :: Bar :: deben apuntar a Foo :: data.

EDIT: Lo que sigue es una aplicación que no funciona de mi intento

Este es mi intento basado en los consejos de @Matthieu. El problema es que cuando deserializo Foo, no obtengo elementos en Foo :: data y 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; 
} 

Respuesta

2

Hay una sección en la documentación que describe cómo (de) serializar clases con constructores no predeterminados. Ver here.

Básicamente, debe implementar dos funciones llamadas save_construct_data y load_construct_data en el espacio de nombres boost::serialization para escribir y leer los datos utilizados para construir instancias de su clase. A continuación, puede llamar a un constructor no predeterminado de Foo desde la función load_construct_data con los parámetros necesarios para reconstruir un objeto Foo.


Aquí es un ejemplo de trabajo basado en el código de actualización:

Nota que he utilizado shared_ptr 's para aclarar que el miembro data serializado por Foo y Bar se hace referencia a la misma cosa.

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

Sí, he visto esto. ¿Pero cómo escribirías la sobrecarga de carga? El constructor predeterminado de Foo no debe ser invocado. – Allan

+0

@Allan: deberá agregar un constructor específico para la deserialización. Por ejemplo, un constructor que toma un archivo de impulso en el argumento. –

+0

@Matthieu Ahh .. tan simple como esto, gracias. ¿Qué pasa con Foo :: data, debería serializarlo como un puntero, y luego hacer un intercambio al deserializar? – Allan

Cuestiones relacionadas