Введение shared_ptr приводит к ошибке сегментации при десериализации (с boost::serialization)

У меня есть гамма класса, как показано ниже.

Функция загрузки выдает ошибку сегментации, когда функция make_nvp пытается десериализовать существующий XML-файл. Ошибка возникает, когда я использую std::shared_ptr<std::tuple<double,double,double>> val;

Если вместо этого val просто std::tuple<double,double,double> val;

тогда все работает нормально (конечно, я соответствующим образом меняю функции получения и установки).

Теперь я просмотрел кучу вопросов о stackoverflow, погуглил и посмотрел примеры в документации по ускорению, и я не могу понять, почему функция загрузки приводит к завершению программы с ошибкой сегментации.

Примечание. Некоторые старые сообщения в Интернете (и некоторые старые вопросы о stackoverflow), по-видимому, подразумевают, что std::shared_ptr не работал с ускоренной сериализацией в то время. Я не думаю, что это так в 2017 году. В любом случае, просто чтобы быть уверенным, я попытался заменить std::shared_ptr на boost::shared_ptr, и ошибка сегментации осталась.

Я не понимаю/не вижу, почему появляется ошибка?

гамма.ч

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

и гамма.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());
}

person Duck Dodgers    schedule 03.03.2017    source источник
comment
Ваш код неполный и не компилируется как есть.   -  person overseas    schedule 03.03.2017
comment
да. Я добавил только класс, вызвавший проблему. Это часть более крупного проекта, где я прихожу к этому классу после прохождения цепочки других классов, начиная с основного. Позвольте мне еще раз посмотреть, смогу ли я добавить эти классы сюда чистым способом.   -  person Duck Dodgers    schedule 06.03.2017
comment
@overseas, также была небольшая ошибка в имени класса двух последних функций, load() и save(). Я исправил это сейчас. Пользователь sehe добавил только main() для вызова этого класса, и кажется, что у него нет ошибки сегментации. Хм, любопытно! Вероятно, ошибка seg вызвана чем-то другим. Через пару часов проверю.   -  person Duck Dodgers    schedule 06.03.2017
comment
Попробуйте получить mcve (stackoverflow.com/help/mcve), и мы посмотрим, что можно сделать. Этот код не компилируется (экземпляр отсутствует), и я не могу догадаться, с какой проблемой вы столкнулись, потому что, если я исправлю очевидные ошибки и предоставлю простой main, я не обнаружу проблему.   -  person overseas    schedule 06.03.2017


Ответы (1)


Для меня загадка, что на самом деле можно добавить с помощью shared_ptr к одному кортежу внутри... синглтона. Но в любом случае, я сделал ваш код автономным, и он работает:

Жить на 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>
person sehe    schedule 03.03.2017
comment
Привет. Спасибо, что не заметили мою ошибку копирования/вставки/редактирования, когда я забыл переименовать класс для двух последних функций (load() и save()) из CalibrationDataObject в Gamma. :D Дайте мне пару часов, и я пройдусь по этому снова. И да. Ты прав. Добавление shared_ptr, кажется, здесь ничего не дает (и исходный код также не использовал shared_ptr в этом месте), но затем кто-то (не я) отредактировал его (по какой-то причине?!?), и при тестировании я заметил, что это казалось сломать функцию загрузки, что меня заинтересовало. Спасибо за ваше время. - person Duck Dodgers; 06.03.2017
comment
Хм, я тоже не вижу ошибки. :/ В порядке. Это само по себе было полезно. Я принял твой ответ. - person Duck Dodgers; 07.03.2017
comment
@JoeyMallone, возможно, вы загружали архив, сохраненный в старой версии. Макет не будет совместим (вы должны сделать Версии классов, если вы хотите загрузить старые форматы архивов) - person sehe; 08.03.2017
comment
Мы почти пришли к такому же выводу. Отладочные сообщения Boost могут быть такими загадочными. Только с опытом кто-то может сказать, в чем может быть ошибка, потому что вывод отладки кажется совершенно непригодным для использования. - person Duck Dodgers; 08.03.2017