【问题标题】:Get private data members for non intrusive boost serialization C++获取私有数据成员以进行非侵入式升压序列化 C++
【发布时间】:2015-08-16 04:14:45
【问题描述】:

我已尝试为我的非成员 serialize() 函数提供类 A 的 getter,因为从成员访问是私有的。

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);

不幸的是,当我尝试使用吸气剂GetRef()GetId()时,执行一直告诉我uncaught exception of type boost::archive::xml_archive_exception - Invalid XML tag name
如果我在公开时直接访问m_id,效果会很好。

有什么好的方法吗?

【问题讨论】:

    标签: c++ serialization boost boost-serialization


    【解决方案1】:
    1. 你可以使用好老派朋友:

      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. 您可以使用getRef() 方法。这个

      • 不需要朋友(干扰较少)
      • 需要make_nvp(因为您不能使用a.getRef() 作为XML 元素名称

      可悲的是,引用 getter 以一种可怕的方式破坏了封装。我个人更喜欢首先公开m_id

      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());
          }
      }
      }
      

      奖励积分:

    3. 您可以使用“pimpl”样式的结构。您可以在A&lt;&gt; 中转发声明一个结构:

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

      这比getRef() 方法的侵入性要小,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);
          }
      };
      

      也可以看到Live On Coliru

    【讨论】:

    • 添加了一种不会破坏封装的“两全其美”的方法:Live On Coliru
    • 哇。这是一个非常好的答案,提供了不同的解决方案及其优缺点。正是我想要的;)。谢谢 !太糟糕了,我不能投票两次...我会尝试 1/ 和 3/ !
    • 一如既往,一个完整的好答案。此外,虽然没有破坏封装,并且可能完全违反信任,this approach demonstrates 序列化私有成员变量。据我所知,它符合规范。很遗憾,在处理第三方库时,我不得不使用它。 ):
    • 感谢 Tanner 的解决方案,我会看看!
    【解决方案2】:

    仅供参考:为了让 sehe 的第一个解决方案正常工作:

    您需要这样的朋友方法的前向删除

    // 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();
    };
    

    我花了一段时间才让它工作。

    干杯

    【讨论】:

    • 实际上,要使该示例正常工作,您只需单击显示 Live On Coliru 的链接,然后您就不需要转发了声明
    【解决方案3】:

    第一个解决方案的补充信息:

    该解决方案需要两阶段查找和/或依赖于参数的查找。不幸的是,MSVC 还没有完全支持这一点。

    在 VS Community 2019 16.1.6 中使用 boost 1.70 编译会导致一个模糊的错误:

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

    即使通过 /permissive- 标志启用了一致性模式并且选择了最新的语言标准 /std::c++latest,如 this MSVC Blog Post 中所述。

    将类型名限定符添加到朋友声明中可以解决问题:

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

    有趣令人沮丧:

    如果类 A 不是模板类,那么无论哪种方式都不起作用,与上述相同的错误... 示例代码:http://coliru.stacked-crooked.com/a/ecfbb39d5975d753

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-04
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-18
      • 1970-01-01
      相关资源
      最近更新 更多