C++ Boost serialization with pointer vs value

Viewed 62

I recently came across something really puzzling with regards to serialization using C++ Boost library. As far I as searched for it, I could not find anything relevant information. So I was thinking that maybe somebody can enlighten me... or at least this will be referenced and may become useful to anyone later.

In essence : in C++, serializing an object with Boost serialization library does not produce the same resulting file if you serialize using a pointer or using a value. Here is a snippet of code to reproduce it

#include <string>
#include <fstream>
#include <boost/archive/text_oarchive.hpp>      // boost::archive::text_oarchive
#include <boost/archive/text_iarchive.hpp>      // boost::archive::text_iarchive

// we are getting into classics here
class animal
{
    public:
    
        animal() = default;
        
        animal(int legs) 
            : legs_(legs) 
        { ; }
        
        int legs() const
        { return legs_; }

    private:
        friend class boost::serialization::access;

        template <typename Archive>
        void serialize(Archive &ar, const unsigned int version)
        {   if(version < 1)
            { ; }
            ar & legs_;
        }

        int legs_;
} ;

// saves using pointer
void save_ptr(const std::string& path,
              const animal* animal)
{   
    std::ofstream f_out(path) ;
    boost::archive::text_oarchive arch_out(f_out) ;
    
    arch_out << animal ;
    f_out.close() ;
}


// save using value
void save_val(const std::string& path,
              animal animal)
{   
    std::ofstream f_out(path) ;
    boost::archive::text_oarchive arch_out(f_out) ;
    
    arch_out << animal ;
    f_out.close() ;
}



int main()
{
    std::string path_out_val("val.animal") ;
    std::string path_out_ptr("ptr.animal") ;

    animal animal ;

    save_val(path_out_val,  animal) ;
    save_ptr(path_out_ptr, &animal) ;

    return 0 ;
}

On my machine, both resulting files are different.

val.animal

22 serialization::archive 19 1 0

ptr.animal

22 serialization::archive 19 0 1 0

There is an extra 0 in the ptr.animal file.

Consequently, you can get into trouble when trying to reload the data from the files if you mix value and pointer serialization and de-serialization. This issue actually kept me busy a few days...

I was wondering whether there was an explanation for this? Or maybe am I simply doing things terribly wrong here?

1 Answers

Yup. You were doing it terribly wrong.

Pointers aren't values. They need dynamic allocation, and they need object tracking.

The actions required for (de)serialization are a superset of value serialization. In a sense, T* is serialized as *T (if not nullptr) and additional meta-data to handle the pointer and any dynamic conversions.

As a side-note, don't close your file streams before the archive is completed. Sadly, the only way I know to enforce it is to make the destructor run. Simply remove the redundant close:

// saves using pointer
void save_ptr(const std::string &path, const animal *animal) {
  std::ofstream f_out(path);
  boost::archive::text_oarchive arch_out(f_out);

  arch_out << animal;
}

// save using value
void save_val(const std::string &path, animal animal) {
  std::ofstream f_out(path);
  boost::archive::text_oarchive arch_out(f_out);

  arch_out << animal;
}
Related