serialization - その他

その他残っている部分。これでだいたい全部出揃ったかな。

デシリアライズ中に例外が発生した場合
ファイルから復元している最中にエラーが起こると例外が投げられるのだけど、その場合に中途半端にオブジェクトが生成されている状態になっていて、それらの後片付けを行わないとメモリリークしてしまう。
x_xarchive::delete_created_pointers() を呼び出せば復元したオブジェクトをすべて消去してくれる。

配列、STLコンテナやsmart_ptr
デフォルトでシリアライズできる。楽チン。ただし、適時ヘッダをインクルードする必要がある。命名則は "boost/serialization/クラス名.hpp"。hash_mapだったら "boost/serialization/hash_map.hpp"。

バイナリデータ
バイナリデータのシリアライズには make_binary_object() を使う。

char m_aBinData[256]; // バイナリデータ
...
ar & boost::serialization::make_binary_object(m_aBinData, sizeof(m_aBinData)));


save/loadの処理の分岐
異バージョン間での互換性のために save/loadの処理を分岐させることはよくあるが、MFCでは CArhive::IsStoring() によって分岐させていた。boostでは Archive::is_saving::value のbool値で可能。
さらにこれを支援するマクロ(BOOST_SERIALIZATION_SPLIT_MEMBER)がある。これを埋め込んで、save()/load()というメンバ関数を実装すればよい。
以下はサンプル。

class B
{
    friend class boost::serialization::access;
    template<class Archive>
    void serialize(Archive & ar, const unsigned int version)
    {
    }
};

class A
{
    char m_binData[256];
    std::vector<char> m_vector;
    std::set<char> m_set;
    boost::shared_ptr<B> m_sp;

    friend class boost::serialization::access;
    template<class Archive>
    void save(Archive & ar, const unsigned int version) const
    {
        ar << boost::serialization::make_nvp("shared_ptr", m_sp);
        ar << boost::serialization::make_nvp("set", m_set);
        ar << boost::serialization::make_nvp("binary_data", 
          boost::serialization::make_binary_object(const_cast<char*>(m_binData), sizeof(m_binData)));
        ar << boost::serialization::make_nvp("vector", m_vector);
    }
    template<class Archive>
    void load(Archive & ar, const unsigned int version)
    {
        if (version > 0) {
            ar >> boost::serialization::make_nvp("shared_ptr", m_sp);
        }
        ar >> boost::serialization::make_nvp("set", m_set);
        ar >> boost::serialization::make_nvp("binary_data",
         boost::serialization::make_binary_object(m_binData, sizeof(m_binData)));
        ar >> boost::serialization::make_nvp("vector", m_vector);
    }
    BOOST_SERIALIZATION_SPLIT_MEMBER();
public:
    A() {
        std::generate(m_binData, m_binData + 256, rand);
        std::string s = "vector test";
        m_vector.assign(s.begin(), s.end());
        s = "set test";
        m_set.insert(s.begin(), s.end());
        m_sp.reset(new B);
    }
};
BOOST_CLASS_VERSION(A, 1);

うーむ、腑に落ちない箇所が2つある。
ひとつは save()内の バイナリデータ書き出しの const_cast。binary_objectのコンストラクタの第1引数が void* なのでこうしないとコンパイルできん。
もう1つは、アーカイブの種類に xml_xarchiveを指定すると、m_vectorとm_setのシリアライズ箇所がなぜかコンパイルエラーになってしまう点。 どうもメンバ関数のsave/loadではなく、非侵入型のsave/loadの呼び出しにいってしまうようだ。
saveだけ、loadだけ、にm_vectorのシリアライズを記述した場合はOKで、両方に記述するとアウトというよくわからん現象になっている。誰か原因知っていたら教えてください。

<< serialization - デフォルトコンストラクタのないクラス Site Top minmax >>

Comments