Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > сингелтон, сериализация в деструкторе


Автор: cupper 12.1.2011, 22:42
делаю вот такую штуку

есть класс (сингелтон)
Код

class DatabaseHelper : private boost::noncopyable
    {
    friend class boost::serialization::access;
            template<class Archive>
            void serialize(Archive&, const unsigned int);
    public:
        static const DatabaseHelper& GetHelper();
        // При разрушении объекта сохраняем найтроки во внешний файл
        ~DatabaseHelper();
    private:
...

        static boost::shared_ptr<DatabaseHelper> theSingelton;
        /// Создает объект с использую дефолтные настройки
        DatabaseHelper();
        
        // Информация для подлкючение к БД
        std::string address_;
        std::string user_;
        std::string pwd_;
        std::string database_;

        /// Сохраняем данные во внешний файл, в xml формате
        void save();
        /// Загружаем настройки из внешнего xml файла
        /// если файл отсутствует, или не соответвует стандарту, загружаются настройки по умолчанию
        void load();
    };

    template<class Archive>
    void DatabaseHelper::serialize(Archive& ar, const unsigned int version)
    {
        ar & BOOST_SERIALIZATION_NVP(address_);
        ar & BOOST_SERIALIZATION_NVP(user_);
        ar & BOOST_SERIALIZATION_NVP(pwd_);
        ar & BOOST_SERIALIZATION_NVP(database_);
    }


Вот основные методы
Код

boost::shared_ptr<DatabaseHelper> DatabaseHelper::theSingelton = boost::shared_ptr<DatabaseHelper>();

    const DatabaseHelper& DatabaseHelper::GetHelper()
    {
        if(!theSingelton)
        {
            theSingelton.reset(new DatabaseHelper());
            theSingelton->load();
        }
        return *theSingelton;
    }

    DatabaseHelper::DatabaseHelper() :
        address_(SERVER_ADDRESS),
        user_(USER),
        pwd_(PASSWORD),
        database_(DATABASE)
    {
    }

    DatabaseHelper::~DatabaseHelper()
    {
        theSingelton->save();
    }
void DatabaseHelper::save()
    {
        try
        {
            std::ofstream ofs(SETTING_FILE_NAME);
            if(ofs.good())
            {
                std::cout << this->user_ << std::endl;
                boost::archive::xml_oarchive oa(ofs);
                oa << boost::serialization::make_nvp("queryString", *this);
                ofs.close();
            }
        }
        // если чтото пошло не так, пользователю знать об этом не обязательн
        catch(...)
        {
        }
    }
    void DatabaseHelper::load()
    {
        try
        {
            std::ifstream ifs(SETTING_FILE_NAME);
            if(ifs.good())
            {
                boost::archive::xml_iarchive ia(ifs);
                ia >> boost::serialization::make_nvp("queryString", *this);
                ifs.close();
            }
        }
        // если чтото пошло не так, используем настройки по умолчанию
        catch(...)
        {
            theSingelton.reset(new DatabaseHelper());
        }
    }


при заверншении программы вызывается по средством shared_ptr вызывается деструктор для объекта theSingelton, а в деструкторе вызывается функция для сериализации текущего состояния в файл. 

При работе программы (а вернее при ее завершении) получаю
Код

server: /usr/include/boost/serialization/singleton.hpp:131: static T& boost::serialization::singleton<T>::get_instance() [with T =
 boost::archive::detail::oserializer<boost::archive::xml_oarchive, DatabaseLayer::DatabaseHelper>]: Assertion `! 
detail::singleton_wrapper<T>::m_is_destroyed' failed.
Аварийный останов

ошибку вызывает строка
Код

ia >> boost::serialization::make_nvp("queryString", *this);

не врублюсь, в чем ошибка.

Автор: mes 12.1.2011, 23:05
Цитата(cupper @  12.1.2011,  21:42 Найти цитируемый пост)
 вызывается деструктор для объекта theSingelton, а в деструкторе вызывается функция для сериализации текущего состояния в файл. 

код не смотрел.. но, мне кажется, Вам не помешает погуглить на темы : недостатки синглетонов и чем опасны деструкторы статических переменных.. 

Автор: cupper 13.1.2011, 09:28
ну о сингелтонах я раньше уже читал. Выбор сингелтона, тут не столько потребность в нем, сколько желание попробовать (ну и цель кажется оправданная, настройки подключения к БД должны быть одни). А вот по поводу деструктора статических переменных, вопсец есть, да я читал что они уничтожаются несколько иным образом чем другие объекты (а именно порядок удаления не определен) но у меня тут это и не важно, яже не делаю акцент на данные из вне. У меня класс получается замкнут в себе. Сам себе десераилзует при первом обращении к сеингелтону, и сам себя сериализует при вызове деструктора. 
Притом не важно делаю я сериализацию через *this и не статические функции load save, или же я наоборот делаю их статическими и сериализуют через указатель на объект theSingelton.

Проблема так и не прояснилась для меня. 

Автор: mes 13.1.2011, 10:03
Цитата(cupper @  13.1.2011,  08:28 Найти цитируемый пост)
я же не делаю акцент на данные из вне.

Цитата(cupper @  13.1.2011,  08:28 Найти цитируемый пост)
и сам себя сериализует при вызове деструктора. 

a сериализация разве не "данные из вне" ?

Цитата(cupper @  13.1.2011,  08:28 Найти цитируемый пост)
Притом не важно делаю я сериализацию через *this и не статические функции load save, или же я наоборот делаю их статическими и сериализуют через указатель на объект theSingelton.

ну так проблема то не с this-ом вашего класса.. а то что он пытается использовать уже разрушенные объекты...
перечитайте внимательно текст ошибки.. 

Автор: cupper 13.1.2011, 12:00
Цитата(mes @ 13.1.2011,  10:03)
ну так проблема то не с this-ом вашего класса.. а то что он пытается использовать уже разрушенные объекты...
перечитайте внимательно текст ошибки..

хм... перед непосредственно сериализацией данные они еще есть, распечатывал, получается что когда процесс передается сериализации то в этот момент данные разрушаются ?

А как это можно проверить, и можно ли как то преодолеть эту проблему. А то не хочется выносить сериализацию в отдельный метод и запускать ее ручками при выходе из программы.

Автор: mes 13.1.2011, 13:33
Цитата(cupper @  13.1.2011,  11:00 Найти цитируемый пост)
перед непосредственно сериализацией данные они еще есть,

данные чего ? вашего синглетона.. ну так конечно есть.. ибо деструктор еще в действии.. 
проблема у того, кому Вы эти данные передаете.. 

Цитата(cupper @  13.1.2011,  11:00 Найти цитируемый пост)
 А то не хочется выносить сериализацию в отдельный метод и запускать ее ручками при выходе из программы. 

ручками не обязательно.. но запускаться она должна при выходе из маин.. а не тогда, когда будет автоматически подчищен весь мусор...


сейчас посмотрю, что там у Вас с кодом.. 

Автор: mes 13.1.2011, 13:57
Цитата

Код

  BOOST_DLLEXPORT static T & get_instance() {
        static detail::singleton_wrapper< T > t;          // <<<<<<<<<<<<<<<<< он уничтожился раньше вашего синглетона.. 
        // refer to instance, causing it to be instantiated (and
        // initialized at startup on working compilers)
        assert(! detail::singleton_wrapper< T >::m_is_destroyed);
        use(instance);
        return static_cast<T &>(t);
    }


Автор: cupper 13.1.2011, 14:27
Цитата(mes @ 13.1.2011,  13:33)
Цитата(cupper @  13.1.2011,  11:00 Найти цитируемый пост)
перед непосредственно сериализацией данные они еще есть,

данные чего ? вашего синглетона.. ну так конечно есть.. ибо деструктор еще в действии.. 
проблема у того, кому Вы эти данные передаете.. 

Цитата(cupper @  13.1.2011,  11:00 Найти цитируемый пост)
 А то не хочется выносить сериализацию в отдельный метод и запускать ее ручками при выходе из программы. 

ручками не обязательно.. но запускаться она должна при выходе из маин.. а не тогда, когда будет автоматически подчищен весь мусор...


сейчас посмотрю, что там у Вас с кодом.. 

ну а что ее там запускать будет ? Получается нужно будет ридумать еще какую то конструкцию над сингелтоном который бы в своем деструкте делал сериализацию сингелтона и потом только удалялся бы сингелтон. 

В голове крутиться идея, в деструкте сингелтона, создатьвать объект-копию уничтожаемого и его уже сериализовать. 


Автор: mes 13.1.2011, 14:37
Цитата(cupper @  13.1.2011,  13:27 Найти цитируемый пост)
Получается нужно будет ридумать еще какую то конструкцию над сингелтоном который бы в своем деструкте делал сериализацию сингелтона и потом только удалялся бы сингелтон. 

при чем тут Ваш синглетон ??? с ним все в порядке.. он жив на момент использования... а вот тот, который должен быть исполнить его последнюю волю, к сожалению, уже погиб..

Добавлено через 1 минуту и 44 секунды
Цитата(cupper @  13.1.2011,  13:27 Найти цитируемый пост)
В голове крутиться идея, в деструкте сингелтона, создатьвать объект-копию уничтожаемого и его уже сериализовать. 

еще раз перечитайте внимательно предыдущие посты.. 

Автор: cupper 13.1.2011, 15:54
Цитата(mes @ 13.1.2011,  14:37)
при чем тут Ваш синглетон ??? с ним все в порядке.. он жив на момент использования... а вот тот, который должен быть исполнить его последнюю волю, к сожалению, уже погиб..
...
еще раз перечитайте внимательно предыдущие посты..

я не могу понять чего вы мне говорите. Скажите прямо. Я дос сих порт так и не понял что вызывает ошибку.

Автор: mes 13.1.2011, 15:59
Цитата(cupper @  13.1.2011,  14:54 Найти цитируемый пост)
я не могу понять чего вы мне говорите. Скажите прямо. Я дос сих порт так и не понял что вызывает ошибку. 

есть два статических объекта.. один Ваш, один библиотечный.. Вы в деструкторе Вашего обращаетесь к библиотечному.. 
но.. библиотечный к тому времени уже уничтожен...

Добавлено через 54 секунды
Цитата(cupper @  13.1.2011,  14:54 Найти цитируемый пост)
 Скажите прямо. 


а тут разве не прямо ? :
Цитата(mes @  13.1.2011,  13:37 Найти цитируемый пост)
при чем тут Ваш синглетон ??? с ним все в порядке.. он жив на момент использования... а вот тот, который должен быть исполнить его последнюю волю, к сожалению, уже погиб..


Автор: cupper 13.1.2011, 16:31
Цитата(mes @ 13.1.2011,  15:59)
есть два статических объекта.. один Ваш, один библиотечный.. Вы в деструкторе Вашего обращаетесь к библиотечному.. 
но.. библиотечный к тому времени уже уничтожен...

ах тыж еп*ать черт блин заногу дери smile 

точно, яж не контролирую процесс удаление, сингелтон удаляется примерно тогда же когда и все другие объекты. Долго так доходило потому что я даже и не подозревал что сам механизм сериализации сам юзает статические объекты. 

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

блин, чеж делать тогда :(

Автор: mes 13.1.2011, 17:02
Цитата(cupper @  13.1.2011,  15:31 Найти цитируемый пост)
Черт, тогда получается то что я пытался сделать вообще нельзя сделать. 

скорее просто задумали не то что нужно..  smile 
что требуется то в общем (без оглядки на реализацию) ?
чтоб сериализовало некую БД по выходу из маин ?

можно например так :
Код

struct auto_saver 
{
     auto_saver (BD& bd) : _bd(bd) {
     }
     ~auto_saver (){
         _bd->save(); 
     }
  private:
     BD _bd;
};

BD bd;
int main ()
{
    auto_saver (bd);

//...   
     
} // <<< здесь произойдет запись.

Автор: cupper 13.1.2011, 20:10
деструктор auto_saver вызывается при выходе из main. А когда же тогда вызываелся дейструктов в моем коде ? я был уверен что также при выходе из main. Тогда в чем собстно разница ? Или же сдесь игра идет за счет того что создается стековый объект auto_saver деструктор которого вызывается 100% раньше чем для любых статических переменных ?

и кстати 
Код

auto_saver (BD& bd) : _bd(bd) {
     }

вы подразумеваете отказ от сингелтона как такового ?

И будет ли разница если сделать так
Код

struct auto_saver 
{
     auto_saver (BD* bd) : _bd(bd) {
     }
     ~auto_saver (){
         _bd->save(); 
     }
  private:
     // существует только один объект класс BD и мы можем хранить только указатель на него
     BD* _bd;
     // а лучьше всетаки shared_ptr<BD>
};


но все равно не понимаю концептуальной разницы между моим кодом и вашим. Тут и там сериализация происходит в деструкторе. Оба вызываются автоматически.

Автор: mes 13.1.2011, 20:33
Цитата(cupper @  13.1.2011,  19:10 Найти цитируемый пост)
вы подразумеваете отказ от сингелтона как такового ?

нет, в контексте данного вопроса, не отказываю Вам в синглетоне, как таковом smile 
а лишь призываю, не использовать в его деструкторе ничего внешнего.. smile

Цитата(cupper @  13.1.2011,  19:10 Найти цитируемый пост)
деструктор auto_saver вызывается при выходе из main

 smile 

Цитата(cupper @  13.1.2011,  19:10 Найти цитируемый пост)
 А когда же тогда вызываелся дейструктов в моем коде ?

гораздо позже.. smile

Цитата(cupper @  13.1.2011,  19:10 Найти цитируемый пост)
Тогда в чем собстно разница ?

во времени жизни.. 
в случае с автосейвером, он гарантировано уничтожится на выходе из мейн и гарантировано раньше, чем  уничтожатся синглетоны статические объекты..  в случае с синглетоном такой гарантии нет..

Цитата(cupper @  13.1.2011,  19:10 Найти цитируемый пост)
 Тут и там сериализация происходит в деструкторе. Оба вызываются автоматически. 

но в разные моменты времени smile см. выше..

Цитата(cupper @  13.1.2011,  19:10 Найти цитируемый пост)
но все равно не понимаю концептуальной разницы 

поэтому я изначально и предложил :

Цитата(mes @  12.1.2011,  22:05 Найти цитируемый пост)
чем опасны деструкторы статических переменных..  

но Вы почему то не отказали себе в этом удовольствии  smile  smile

а так бы съэкономили время и мне и себе  smile

Добавлено через 11 минут и 9 секунд
Цитата(cupper @  13.1.2011,  15:31 Найти цитируемый пост)
 сингелтон удаляется примерно тогда же когда и все другие объекты.

другие статические объекты и в неопределенном для пользователя порядке.. 


Цитата(cupper @  13.1.2011,  15:31 Найти цитируемый пост)
 потому что я даже и не подозревал что сам механизм сериализации сам юзает статические объекты. 

 smile, А Вы пробовали прочитать сообщение ошибки ? или это только для нас выложили :

Цитата(cupper @  12.1.2011,  21:42 Найти цитируемый пост)
server: /usr/include/boost/serialization/singleton.hpp:131: static T& boost::serialization::singleton<T>::get_instance() [with T =
 boost::archive::detail::oserializer<boost::archive::xml_oarchive, DatabaseLayer::DatabaseHelper>]: Assertion `! 
detail::singleton_wrapper<T>::m_is_destroyed' failed.

 smile 

Автор: cupper 13.1.2011, 20:51
спасибо за объяснение. Теперь все окончательно понятно. 


PS. текст ошибки читал, и недоумевал как сериалиазция узнала что я юзаю сингелтон )))

Автор: mes 13.1.2011, 21:06
Цитата(cupper @  13.1.2011,  19:51 Найти цитируемый пост)
текст ошибки читал, и недоумевал как сериалиазция узнала что я юзаю сингелтон )))

 smile  smile  

Автор: cupper 13.1.2011, 21:59
господя сколько же извращений я нетерпелся пока довел это до рабочего состояния. В итоге имею
Шаблонный класс
Код

template<class T>
    class AutoSave
    {
    public:
        AutoSave(){}

        ~AutoSave()
        {
            T::save();
        }
    };

Для его использование необходим класс предоставляющий открытй статический метод save() реализующий  что душе взбредет.
Код

class DatabaseHelper : private boost::noncopyable
    {
    friend class BaseDB;
    friend class boost::serialization::access;
            template<class Archive>
            void serialize(Archive&, const unsigned int);

              static boost::shared_ptr<DatabaseHelper> theSingelton;
        /// Загружаем настройки из внешнего xml файла
        /// если файл отсутствует, или не соответвует стандарту, загружаются настройки по умолчанию
        void load();
    public:
        /// Сохраняем данные во внешний файл, в xml формате
        static void save();
    };
void DatabaseHelper::save()
    {
        try
        {
            std::ofstream ofs(SETTING_FILE_NAME);
            if(ofs.good())
            {
                //std::cout << this->user_ << std::endl;
                boost::archive::xml_oarchive oa(ofs);
                oa << boost::serialization::make_nvp("queryString", *theSingelton);
                ofs.close();
            }
        }
        // если чтото пошло не так, пользователю знать об этом не обязательно
        catch(...)
        {
        }
    }


Автор: mes 13.1.2011, 22:18
Цитата(cupper @  13.1.2011,  20:59 Найти цитируемый пост)
Для его использование необходим класс предоставляющий открытй статический метод save() реализующий  что душе взбредет.

Цитата(cupper @  13.1.2011,  20:59 Найти цитируемый пост)
господя сколько же извращений я нетерпелся пока довел это до рабочего состояния. В итоге имею

если уж делаете шаблон, то пусть он принимает функциональный объект, который и будет вызывать в деструкторе.. 
(а может в бусте уже такое и есть..) тогда можно будет прибиндить все что душе угодно smile


Автор: cupper 13.1.2011, 22:30
Цитата(mes @ 13.1.2011,  22:18)
если уж делаете шаблон, то пусть он принимает функциональный объект, который и будет вызывать в деструкторе.. 

вот про это извращение я и говорил smile

у меня то из класса единственно что можно получить это ссылку на костантный объект (на сингелтон мой). А додумать как ее запихать в shared_ptr и чтоб потом все еще работало... вот это так и не получилось.

Автор: mes 13.1.2011, 22:56
Цитата(cupper @  13.1.2011,  21:30 Найти цитируемый пост)
вот про это извращение я и говорил 

и где ж извращение то ?

Цитата(cupper @  13.1.2011,  21:30 Найти цитируемый пост)
у меня то из класса единственно что можно получить это ссылку на костантный объект (на сингелтон мой). А додумать как ее запихать в shared_ptr и чтоб потом все еще работало... вот это так и не получилось. 

 smile

Добавлено через 7 минут и 8 секунд
вот например :
http://liveworkspace.org/code/614303e1aa0d73cfe983dbb2014d7245

Автор: cupper 13.1.2011, 23:22
извращение не в идеи, а в процессе ее реализации ))
 
но ятаки навоял код
Код

template<class T>
    class AutoSave
    {
    public:
        AutoSave(boost::shared_ptr<T> object):ptr(object){}

        ~AutoSave()
        {
            if(ptr)
            {
                ptr->save();
            }
        }
    private:
        boost::shared_ptr<T> ptr;
    };

Код

class DatabaseHelper : private boost::noncopyable
    {
    public:
        static const DatabaseHelper& GetHelper();
        static const boost::shared_ptr<const DatabaseHelper> GetHelperPtr();

        /// Загружаем настройки из внешнего xml файла
        /// если файл отсутствует, или не соответвует стандарту, загружаются настройки по умолчанию
        void load();
    public:
        /// Сохраняем данные во внешний файл, в xml формате
        void save() const;
    };

Код

main(){
     AutoSave<const DatabaseHelper> autosave(DatabaseHelper::GetHelperPtr());


PS. а теперь посмотрб чего предложили вы, и вероятно ужаснусь от того сколько я всего сделал лишнего ))
PPS. хмм.... мой мозг наверно сегодня уже совсем не варит, ибо я понимаю что за в примере, но уже не понимаю как его связать со своей задачей.

Псукай в итоге мой код уже даже работает, может даже и правильно, но увы, это уже не то чего я хотел изначально :(

Вам mes большое спасибо за разъяснение основной ошибки. Яб сам до этого долго еще не додумался бы.

Автор: mes 14.1.2011, 00:00
Цитата(cupper @  13.1.2011,  22:22 Найти цитируемый пост)
, ибо я понимаю что за в примере, но уже не понимаю как его связать со своей задачей.

а может просто BD не соответствует требованиям ? поэтому и возникают вопросы.. хотя это уже другая тема..

Автор: cupper 14.1.2011, 00:32
Цитата(mes @ 14.1.2011,  00:00)
Цитата(cupper @  13.1.2011,  22:22 Найти цитируемый пост)
, ибо я понимаю что за в примере, но уже не понимаю как его связать со своей задачей.

а может просто BD не соответствует требованиям ? поэтому и возникают вопросы.. хотя это уже другая тема..

что за БД ? каким требованиям ?

DatabaseHelper содаржит параметры для подключения, адрес, логин, пассовр, название БД, одним сломов настройки, я как раз и хотел иметь возможность харнить эти настройки вне программы smile 

Автор: mes 14.1.2011, 00:48
Цитата(cupper @  13.1.2011,  23:32 Найти цитируемый пост)
DatabaseHelper содаржит параметры для подключения, адрес, логин, пассовр, название БД, одним сломов настройки, я как раз и хотел иметь возможность харнить эти настройки вне программы 

но вот тогда зачем его делать синглетоном ?!  smile  smile 

Автор: cupper 14.1.2011, 10:51
в моем конкретном случае, БД одна и настройки подключения к ней тоже только одни. Вот и решил опробовать паттерн. С лихвой закруткой в виде автосохранения.

Автор: mes 14.1.2011, 10:59
Цитата(cupper @  14.1.2011,  09:51 Найти цитируемый пост)
 БД одна и настройки подключения к ней тоже только одни

это еще не основание для применения паттерна синглетон.. 

Автор: cupper 14.1.2011, 16:49
Цитата

решил опробовать паттерн

а это ?

Автор: mes 14.1.2011, 17:17
Цитата(cupper @  14.1.2011,  15:49 Найти цитируемый пост)
а это ? 

это сойдет  smile  тем более что результативно.. сразу же нарвались на одно из больных мест этого паттерна  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)