Improve serialization with non-default pointers and constructor

How would you serialize / deserialize this class with 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" } }; 

The constructor in Foo should not be executed when the object object is loaded from serialized data, but if the object is constructed by default, the constructor must be evaluated.

You can add a default constructor to Bar, but after serialization, the Foo :: Bar :: data should point to Foo :: data.

EDIT: The following is a broken implementation of my attempt

This is my attempt based on tips from @Matthieu. The problem is that when I deserialize Foo, I don't get any elements in Foo :: data and 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; } 
+6
source share
1 answer

The documentation contains a section that describes how to (de) serialize classes with non-default constructors. See here .

Basically, you should implement two functions called save_construct_data and load_construct_data in the boost::serialization namespace to write and read data used to instantiate your class. Then you can call the non-default Foo constructor from the load_construct_data function with the parameters needed to restore the Foo object.


Here is an example based on the updated code:

Note that I used shared_ptr to make it clear that the data member serialized by Foo and Bar refers to the same thing.

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

Source: https://habr.com/ru/post/910077/


All Articles