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

这一生的挚爱 提交于 2019-11-26 20:59:29

问题


I have tried providing getters of class A for my non-member serialize() function` since accessing from members is private.

template<typename T>
class A
{
public:
  A(const T& id) : m_id(id) {}
  T& getRef() { return m_id; } // not giving good results
  T  getId()  { return m_id; } // not giving good results
  const T& getRef() const { return m_id; } // not giving good results
private: // I would like to keep it private
  T m_id;
}

namespace boost { namespace serialization {

template<class Archive,typename T>
void serialize(Archive &ar, A &a, const unsigned int version)
{
    // ar &BOOST_SERIALIZATION_NVP(a.m_id); // I would like to avoid that it works if m_id is public
    ar &BOOST_SERIALIZATION_NVP(a.GetRef()); // I want this !
}

}}

// and later I use
std::ofstream ofs("test.xml");
boost::archive::xml_oarchive oa(ofs);
A<int> a(42);
oa << BOOST_SERIALIZATION_NVP(a);

Unfortunately the execution keeps telling me uncaught exception of type boost::archive::xml_archive_exception - Invalid XML tag name when I try to use getters either GetRef()or GetId().
It works well if I access directly to m_id when it is public.

Are there any nice ways of doing so ?


回答1:


  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




回答2:


Just for additional info: In order to get the first solution from sehe working:

You need a forward decleration of the friends method like this:

// Boost
#include <boost/serialization/access.hpp>

class ClassB;

namespace boost{
namespace serialization {
    template <typename Ar> void serialize(Ar&,ClassB&,const unsigned);
}
}

class ClassB: public ClassA{

private:
    template <typename Ar> friend void boost::serialization::serialize(Ar&,ClassA&,const unsigned);
public:
    ClassA();
    virtual ~ClassA();
};

Took me a while to get it working.

Cheers




回答3:


Supplementary Information to sehe's first solution:

The solution requires two-phase lookup and or argument dependent lookup. Unfortunately, MSVC does not yet support this to its full extent.

Compiling this in VS Community 2019 16.1.6 with boost 1.70 results in an obscure error:

Error   C2063    'boost::serialization::serialize': not a function

Even though conformance mode is enabled through the /permissive- flag and the latest language standard /std::c++latest is selected, as described in this MSVC Blog Post.

Adding the typename qualifier to the friend declaration solves the problem:

template <typename Ar, typename U> friend void boost::serialization::serialize(typename Ar&, A<U>&, const unsigned);

Even more interestingly frustratingly:

if class A is not a templated class, then it doesn't work either way, same error as above... Example code: http://coliru.stacked-crooked.com/a/ecfbb39d5975d753



来源:https://stackoverflow.com/questions/30594917/get-private-data-members-for-non-intrusive-boost-serialization-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!