【问题标题】:Boost serialization of class handling a possible null pointer提升处理可能的空指针的类的序列化
【发布时间】:2015-10-26 21:06:02
【问题描述】:

我想序列化下面的类,包装一个可以处理空 m_element 的指针,正如您在调用默认构造函数时所看到的那样。这跟在question之后。

Live MCVE on Coliru

template <typename T>
struct Ptr { // Ptr could use init constructor here but this is not the point
    Ptr() { m_elem = 0; }
    Ptr(const T* elem) {
        if (elem)
            m_elem = new T(*elem);
        else
            m_elem = 0;
    }
    Ptr(const T& elem)
    {
        m_elem = new T(elem);
    }
    Ptr(const Ptr& elem)
    {
        if (elem.m_elem)
            m_elem = new T(*(elem.m_elem));
        else
            m_elem = 0;
    }
    virtual ~Ptr() { delete m_elem; m_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; };

    T* m_elem;
};

namespace boost { namespace serialization {

    // Not sure about the approach to manage null m_elem here
    template<class Archive, class T>
    void save(Archive & ar, const Ptr<T> &ptr, const unsigned int version)
    {
        T elem = 0;
        if (ptr.m_elem != 0)
            ar& boost::serialization::make_nvp("data", *ptr.m_elem);
        else
            ar& boost::serialization::make_nvp("data", elem);
    }

    // How to implement load ?
    template<class Archive, class T>
    void load(Archive & ar, Ptr<T> &ptr, const unsigned int version)
    {
        ar& boost::serialization::make_nvp("data", *ptr.m_elem);
    }

    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> p;
        std::ostringstream oss;
        boost::archive::xml_oarchive oa(oss);
        oa << BOOST_SERIALIZATION_NVP(p);
        std::cout << oss.str() << std::endl;

        // segfault

        Ptr<double> po;
        std::istringstream iss;
        iss.str(oss.str());
        boost::archive::xml_iarchive ia(iss);
        ia >> BOOST_SERIALIZATION_NVP(po);

    }
    {
        Ptr<double> p(new double(2.0));
        std::cout << *(p.m_elem) << std::endl;

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

        // segfault

        Ptr<double> po;
        std::istringstream iss;
        iss.str(oss.str());
        boost::archive::xml_iarchive ia(iss);
        ia >> BOOST_SERIALIZATION_NVP(po);

    }
}

序列化似乎工作,但反序列化给出了段错误。我在 C++0x 中工作。

  • 如果可能,如何在不更改 Ptr 的情况下提供安全的保存和加载功能来序列化 Ptr?
  • 如果我需要修改Ptr,你有什么建议?

编辑:感谢 Jarod42 评论,我想出了以下保存/加载函数,使用布尔值来检测空指针与否。现在当m_elem 为空时我不再有段错误,但当它不为空时我有一个。

template<class Archive, class T>
void save(Archive & ar, const Ptr<T> &ptr, const unsigned int version)
{
    bool is_null;
    if (ptr.m_elem != 0) {
        is_null = false;
        ar& boost::serialization::make_nvp("is_null", is_null);
        ar& boost::serialization::make_nvp("data", *ptr.m_elem);
    }
    else
    {
        is_null = true;
        ar& boost::serialization::make_nvp("is_null", is_null);
    }
}

template<class Archive, class T>
void load(Archive & ar, Ptr<T> &ptr, const unsigned int version)
{
    bool is_null;
    ar& boost::serialization::make_nvp("is_null", is_null);
    if (is_null == true) {
        ptr.m_elem = 0;   
    }
    else
    {
        ar& boost::serialization::make_nvp("data", *ptr.m_elem);
    }
}

【问题讨论】:

  • 您应该序列化一个布尔值以了解指针是否为 nullptr(如果不是,也序列化其内容)。对于加载,检索此布尔值并在需要加载时分配一个默认元素。
  • 谢谢 Jarod42 我会尝试实施这个解决方案
  • 现在反序列化适用于空指针,但new double 测试用例出现段错误。 Live on Coliru
  • 呃。这个问题与stackoverflow.com/questions/31180311/… 有何不同?
  • 我无法使反序列化正常工作,因此提出了这个新问题。

标签: c++ serialization boost boost-serialization


【解决方案1】:

boost::archive 的保存和加载方法理解指针和对象引用之间的区别。您不需要指定 *m_elem。 m_elem 会做(并正常工作)。 Boost 会理解指针是否为空,并将简单地存储一个指示空指针的值,该值将被正确反序列化。

(简化)示例:

#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <iterator>

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/split_free.hpp>

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

std::ostream& operator<<(std::ostream& os, const A& a) {
    os << "A{" << a.a << "}";
    return os;
}

template <typename T>
struct Ptr { // Ptr could use init constructor here but this is not the point
    Ptr()
    : m_elem(0)
    {}

    Ptr(T elem)
    : m_elem(new T(elem))
    {
    }

private:
    // no copies
    Ptr(const Ptr&);
    Ptr& operator=(const Ptr&);
public:
    // delete is a NOP when called with nullptr arg
    virtual ~Ptr() { delete m_elem; };

    T* get() const {
        return m_elem;
    }

    T& operator*() const {
        return *m_elem;
    }

    template <class Archive>
    void serialize(Archive& ar, const unsigned int /*version*/)
    {
        ar& BOOST_SERIALIZATION_NVP(m_elem);
    }

private:
    T* m_elem;
};

template<class T>
std::ostream& operator<<(std::ostream& os, const Ptr<T>& p) {
    if (p.get()) {
        os << *p;
    }
    else {
        os << "{nullptr}";
    }
    return os;
}

int main()
{
    std::string payload;

    {
        Ptr<A> p;
        std::cout << p << std::endl;
        std::ostringstream oss;
        boost::archive::xml_oarchive oa(oss);
        oa << BOOST_SERIALIZATION_NVP(p);
        payload = oss.str();
//        std::cout << payload << std::endl;

        Ptr<A> p2(A(6));
        std::cout << p2 << std::endl;
        oa << BOOST_SERIALIZATION_NVP(p2);
        payload = oss.str();
//        std::cout << payload << std::endl;
    }
    {

        Ptr<A> po;
        std::istringstream iss(payload);
        boost::archive::xml_iarchive ia(iss);
        ia >> BOOST_SERIALIZATION_NVP(po);
        std::cout << po << std::endl;

        Ptr<A> po2;
        ia >> BOOST_SERIALIZATION_NVP(po2);
        std::cout << po2 << std::endl;
    }
}

预期输出:

{nullptr}
A{6}
{nullptr}
A{6}

【讨论】:

  • 我可能误解了,但是这个live code 在更改/加载函数中不能与ar&amp; boost::serialization::make_nvp("data", ptr.m_elem); 一起编译。我想这将是 serialize() 中的相同行为而无需拆分。
  • 上面发布的工作示例。注意指针成员没有特殊处理。我省略了分裂行为。建议您从这里开始,然后添加拆分保存/加载。
  • 感谢您的更新。不幸的是,我使用new double 进行的第二次反序列化测试似乎不起作用,请参阅live code here。是否可以坚持使用我的 Ptr 版本?我有一些复制构造函数,而且我正在使用 C++0x,所以不幸的是没有 std::move、前向引用等。此外,这里需要一个非侵入式的 boost 序列化(但我认为它在这里并不重要)。
  • 当然。实际上 c++11 的使用也无关紧要。我将修改示例。
  • @coincoin 示例已更新为仅使用 c++03 代码。注意空指针被正确序列化和反序列化。
【解决方案2】:

感谢 Jarod42 的评论,

您应该序列化一个布尔值以了解指针是否为 nullptr (如果没有,也序列化它的内容)。加载,检索这个 布尔值并在需要时分配一个默认元素以供您加载。

我们提供了以下保存和加载功能:

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

template<class Archive, class T>
void load(Archive & ar, Ptr<T> &ptr, const unsigned int version)
{
    bool is_null;
    ar & boost::serialization::make_nvp("is_null", is_null);

    if (!is_null) {
        ptr.m_elem = new T;
        ar& boost::serialization::make_nvp("data", *ptr.m_elem);
    }
}

Live on Coliru

当 is_null 为 false 时,加载函数出现问题。在这种情况下,ptr 确实需要一个存储空间。

【讨论】:

  • 坦率地说,这看起来是个坏主意。我想我已经告诉过你“你需要重新考虑你的序列化计划。你希望/需要跟踪什么对象身份?你可以跟踪Ptr&lt;&gt;对象的身份,从你不厌其烦的事实来看要实现自定义指针包装器类型,我的印象是这可能是您想要/需要的全部。” a month ago 粗略检查,问题似乎是一样的?我会重新考虑您的连载计划。想想对象跟踪对您的“特殊指针”意味着什么
  • 问题几乎相同,只是反序列化不起作用。因此,我尝试使用更新的不同代码提出另一个问题。实际上Ptr 不是来自我,而是由其他用户使用,所以我不知道他们将如何使用它。所以我不知道如何按照你的理念实现序列化,这个解决方案似乎适合我的需要。我想采用 Richard 的解决方案,但不幸的是,我不知道为什么它在某些情况下不起作用,请参阅我在 cmets 中回答的实时代码。
  • 你应该计算出Ptr&lt;&gt; 类型的语义。特别是Ptr&lt;T&gt; 是否可以/应该别名并呈现唯一身份的问题(您希望如何跟踪事物)。这就是这里的全部问题。不要用更多的代码来掩盖事情。在你弄清楚它是什么,你需要它之​​前,它永远不会做你需要的事情
猜你喜欢
  • 2015-09-19
  • 1970-01-01
  • 2018-12-13
  • 2018-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多