сериализация std::queue
От: k732  
Дата: 19.07.07 11:37
Оценка:
хочу сериализовать класс
template <typename T, class Container = std::deque <T> >
class Queue {
private :
    friend class boost::serialization::access;

    std::queue <T, Container> m_queue;

    template <class Archive>
    void serialize (Archive& archive, const unsigned int version)
    {
        archive & BOOST_SERIALIZATION_NVP (m_queue);
    }

public :
    Queue () {}
    ~Queue() {}
};


Но что-то не нашел в boost/serialization ничего подходящего для сериализации std::queue.
Как тогда она сериализуется ?
Re: сериализация std::queue
От: NKZ  
Дата: 19.07.07 13:03
Оценка:
Здравствуйте, k732, Вы писали

В бусте нет сериализации std::queue, но сделать несложно. Примерно так (код не проверял):



#ifndef BOOST_SERIALIZATION_QUEUE_HPP
#define BOOST_SERIALIZATION_QUEUE_HPP

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

#include <queue>

#include <boost/config.hpp>

#include <boost/serialization/collections_save_imp.hpp>
#include <boost/serialization/collections_load_imp.hpp>
#include <boost/serialization/split_free.hpp>

#include "queue_save_imp.hpp"

// function specializations must be defined in the appropriate
// namespace - boost::serialization
#if defined(__SGI_STL_PORT) || defined(_STLPORT_VERSION)
#define STD _STLP_STD
#else
#define STD std
#endif

namespace boost { 
    namespace serialization {
        namespace stl {

template<class Archive, class Container>
struct archive_input_queue
{
    inline void operator()(Archive &ar, Container &s)
    {
        detail::stack_construct<Archive, BOOST_DEDUCED_TYPENAME Container::value_type> t(ar);
        // borland fails silently w/o full namespace
        ar >> boost::serialization::make_nvp("item", t.reference());
        s.push(t.reference());
        ar.reset_object_address(& s.back() , & t.reference());
    }
};
        }
    } 
}

namespace boost { 
    namespace serialization {

        template<class Archive, class U, class Allocator>
        inline void save(
            Archive & ar,
            const STD::queue<U, Allocator> &t,
            const unsigned int /* file_version */
            ){
                boost::serialization::stl::save_queue<
                    Archive, 
                    std::queue<U, Allocator> 
                >(ar, t);
        }

        template<class Archive, class U, class Allocator>
        inline void load(
            Archive & ar,
            STD::queue<U, Allocator> &t,
            const unsigned int /* file_version */
            ){
                boost::serialization::stl::load_queue<
                    Archive,
                    std::queue<U, Allocator>,
                    boost::serialization::stl::archive_input_queue<
                    Archive, 
                    std::queue<U, Allocator> 
                    >,
                    boost::serialization::stl::no_reserve_imp<STD::queue<U, Allocator> >
                >(ar, t);
        }

        // split non-intrusive serialization function member into separate
        // non intrusive save/load member functions
        template<class Archive, class U, class Allocator>
        inline void serialize(
            Archive & ar,
            STD::queue<U, Allocator> & t,
            const unsigned int file_version
            ){
                boost::serialization::split_free(ar, t, file_version);
        }

    } // serialization
} // namespace boost

#include <boost/serialization/collection_traits.hpp>

BOOST_SERIALIZATION_COLLECTION_TRAITS(STD::queue)
#undef STD

#endif // BOOST_SERIALIZATION_LIST_HPP


#ifndef BOOST_QUEUE_SAVE_IMP_HPP
#define BOOST_QUEUE_SAVE_IMP_HPP

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

#include <boost/serialization/nvp.hpp>
#include <boost/serialization/serialization.hpp>

namespace boost{
    namespace serialization {
        namespace stl {


            //////////////////////////////////////////////////////////////////////
            // implementation of serialization for STL containers
            //

            template<class Archive, class Container>
            inline void save_queue(Archive & ar, const Container &s)
            {
                // record number of elements
                unsigned int count = s.size();
                ar << make_nvp("count", const_cast<const unsigned int &>(count));
                //BOOST_DEDUCED_TYPENAME Container::const_iterator it = s.begin();

                //make copy of queue
                Container tmp = s;

                while(!tmp.empty())
                {
                    boost::serialization::save_construct_data_adl(ar, &(tmp.front()), 0U);
                    ar << boost::serialization::make_nvp("item", tmp.front());
                    tmp.pop();
                }
            }

            template<class Archive, class Container, class InputFunction, class R>
            inline void load_queue(Archive & ar, Container &s)
            {
                while(!s.empty()) s.pop();

                // retrieve number of elements
                unsigned int count;
                ar >> BOOST_SERIALIZATION_NVP(count);
                R rx;
                rx(s, count);
                InputFunction ifunc;
                while(count-- > 0){
                    ifunc(ar, s);
                }

            }


        } // namespace stl 
    } // namespace serialization
} // namespace boost

#endif //BOOST_QUEUE_SAVE_IMP_HPP
Re[2]: сериализация std::queue
От: k732  
Дата: 19.07.07 16:59
Оценка:
Здравствуйте, NKZ, Вы писали:
NKZ>В бусте нет сериализации std::queue, но сделать несложно. Примерно так (код не проверял):

Не получается собрать. А разбираться в этом ужасе пока для меня сложновато
Re[3]: сериализация std::queue
От: NKZ  
Дата: 20.07.07 05:50
Оценка:
Здравствуйте, k732, Вы писали:

K>Не получается собрать. А разбираться в этом ужасе пока для меня сложновато


А какие ошибки? Здесь тестовый проект для VS 2005, проверил, все вроде работает.
Re[4]: сериализация std::queue
От: k732  
Дата: 20.07.07 09:24
Оценка:
Здравствуйте, NKZ, Вы писали:

NKZ>А какие ошибки? Здесь тестовый проект для VS 2005, проверил, все вроде работает.


Извини — это я что-то не то сделал. Вроде собралось.
Только 2 вопроса
1. Зачем реализацию выносить в отдельный файл
2. Меня смущает временный контейнер в методе save_queue. Ведь семантика queue подразумевает уничтожение объекта при доступе к нему. Полезно ли это или все-таки при сериализации нужно разрушать прочитанные данные ?
Re[5]: сериализация std::queue
От: NKZ  
Дата: 20.07.07 10:01
Оценка:
Здравствуйте, k732, Вы писали:

K>1. Зачем реализацию выносить в отдельный файл


В принципе не обязательно, я просто переделал исходные бустовские файлы, а у них разнесено по разным файлам (правда там обобщенная имплементация save/load collection)

K>2. Меня смущает временный контейнер в методе save_queue. Ведь семантика queue подразумевает уничтожение объекта при доступе к нему. Полезно ли это или все-таки при сериализации нужно разрушать прочитанные данные ?


В общем случае void save_queue(Archive & ar, const Container &s) тоже самое что и void save_collection(Archive & ar, const Container &s) в бусте, подразумевается, что при сериализации контейнер не модифицируется.

А так все зависит от твоих требований к очереди, например
1. При остановке обработки ты должен сериализовать очередь, а при старте прочитать и обработать. В этом случае можно(но не обязательно), разрушать очередь.
2. Либо серилизовать данные в процессе обработки, например отсылать их для другим модулям для нотификации об изменений данных, тогда не надо разрушать.
3. что-нить еще
Re[6]: сериализация std::queue
От: Аноним  
Дата: 20.07.07 10:52
Оценка:
Здравствуйте, NKZ, Вы писали:
http://forum.sources.ru/index.php?act=ST&amp;f=150&amp;t=195905&amp;st=0
это случайно не твоя тема на sources.ru
Re[6]: сериализация std::queue
От: k732  
Дата: 20.07.07 11:15
Оценка:
Здравствуйте, NKZ, Вы писали:

NKZ>В общем случае void save_queue(Archive & ar, const Container &s) тоже самое что и void save_collection(Archive & ar, const Container &s) в бусте, подразумевается, что при сериализации контейнер не модифицируется.


логично. согласен.

NKZ>А так все зависит от твоих требований к очереди, например

NKZ> 1. При остановке обработки ты должен сериализовать очередь, а при старте прочитать и обработать. В этом случае можно(но не обязательно), разрушать очередь.

ну лучше тогда придерживаться принятой сигнатуры.

NKZ> 2. Либо серилизовать данные в процессе обработки, например отсылать их для другим модулям для нотификации об изменений данных, тогда не надо разрушать.


Тут вся проблемма в том, что данные помещаются в очередь несколькими писателями. Естественно синхронизировано.
Но в процессе этих действий читатель запрашивает данные для обработки. Тут синхронизация в самом методе сериализации (archive). Если я не буду подчищать очередь в просессе сериализации, то потом мне все равно нужно ее почистить т.к. она имеет ограничение по размеру, а отправленные данные мне больше не нужны. Если же я буду чистить очередь сразу, то отойду от принятой бустовской сигнатуры.

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

Это операция критична ко времени, поэтому тут делема.... Как лучше поступить ?
Re[7]: сериализация std::queue
От: NKZ  
Дата: 20.07.07 11:33
Оценка:
Здравствуйте, k732, Вы писали:

K>Тут вся проблемма в том, что данные помещаются в очередь несколькими писателями. Естественно синхронизировано.

K>Но в процессе этих действий читатель запрашивает данные для обработки. Тут синхронизация в самом методе сериализации (archive). Если я не буду подчищать очередь в просессе сериализации, то потом мне все равно нужно ее почистить т.к. она имеет ограничение по размеру, а отправленные данные мне больше не нужны. Если же я буду чистить очередь сразу, то отойду от принятой бустовской сигнатуры.

K>Разниза лишь в том, что в первом случае мне придется выполнять копирование, а после очистку, что может быть накладно, т.к. ограничена 50000 объектов, а во втором случа придется только очищать ее в процессе, что избавляет от копирования.


K>Это операция критична ко времени, поэтому тут делема.... Как лучше поступить ?


Я не совсем понял, очередь сериализуется и сериализованные данные отдаются читателям и очередь очишается?

В любом случае, надо исходить из эффективности и требований к реализации, так что сигнатуры методов в бусте не являются истинной в последней инстанции . Еще как менее эффективный вариант, можно очищать очередь в синхронизированном блоке кода, сразу после сериализации.
Re[7]: сериализация std::queue
От: NKZ  
Дата: 20.07.07 11:35
Оценка:
Здравствуйте, Аноним, Вы писали:

А>это случайно не твоя тема на sources.ru


Нет
Re[8]: сериализация std::queue
От: k732  
Дата: 20.07.07 12:50
Оценка:
Здравствуйте, NKZ, Вы писали:
NKZ>Я не совсем понял, очередь сериализуется и сериализованные данные отдаются читателям и очередь очишается?

Очередь сериализуется. Полученные данные отправляются потзователю, а сама очередь подчищается, т.к. при сериализации создавалась ее копия. Тоесть данные после отправки не нужны в очереди.

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


Да, но тогда не избежать копирования...

З.Ы. Тут возникла еще одна проблемма, на которую я сначала не обратил внимания.
Т.к. очередь у меня синхронизирована, то методы добавления в очередь и извлечения из нее используют объекты синхронизации (мьютекс и семафор), которые я вляются членами-данными класса-очереди.

Естественно сериализовать объекты синхронизации нельзя. А если сериализовать только сам queue, то после десериализации
нельзя будет исподьзовать методы доступа к очереди, т.к. синхро-объекты будут не валидными.
Примерный код

template <typename T, class Container = std::deque <T> >
class Queue {
private :
    friend class boost::serialization::access;

    boost::mutex m_mutex;
    Semaphore m_semaphore;
    std::queue <T, Container> m_queue;

    template <class Archive>
    void serialize (Archive& archive, const unsigned int version)
    {
        archive & BOOST_SERIALIZATION_NVP (m_queue);
    }

public :
    Queue () {}
    ~Queue() {}

    void push (const T& obj)
    {
        m_semaphore/m_mutex;
        ...
    }
    void pop (T& obj)
    {
        m_semaphore/m_mutex;
        ...
    }
};



Как можно с наименьшими усилиями выйти из этой ситуации ? Вводить метод для сериализации самого std::queue в
класс Queue не очень бы хотелось. Может дружественная функция иди еще что...
Re[9]: сериализация std::queue
От: NKZ  
Дата: 20.07.07 13:29
Оценка:
Здравствуйте, k732, Вы писали:

K>Очередь сериализуется. Полученные данные отправляются потзователю, а сама очередь подчищается, т.к. при сериализации создавалась ее копия. Тоесть данные после отправки не нужны в очереди.


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

K>З.Ы. Тут возникла еще одна проблемма, на которую я сначала не обратил внимания.


А вообще зачем сериализовать объекты синхронизации? Почему они будут невалидными?

Я уже запутался , я себе представляю алгоритм работы так
1. создаем инстанс класса очереди и загружаем сериализованные данные (только саму очередь, синхро-объекты инициализируются по умолчанию)
2. читатели читают (или нет???), писатели пишут
3. в любой момент времени можно сериализовать очередь(тоже нет проблем т.к метод синхронизируется), очередь очищается.

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

Видимо я просто не до конца понимаю ваш алгоритм работы с сериализованными данными, когда они нужны и кому (и нужны ли вообще, потому что после того, как очередь изменится они станут невалидными).
Re[10]: сериализация std::queue
От: k732  
Дата: 20.07.07 19:45
Оценка:
Здравствуйте, NKZ, Вы писали:

NKZ>Я думаю просто очищать очередь во время сериализации (метод закрыть мютексом). Вообщем убрать консантность и копирование в save_queue. Фиг с ним с бустом, так эффекивнее


понятно

NKZ>А вообще зачем сериализовать объекты синхронизации? Почему они будут невалидными?

А как будет инициализирован семафор ? ведь од изменяет свое состояние мотодами push и pop. А тут сериализация минует эти методы

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


Вот пример (на непонятные типы не обращай внимания — думаю суть будет понятна)
Например LimitQueue и организована на std::queue

template <typename T>
class MonitorQueue : public IRQueue<T>, public IWQueue<T> {
private:
    friend class boost::serialization::access;
    static const unsigned WAIT_TIME        = 100;
    static const unsigned QUEUE_SIZE    = 50 * 1000;

    LimitQueue<T> m_queue;
    boost::mutex m_mutex;
    Semaphore m_semaphore;

    template <class Archive>
    void serialize (Archive& archive, const unsigned int version)
    {
        boost::mutex::scoped_lock lock(m_mutex);
        archive & BOOST_SERIALIZATION_NVP (m_queue);
    }

    virtual void OnOverflow (const T& value) const
    {

        FileStream fs ("overflow.dbg");
        fs << "Overflow buffer" << std::endl;
    }

public:
    typedef IRQueue<T> TRQueue;
    typedef IWQueue<T> TWQueue;

    explicit MonitorQueue (std::size_t size = QUEUE_SIZE)
        : m_queue (size), m_semaphore (0, (unsigned)size, WAIT_TIME) {}
    virtual ~MonitorQueue() {}

    virtual void push (const T& value)
    {
        boost::mutex::scoped_lock lock(m_mutex);
        if (m_queue.full())
        {
            OnOverflow(value);
            return;
        }
        m_queue.push(value);
        m_semaphore.unlock();
    }

    virtual bool pop (T& value)
    {
        if (!m_semaphore.lock()) return false;
        boost::mutex::scoped_lock lock(m_mutex);
        _ASSERTE(!m_queue.empty());
        value = m_queue.pop();
        return true;
    }
};


Пусть семафор имеет 30 свободных состояний.
В этот момент происходит сериализация. Очередь чистится.

1.Очистка очереди не может осуществляться методом pop, т.к. произойдет дедлок. Тоесть после очистки нужно еще сбросить семафор.
2. На друго стороне произошла десериализация. Какое состояние имеет семафор ? кто его будет инициализировать ?
Тоесть получается что его инициализация обязательно должна быть в методе сериализации
Re[11]: сериализация std::queue
От: NKZ  
Дата: 23.07.07 06:21
Оценка:
Здравствуйте, k732, Вы писали:

K>Пусть семафор имеет 30 свободных состояний.

K>В этот момент происходит сериализация. Очередь чистится.

K>1.Очистка очереди не может осуществляться методом pop, т.к. произойдет дедлок. Тоесть после очистки нужно еще сбросить семафор.

K>2. На друго стороне произошла десериализация. Какое состояние имеет семафор ? кто его будет инициализировать ?
K>Тоесть получается что его инициализация обязательно должна быть в методе сериализации

1.да надо сбрасывать при сериализации и устанавливать queue.size() при десериализации.
2.да так оно и есть, при сериализаций/десериализаций, дожидаемся когда потоки закончат работать с данными, лочим данные и загружаем/сохраням очередь и инициализируем семафор(при загрузке данных) .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.