Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boost: Non intrusively serialize a class in separate load/save functions

I am currently working on a software project that requires object persistence as part of its implementation. The boost serialization library seemed to fit the job perfectly at first glance, but now that I've tried to use it I am seriously starting to question its overall design.

The library wants the user to define a serialize method for each class the user wants to serialize.

class Object
{
private:
    int member;
public:
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & member;
    }
};

This is problematic if the class in question has member objects which are part of other third party libraries and APIs. Even though the ones that I happen to use are all available under the zlib license, modifying library header just feels wrong.

But the developers have thought of that and provided a non intrusive version that allows to add serialization to classes without ever modifying them. Brilliant.

template<class Archive>
void serialize(Archive & ar, Object& o, const unsigned int version)
{
    ar & o.member;
}

But alas, this doesn't quite work since the member is private and can't be accsesed from outside the class. Thankfully the hypothetical class Object provides both, getter and setters for its encapsulated member. However we now need to split serialize into separate save and load functions, thankfully boost allows this too. Unfortunately it seems as if this split is only possible if the user is using the intrusive method. In order to tell boost to split the function the official documentation refers to this macro.

BOOST_SERIALIZATION_SPLIT_MEMBER()

Which translates to:

template<class Archive>                                          
void serialize(Archive &ar, const unsigned int file_version)
{                                                               
    boost::serialization::split_member(ar, *this, file_version); 
}

As you can see, this requires to be placed inside of the class which is exactly what I am trying to avoid in the first place.

Digging around the boost mailing list I have come across this solution:

template <class Archive>
void serialize(Archive & ar, Object& o, const unsigned int version)
{
    boost::serialization::split_free(ar, o, version);
} 

Now everything seems like it's coming together at last, but my compiler decided differently and printed this error message:

error: no matching function for call to 'load(boost::archive::text_iarchive&, Object&, const boost::serialization::version_type&)'

error: no matching function for call to 'save(boost::archive::text_oarchive&, const Object&, const boost::serialization::version_type&)'

split_free.hpp:45:9: note:   cannot convert 't' (type 'const Object') to type 'const boost_132::detail::shared_count&'

What exactly am I doing wrong here?

like image 869
Byzantian Avatar asked Jan 03 '14 00:01

Byzantian


1 Answers

When you use boost::serialization::split_free() you have to provide the splitted load() and save() methods, that's what the compiler is complaining about.

Using your example and assuming that Member is the external object that you can't modify, implement the serialisation as follows:

// outside of any namespace
BOOST_SERIALIZATION_SPLIT_FREE(Member)

namespace boost { namespace serialization {
template<class Archive>
void save(Archive& ar, const Member& m, unsigned int) {
    ...
}
template<class Archive>
void load(Archive& ar, Member& m, unsigned int) {
    ...
}
}} // namespace boost::serialization

class Object {
private:
    Member member;
public:
    template<class Archive>
    void serialize(Archive& ar, const unsigned int) {
        ar & member;
    }
};
like image 162
mockinterface Avatar answered Oct 09 '22 10:10

mockinterface