Get Private Data Members for Non Intrusive Boost Serialization C++

Get private data members for non intrusive boost serialization C++

  1. You can use good old-fashioned friends:

    Live On Coliru

    template <typename T>
    class A {
    public:
    A(const T &id) : m_id(id) {}
    private:
    template <typename Ar, typename U> friend void boost::serialization::serialize(Ar&,A<U>&,const unsigned);
    T m_id;
    };

    namespace boost {
    namespace serialization {
    template <class Archive, typename T>
    void serialize(Archive &ar, A<T> &a, const unsigned int)
    {
    ar & BOOST_SERIALIZATION_NVP(a.m_id);
    }
    }
    }

  2. You can use the getRef() approach. This

    • requires no friends (less intrusive)
    • requires make_nvp (because you can't use a.getRef() as an XML element name

    Sadly, having the reference getter break encapsulation in a horrific way. I'd personally prefer to have m_id public in the first place, instead.

    Live On Coliru

    template <typename T>
    class A {
    public:
    A(const T &id) : m_id(id) {}

    T& getRef() { return m_id; }
    T const& getRef() const { return m_id; }
    private:
    T m_id;
    };

    namespace boost {
    namespace serialization {
    template <class Archive, typename T>
    void serialize(Archive &ar, A<T> &a, const unsigned int)
    {
    ar & boost::serialization::make_nvp("m_id", a.getRef());
    }
    }
    }

    Bonus points:

  3. You can use a 'pimpl' style struct. You can forward declare a struct inside A<>:

    template <typename T>
    class A {
    public:
    struct access;

    A(const T &id) : m_id(id) {}
    private:
    T m_id;
    };

    That's less intrusive than the getRef() approach which simply breaks encapsulation all the way. Now, you can hide the private access inside this class:

    namespace boost {
    namespace serialization {
    template <class Archive, typename T>
    void serialize(Archive &ar, A<T> &a, const unsigned int version)
    {
    A<T>::access::serialize(ar, a, version);
    }
    }
    }

    Of course you still need to implement it, but this can be done in a separate header and doesn't influence class A<> (or any of its specializations) at all:

    template <typename T>
    struct A<T>::access {
    template <class Archive>
    static void serialize(Archive &ar, A<T> &a, const unsigned int) {
    ar & BOOST_SERIALIZATION_NVP(a.m_id);
    }
    };

    See it Live On Coliru as well

boost serialization access to protected data

Like any other non-member function, your serialize function can only access the public members of NetElement. If, as is often the case, the public interface doesn't expose enough state to serialize the object, then you'll need to make the serialize function a member.

In this case, though, the state is protected, so you could get to it using an "accessor" class derived from NetElement:

class NetElementAccessor : private NetElement
{
public:
explicit NetElementAccessor(const NetElement &e) : NetElement(e) {}

using NetElement::nelements;
using NetElement::ids;
};

template<class Archive>
void serialize(Archive & ar, NetElement& element, const unsigned int version=1)
{
NetElementAccessor accessor(element);
ar & accessor.nelements & accessor.ids;
}

The downside is that this copies the object before serialising it.

Why does boost::serialize not work despite everything seeming right? (unregistered class)

This is a partial answer as it doesn't explain exactly why it failed. I have managed to solve the problem by compiling the program as a single program instead of a bunch of libraries that are then statically linked together, which is how I thought I had to do it with the build system I was using since the documentation that was available online for the system was terse and when I put together the makefiles, I wasn't sure exactly how to do it. I suspect it has something to do with Boost's trouble dealing with this kind of code in libraries.

Does the non-intrusive method for serialising a class in Boost::Serialise make the class serializable?

While this method provides a function 'serialise', which can be used
to serialize the 'gps_position' class, I am not sure whether the class
is itself serializable (ie. acts like a primitive in terms of
serializing/deserializing) once this function has been created, or if
I have to use the intrusive method for that...

Yes the class will be serializable through the normal method of using the '&' operator with a boost.serialization archive, regardless of which method you use.

That is, if I have another class which contains, say, a vector of
'gps_position' instances, will Boost know to look for the overloaded
'serialize' function in the Boost::serialization namespace matching a
'gps_position' parameter when I try to serialize the parent class? Or
would it only look explicitly for a 'serialize' method of the class
(which it wouldn't find in this instance)?

You will need to also provide a serialization function for the parent class. In which you will serialize each member of the class in much the same way as you will have done the child class.

P.S I'm reluctant to "just try it" because I don't know what failure
should look like (will Boost just serialize "something"?)...

Just trying it, is the best possible way you could have learned. It's most likely that this is how anyone else looking to answer this question will have learnt after a brief look at the docs.

Here's a very quick mockup of both the intrusive and non-instrusive versions:

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <boost/serialization/access.hpp>
#include <boost/serialization/string.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

// child class serialized using intrusive

struct child
{
std::string name;

child() = default;

explicit child(const std::string& name)
: name(name)
{ }

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

struct parent
{
std::vector<child> children;

parent() = default;

explicit parent(const std::vector<child>& children)
: children(children)
{ }
};

// parent class serialized using non-instrusive

namespace boost {
namespace serialization {

template <class Archive>
void serialize(Archive& ar, parent& p, const unsigned int /* version */)
{
ar & p.children;
}

}
}

int main()
{
parent p1 {{child("one"), child("two"), child("three")}};

std::stringstream ss;
boost::archive::text_oarchive oa(ss);
oa << p1;

parent p2;
boost::archive::text_iarchive ia(ss);
ia >> p2;

for (auto& child : p2.children) {
std::cout << child.name << "\n";
}
}

Boost - class has no member named ‘serialize’ (abstract class)?

I think the key here is "when I'm using its inherited classes elsewhere". Correct me (and your question, please) if I'm wrong, but this suggests that you are getting the compile error while compiling a source file other than neuron.cpp.

This makes sense, given what the compiler has to work with. You might have noticed that changes to one source file tend to not require re-compiling other source files. So adding something -- like an overload of serialize() -- to neuron.cpp does not change how other translation units are compiled. (It can change how everything is linked together in the end, but we're not there yet.) If another translation unit tries to serialize Neuron, the stuff in neuron.cpp does not matter. The compiler is not aware of an appropriate overload of serialize(), so serializing Neuron in another source file results in intrusive serialization. That is, the compiler will look for a member function of Neuron called serialize().

In order for your overload of serialize() to affect how other translation units are compiled, it needs to be declared in a header file.

Since you cannot put Boost stuff in neuron.h, you might have to create a new header file, say neuron_boost.h. This file would #include "neuron.h" then provide the declarations needed for Boost serialization. Source files that serialize descendants of Neuron would include neuron_boost.h while other source files could continue to include the original neuron.h.

boost::serialization of boost::optional of type with private default constructor

I have not found any better solution than to add the following friendship declaration to my class A, in addition to the existing friend class boost::serialization::access declaration:

template<class Archive, class T>
friend void boost::serialization::load(
Archive & ar, boost::optional<T> & t, const unsigned int version);

Why does an non-intrusive serialization add a 5 byte zero prefix?

So, you'd want to deserialize a previously serialized own_string as if it were a std::string.

From boost(1.65.1) doc:

By default, for each class serialized, class information is written to the archive. This information includes version number, implementation level and tracking behavior. This is necessary so that the archive can be correctly deserialized even if a subsequent version of the program changes some of the current trait values for a class. The space overhead for this data is minimal. There is a little bit of runtime overhead since each class has to be checked to see if it has already had its class information included in the archive. In some cases, even this might be considered too much. This extra overhead can be eliminated by setting the implementation level class trait to: boost::serialization::object_serializable.

Now, probably(*) this is the default for standard classes. In fact, adding

BOOST_CLASS_IMPLEMENTATION(own_string, boost::serialization::object_serializable)

at global scope makes test_X_string results in the same bytes. This should explain the observed extra bytes difference.

That said, I failed to find any specific guarantee concerning standard classes serialization traits (others may know better than me).

(*) actually the section about portability of traits settings mentions that:

Another way to avoid this problem is to assign serialization traits to all specializations of the template my_wrapper for all primitive types so that class information is never saved. This is what has been done for our implementation of serializations for STL collections

so this may give you enough confidence that standard collections (hence including std::string) will give the same bytes in this case.



Related Topics



Leave a reply



Submit