2008-02-10

boost::serialization

boost::serialization 은, 템플릿 멤버 함수 하나 또는 free function 하나만 정의하면, 해당 클래스를 다양한 스트림으로 직렬화할 수 있게 해준다. 특히 list - vector - map - set 등의 다양한 STL 컨테이너들을 지원한다는 것이 장점이다.

이때, 매크로 없이 & 연산자 만으로 읽고 쓰기를 가능하게 했는데, 이건 템플릿 아카이브 파라미터가 알아서 잘 읽거나 쓰도록 책임을 전가했다는 게 상당히 아름답다.. 단, virtual 이 아니라서 하위 클래스는 모두 이걸 정의해줘야 한다. 그리고 아직 hash_map 은 잘 지원하지 않는 모양인데, 뭔가 복잡한 사정이 있는 듯하다.

주의할 점이라면 아래 예제처럼 쓰려는 객체가 const 여야 한다는 점 정도?


#include "stdafx.h"

#include <fstream>

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>

#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/hash_map.hpp>

class chat_message
{
    friend class boost::serialization::access;
public:

    chat_message() {}

    chat_message(unsigned short id, unsigned short body_length, string msg, wstring wmsg)
        : id_(id)
        , body_length_(body_length)
        , message(msg)
        , wmessage(wmsg)
    {
        for ( size_t i = 0 ; i < 10 ; i ++ )
        {
            string val = boost::str(boost::format("Hello %1%")%i);
            messages[i] = val;
            array_[i] = val;
            list_.push_back(i);
            vector_.push_back(i);
            map_[i] = val;
            hash_map_[val] = (rand()%2==0 ? true : false);
        }
    }

    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
        ar & id_;
        ar & body_length_;
        ar & message;
        ar & wmessage;
        ar & messages;          // pritimive array
        ar & array_.elems;      // boost::array
        ar & list_;             // std::list
        ar & vector_;           // std::vector
        ar & map_;              // std::map
        //ar & hash_map_;
    }

    void check_equal( const chat_message & r ) const
    {
        BOOST_CHECK_EQUAL(id_,r.id_);
        BOOST_CHECK_EQUAL(body_length_,r.body_length_);
        BOOST_CHECK_EQUAL(message,r.message);
        BOOST_CHECK(wmessage==r.wmessage);

        for ( size_t i = 0 ; i < 10 ; i ++ )
        {
            BOOST_CHECK_EQUAL(messages[i],r.messages[i]);
        }

        for ( size_t i = 0 ; i < array_.size() ; i ++ )
        {
            BOOST_CHECK_EQUAL(array_[i],r.array_[i]);
        }

        {
            BOOST_REQUIRE_EQUAL( list_.size(), r.list_.size() );
            list<float>::const_iterator itr = list_.begin();
            list<float>::const_iterator itr2 = r.list_.begin();
            for ( ; itr != list_.end() ; itr ++, itr2++ )
            {
                BOOST_CHECK_EQUAL( *itr, *itr2 );
            }
        }

        {
            BOOST_REQUIRE_EQUAL( vector_.size(), r.vector_.size() );
            vector<int>::const_iterator itr = vector_.begin();
            vector<int>::const_iterator itr2 = r.vector_.begin();
            for ( ; itr != vector_.end() ; itr ++, itr2++ )
            {
                BOOST_CHECK_EQUAL( *itr, *itr2 );
            }
        }

        {
            BOOST_REQUIRE_EQUAL( map_.size(), r.map_.size() );
            map<int,string>::const_iterator itr = map_.begin();
            map<int,string>::const_iterator itr2 = r.map_.begin();
            for ( ; itr != map_.end() ; itr ++, itr2++ )
            {
                BOOST_CHECK_EQUAL( itr->first, itr2->first );
                BOOST_CHECK_EQUAL( itr->second, itr2->second );
            }
        }

        //{
        //  BOOST_REQUIRE_EQUAL( hash_map_.size(), r.hash_map_.size() );
        //  stdext::hash_map<string,bool>::const_iterator itr = hash_map_.begin();
        //  stdext::hash_map<string,bool>::const_iterator itr2 = r.hash_map_.begin();
        //  for ( ; itr != hash_map_.end() ; itr ++, itr2++ )
        //  {
        //      BOOST_CHECK_EQUAL( itr->first, itr2->first );
        //      BOOST_CHECK_EQUAL( itr->second, itr2->second );
        //  }
        //}
    }
private :
    unsigned short id_;
    unsigned short body_length_;
    string message;
    wstring wmessage;
    string messages[10];
    boost::array<string,10> array_;
    list<float> list_;
    vector<int> vector_;
    map<int,string> map_;
    stdext::hash_map<string,bool> hash_map_;
};

// TODO
// - archive from/to buffer
// - user defined archive
// - string, wstring load/save
// - stl support (list,vector,map)

BOOST_AUTO_TEST_CASE(test_serialize)
{
    const chat_message msg1(35, 59, "Hello World!",L"Welcome!");

    {
        chat_message msg2;

        std::ofstream ofs("serialize.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << msg1;
        ofs.close();

        std::ifstream ifs("serialize.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> msg2;
        ifs.close();

        msg1.check_equal(msg2);
    }

    {
        chat_message msg2;

        std::ofstream ofs("serialize.bin", std::ios::binary);
        boost::archive::binary_oarchive oa(ofs);
        oa << msg1;
        ofs.close();

        std::ifstream ifs("serialize.bin", std::ios::binary);
        boost::archive::binary_iarchive ia(ifs);
        ia >> msg2;
        ifs.close();

        msg1.check_equal(msg2);
    }

    {
        chat_message msg2;

        boost::asio::streambuf buf;
        ostream os(&buf);
        boost::archive::text_oarchive oa(os);
        oa << msg1;

        istream is(&buf);
        boost::archive::text_iarchive ia(is);
        ia >> msg2;

        msg1.check_equal(msg2);
    }

    {
        chat_message msg2;

        boost::asio::streambuf osf;
        boost::archive::binary_oarchive oa(osf);
        oa << msg1;

        boost::archive::binary_iarchive ia(osf);
        ia >> msg2;

        msg1.check_equal(msg2);
    }

    // via istream/ostream
    {
        chat_message msg2;

        boost::asio::streambuf buf;
        ostream os(&buf);
        boost::archive::binary_oarchive oa(os);
        oa << msg1;

        istream is(&buf);
        boost::archive::binary_iarchive ia(is);
        ia >> msg2;

        msg1.check_equal(msg2);
    }

}

comments powered by Disqus