Получить личные данные членов для не навязчивого повышения сериализации 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 ответа

Решение
  1. Вы можете использовать хороших старомодных друзей:

    Жить на Колиру

    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. Вы можете использовать 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());
        }
    }
    }
    

    Бонусные очки:

  3. Вы можете использовать структуру стиля "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

Другие вопросы по тегам