【问题标题】:Introduction of shared_ptr leads to a segmentation fault upon deserialization (with boost::serialization)引入 shared_ptr 会导致反序列化时出现分段错误(使用 boost::serialization)
【发布时间】:2017-07-23 18:08:16
【问题描述】:

我有一个 gamma 类,如下所示。

当 make_nvp 函数尝试反序列化现有 xml 文件时,加载函数会引发分段错误。当我使用std::shared_ptr<std::tuple<double,double,double>> val;时出现错误

如果 val 只是 std::tuple<double,double,double> val;

然后似乎一切正常(当然,我相应地更改了 getter 和 setter 函数)。

现在我在 stackoverflow 上解决了一堆问题,我在 Google 上搜索并查看了 boost 文档中的示例,但我无法弄清楚为什么 load 函数会导致程序因分段错误而终止。

注意: 互联网上的一些旧帖子(以及有关 stackoverflow 的一些旧问题)似乎暗示 std::shared_ptr 过去在那个时间点无法使用 boost 序列化。我认为 2017 年不会出现这种情况。无论如何,为了确定,我尝试用 boost::shared_ptr 替换 std::shared_ptr 并且分段错误仍然存​​在。

我不明白/不明白为什么会出现错误?

伽玛.h

#pragma once
#include <map>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/shared_ptr.hpp>

#include <tuple>

namespace boost
{
    namespace serialization
    {
        template<typename Archive>
        void serialize(Archive & ar, std::tuple<double, double, double> & t,
                    const unsigned int version)
        {
            ar & boost::serialization::make_nvp("t0",std::get<0>(t));
            ar & boost::serialization::make_nvp("t1",std::get<1>(t));
            ar & boost::serialization::make_nvp("t2",std::get<2>(t));
        }

    }
}

class Gamma
{
public:
    static void save(std::ostream& os);
    static void load(std::istream& is);

    std::shared_ptr<std::tuple<double, double, double>> getterX() const;
    void setterX(const std::tuple<double, double, double> &val);

private:

    std::shared_ptr<std::tuple<double,double,double>> val;

    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive& arc, const unsigned int version)
    {
          arc & boost::serialization::make_nvp("val", val);
    }
};

和 gamma.cpp

#include "gamma.h"
#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/utility.hpp>

Gamma &Gamma::instance()
{
    static Gamma obj;
    return obj;
}

std::shared_ptr<std::tuple<double, double, double>> Gamma::getterX() const
{
    return val;
}

void Gamma::setterX(const std::tuple<double, double,double> &v)
{
    if (nullptr == val) {
        m_touchDownCalibration = std::make_shared<std::tuple<double, double,double>>();
    }
    *val = v;
}

const char* TAG = "tag";

void Gamma::save(std::ostream& os)
{
    boost::archive::xml_oarchive arc(os);
    arc & boost::serialization::make_nvp(TAG,instance());
}

void Gamma::load(std::istream& is)
{
    boost::archive::xml_iarchive arc(is);
    arc & boost::serialization::make_nvp(TAG,instance());
}

【问题讨论】:

  • 您的代码不完整,无法按原样编译。
  • 是的。我只添加了导致问题的类。这是一个更大项目的一部分,我在通过一系列其他课程后到达这个课程,从 main 开始。让我再看看,如果我能以一种干净的方式在这里添加这些类。
  • @overseas,最后两个函数load()和save()的类名也有小错误。我现在更正了。用户 sehe 只添加了一个 main() 来调用这个类,似乎他在那里没有分段错误。嗯,好奇!段错误可能是由其他原因引起的。几个小时后会检查。
  • 尝试获得一个 mcve (stackoverflow.com/help/mcve),我们看看我们能做些什么。这段代码既不能编译(缺少实例),我也猜不出你面临什么问题,因为如果我纠正明显的错误并提供一个简单的main,我就不会发现任何问题。

标签: c++ serialization boost deserialization shared-ptr


【解决方案1】:

对我来说,shared_ptr 实际上可以添加到内部的单个元组中是一个谜……单例。但无论如何,我让你的代码独立并且可以工作:

Live On Coliru

我希望你能找出你做的不同/错误的部分:

#include <map>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/serialization/shared_ptr.hpp>

#include <tuple>

namespace boost { namespace serialization {
    template<typename Archive>
    void serialize(Archive & ar, std::tuple<double, double, double> & t, unsigned) {
        ar & boost::serialization::make_nvp("t0", std::get<0>(t));
        ar & boost::serialization::make_nvp("t1", std::get<1>(t));
        ar & boost::serialization::make_nvp("t2", std::get<2>(t));
    }
} }

class CalibrationDataObject
{
public:
    static CalibrationDataObject &instance();
    static void save(std::ostream& os);
    static void load(std::istream& is);

    std::shared_ptr<std::tuple<double, double, double>> getterX() const;
    void setterX(const std::tuple<double, double, double> &val);

private:
    std::shared_ptr<std::tuple<double,double,double>> val;

    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive& arc, unsigned)
    {
          arc & boost::serialization::make_nvp("val", val);
    }
};

//#include "CalibrationDataObject.h"
#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/utility.hpp>

CalibrationDataObject &CalibrationDataObject::instance() { static CalibrationDataObject obj; return obj; }

std::shared_ptr<std::tuple<double, double, double>> CalibrationDataObject::getterX() const {
    return val;
}

void CalibrationDataObject::setterX(const std::tuple<double, double,double> &v)
{
    if (val)
        *val = v;
    else 
        val = std::make_shared<std::tuple<double, double,double>>(v);
}

const char* TAG = "tag";

void CalibrationDataObject::save(std::ostream& os)
{
    boost::archive::xml_oarchive arc(os);
    arc & boost::serialization::make_nvp(TAG,instance());
}

void CalibrationDataObject::load(std::istream& is)
{
    boost::archive::xml_iarchive arc(is);
    arc & boost::serialization::make_nvp(TAG,instance());
}

#include <fstream>

int main() {
    {
        std::ofstream ofs("test.data");
        CalibrationDataObject::save(ofs);
    }
    {
        std::ifstream ifs("test.data");
        CalibrationDataObject::load(ifs);
    }
}

打印以下数据:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="14">
<tag class_id="0" tracking_level="0" version="0">
    <val class_id="1" tracking_level="0" version="1">
        <px class_id="-1"></px>
    </val>
</tag>
</boost_serialization>

【讨论】:

  • 嗨。感谢您忽略我的复制/粘贴/编辑错误,忘记将最后两个函数(load() 和 save())的类从 CalibrationDataObject 重命名为 Gamma。 :D 给我几个小时,我再过一遍。是的。你说的对。在这里添加一个 shared_ptr 似乎没有任何效果(并且原始代码也没有在这个位置使用 shared_ptr)但是后来有人(不是我)编辑了它(出于某种原因?!?)并且在测试时,我注意到它似乎打破加载功能,这让我很好奇。感谢您的宝贵时间。
  • 嗯,我也没有发现错误。 :/ 好的。这本身就很有帮助。我接受了你的回答。
  • @JoeyMallone 也许您正在加载使用旧版本保存的存档。布局将不兼容(如果你想加载旧的存档格式,你必须这样做Class Versioning
  • 我们几乎得出了相同的结论。 Boost 调试消息可能非常神秘。只有经验才能知道错误可能是什么,因为调试输出似乎完全无法使用。
猜你喜欢
  • 1970-01-01
  • 2021-05-29
  • 2013-12-09
  • 2015-12-22
  • 1970-01-01
  • 2021-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多