Получить личные данные членов для не навязчивого повышения сериализации C++
Я попытался обеспечить получателей класса A
для моего не члена serialize()
function`, так как доступ от членов является приватным.
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);
К сожалению, исполнение продолжает говорить мне uncaught exception of type boost::archive::xml_archive_exception - Invalid XML tag name
когда я пытаюсь использовать геттеры либо GetRef()
или же GetId()
,
Это работает хорошо, если я получаю доступ напрямую m_id
когда это публично.
Есть ли хорошие способы сделать это?
2 ответа
Вы можете использовать хороших старомодных друзей:
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); } } }
Вы можете использовать
getRef()
подход. это- не требует друзей (менее навязчиво)
- требует
make_nvp
(потому что вы не можете использоватьa.getRef()
как имя элемента XML
К сожалению, наличие эталонного геттера разрушает инкапсуляцию ужасным образом. Я лично предпочел бы иметь
m_id
Публика в первую очередь вместо.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()); } } }
Бонусные очки:
Вы можете использовать структуру стиля "pimpl". Вы можете переслать объявить структуру внутри
A<>
:template <typename T> class A { public: struct access; A(const T &id) : m_id(id) {} private: T m_id; };
Это менее навязчиво, чем
getRef()
подход, который просто нарушает инкапсуляцию полностью. Теперь вы можете скрыть закрытый доступ внутри этого класса: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); } } }
Конечно, вам все еще нужно это реализовать, но это можно сделать в отдельном заголовке, и это никак не повлияет на класс A<> (или любую из его специализаций):
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); } };
Посмотри это в прямом эфире на Coliru
Просто для дополнительной информации: Чтобы получить первое решение от sehe working:
Вам нужно предварительное объявление метода друзей, например:
// 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();
};
Мне понадобилось время, чтобы заставить его работать.
ура
Дополнительная информация к первому решению sehe:
Решение требует двухэтапного поиска или поиска, зависимого от аргументов. К сожалению, MSVC еще не поддерживает это в полной мере.
Компиляция этого в VS Community 2019 16.1.6 с boost 1.70 приводит к неясной ошибке:
Error C2063 'boost::serialization::serialize': not a function
Несмотря на то, что режим соответствия включен с помощью флага /permissive- и выбран последний стандарт языка /std:: C++latest, как описано в этой публикации блога MSVC.
Добавление квалификатора typename в объявление друга решает проблему:
template <typename Ar, typename U> friend void boost::serialization::serialize(typename Ar&, A<U>&, const unsigned);
Еще более интересно удручающе:
если класс A не является шаблонным классом, то он в любом случае не работает, та же ошибка, что и выше... Пример кода: http://coliru.stacked-crooked.com/a/ecfbb39d5975d753