处理可能的空指针的类的增强序列化

Boost serialization of class handling a possible null pointer

本文关键字:增强 序列化 空指针 处理      更新时间:2023-10-16

我想序列化下面的类,包装一个可以处理null m_element的指针,就像你在调用默认构造函数时看到的那样。这个跟着这个问题。

生活MCVE 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);
    }
}

boost::archive的save和load方法理解指针和对象引用之间的区别。您不需要指定*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}

感谢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时,load函数出现问题。在这种情况下,ptr确实需要一个存储空间。