【问题标题】:Boost serialization of class wrapping a pointer提升包装指针的类的序列化
【发布时间】:2015-09-19 17:33:14
【问题描述】:

我有一个类Ptr,它包装了一个指针。 此指针可以处理诸如Ptr<A> 之类的结构,其中A 可以是复杂结构或诸如Ptr<double> 之类的原语。我想指定Ptr 序列化的saveload 函数适用于这两种情况。
这里我尝试重现一个简化的例子:

struct A { 
    A(int aa) : a(aa) {} 
    int a; 
    template<class Archive>
    void serialize(Archive &ar, const unsigned int version) {
        ar & BOOST_SERIALIZATION_NVP(a);
    }
};

template <typename T>
struct Ptr {
    Ptr() : m_elem(0) {}
    Ptr(const T* elem) { m_elem = (elem ? new T(*elem) : 0); };
    const T& operator*() const { return *m_elem; };
    T& operator*()  { return *m_elem; };
    const T* operator->() const { return m_elem;};
    T* operator->() { return m_elem;};

    // [...] other ctor, copy, del, etc...

    T* m_elem;
};

namespace boost { namespace serialization {

template<class Archive, class T>
void save(Archive & ar, const Ptr<T> &ptr, const unsigned int version) {
    T* base_pointer = (ptr.m_elem);
    ar & boost::serialization::make_nvp("Ptr", base_pointer);
}

template<class Archive, class T>
void load(Archive & ar, Ptr<T> &ptr, const unsigned int version) {
    T *base_pointer;
    ar & boost::serialization::make_nvp("Ptr", base_pointer);
    ptr.m_elem = base_pointer;
}

template<class Archive, class T>
void serialize(Archive & ar, Ptr<T> &ptr, const unsigned int version)
{
    boost::serialization::split_free(ar, ptr, version);
}

}} // end namespace

int main() {
    Ptr<A> p1(new A(4));
    std::cout << p1.m_elem->a << std::endl;
    Ptr<double> p2(new double(2.0));
    std::cout << *(p2.m_elem) << std::endl;

    // The serialization for Ptr<A> seems to work
    std::ostringstream archive_ostream;
    boost::archive::xml_oarchive oa(archive_ostream);
    oa << BOOST_SERIALIZATION_NVP(p1); 
    std::cout << archive_ostream.str() << std::endl;

    // Serialization for Ptr<double> does not compile
    /*
    std::ostringstream archive_ostream2;
    boost::archive::xml_oarchive oa2(archive_ostream2);
    oa2 << BOOST_SERIALIZATION_NVP(p2); 
    std::cout << archive_ostream2.str() << std::endl;
    */
}

Live example

如您所见,Ptr&lt;A&gt; 的序列化似乎有效(我仍然不确定它是否足够安全)。但是,Ptr&lt;double&gt; 的序列化未编译。

错误输出是:

main.cpp: 在 'void 的实例化中 boost::serialization::save(Archive&, const Ptr&, unsigned int) [与存档 = boost::archive::xml_oarchive; T = A]':

/usr/local/include/boost/serialization/split_free.hpp:45:13:
'static void boost::serialization::free_saver::invoke(Archive&, const T&, unsigned int) [with Archive = boost::archive::xml_oarchive; T = Ptr]'

/usr/local/include/boost/serialization/split_free.hpp:74:18:
'void boost::serialization::split_free(Archive&, T&, unsigned int) [with Archive = boost::archive::xml_oarchive; T = 点]'

main.cpp:57:34: 来自 'void boost::serialization::serialize(Archive&, Ptr&, unsigned int) [with 存档 = boost::archive::xml_oarchive; T = A]'

因此我正在寻找正确的Ptr 序列化!

【问题讨论】:

  • 嗨。感谢您提供现场 SSCCE。如果您有兴趣,我会 a live coding session 回答这个问题。 (experiment)
  • 谢谢@sehe 我会看看这个。看起来很棒:)

标签: c++ serialization boost


【解决方案1】:

谜底的解决方法是不支持通过指针序列化原始类型。

原因是原始类型禁用了对象跟踪。这记录在这里:

特别注意事项/Object Tracking

默认情况下,由实现级别类序列化特征指定的原始数据类型永远不会被跟踪。如果希望通过指针跟踪共享的原始对象(例如用作引用计数的 long ),则应将其包装在类/结构中,使其成为可识别的类型。更改 long 的实现级别的替代方法会影响整个程序中序列化的所有 long - 可能不是人们想要的。

这是一个简单的示例,它单独显示了根本原因:

Live On Coliru

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <iostream>
#include <sstream>

int main() {
    // Serialization for double* does not compile
    double* p(new double(2.0));
    std::cout << *p << std::endl;

    std::ostringstream oss;
    boost::archive::xml_oarchive oa(oss);
    oa << BOOST_SERIALIZATION_NVP(p); 
    std::cout << oss.str() << std::endl;
}

您需要重新考虑您的序列化计划。您希望/需要跟踪什么对象身份?

您可以跟踪Ptr&lt;&gt; 对象的身份,并且从您费力实现自定义指针包装器类型这一事实来看,我的印象是这可能是您想要/需要的全部。

演示:Live On Coliru

万一您真的想要在这里进行双层对象跟踪(例如,如果您可以有两个 Ptr&lt;T&gt; 实例指向同一个 T?)您将需要部分专门化 T 是原始类型。

【讨论】:

  • 谢谢!我会尝试并随时通知您!
  • 实际上,如果我使用make_nvp("data", *ptr.m_elem);,代码会出现运行时错误(未处理的异常。访问冲突读取位置...),如果我使用make_nvp("data", ptr.m_elem);,它运行良好但不会打印任何数据。我正在尝试通过示例重现此错误。
  • 请做。我很想知道有什么不同
  • 哦-当然。我刚刚意识到,如果您反序列化为默认构造的Ptr&lt;double&gt;,那么(显然)m_elem 仍然为 NULL。你应该决定你希望如何处理事情。我的答案在这里显示了类似的情况:deserialization of raw C arrays
  • 如果您需要,这是最小的复制器:coliru.stacked-crooked.com/a/8dd37b7acd703279
猜你喜欢
  • 1970-01-01
  • 2018-12-13
  • 1970-01-01
  • 2015-10-26
  • 2018-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多