C++と色々

主にC++やプログラムに関する記事を投稿します。

boost::serializationでデフォルトコンストラクタのないクラスをシリアライズ化する

デフォルトコンストラクタを持たないクラスをシリアライズする。

save_construct_dataを使う、とかいった情報は出てくるのに、肝心のアーカイブ化するサンプルコードが探した限りあんまり無いので、サンプル張っておきます。ここに知りたいことが書いてありました。

  • 侵入型
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>

class Hoge
{
public:
    Hoge(int d, double& r) : data_(d), ref_(r) {}
    int data() const { return data_; }
    double const& ref() const { return ref_; }

private:
    friend boost::serialization::access;
    template <typename Archive>
    void serialize(Archive&, unsigned int)
    {
        // 何もしない
    }

    int data_;
    double& ref_;
};

namespace boost
{
    namespace serialization
    {
        template <typename Archive>
        void save_construct_data(Archive& ar, Hoge const* p, unsigned int)
        {
            auto const& d = p->data();// ar << xxxや、make_nvpは左辺値参照を引数に取るので、右辺値を左辺値に変換する必要が有る
            ar << boost::serialization::make_nvp("data", d);
            ar << boost::serialization::make_nvp("ref", p->ref());
        }

        template <typename Archive>
        void load_construct_data(Archive& ar, Hoge* p, unsigned int)
        {
            int d;
            double* r = new double{};// ここで確保したメモリは手動で開放する必要があるので、shared_ptrとか使ったほうがいい
            ar >> boost::serialization::make_nvp("data", d);
            ar >> boost::serialization::make_nvp("ref", *r);
            ::new(p) Hoge(d, *r);
        }
    }
}

#include <fstream>
#include <iostream>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>

int main()
{
    {
        double r = 3.14;
        Hoge h{42, r};
        auto* p = &h;
        std::ofstream ofs{"hoge.xml"};
        boost::archive::xml_oarchive oa{ofs};
        oa << boost::serialization::make_nvp("Hoge", p);
    }
    {
        Hoge* p = nullptr;
        std::ifstream ifs{"hoge.xml"};
        boost::archive::xml_iarchive ia{ifs};
        ia >> boost::serialization::make_nvp("Hoge", p);

        std::cout << p->data() << ", " << p->ref() << std::endl;
        delete &(p->ref());// load_construct_dataで確保したメモリを開放する
        delete p;
    }
}
  • 非侵入型
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/nvp.hpp>

class Hoge
{
public:
    Hoge(int d, double& r) : data_(d), ref_(r) {}
    int data() const { return data_; }
    double const& ref() const { return ref_; }

private:
    int data_;
    double& ref_;
};

namespace boost
{
    namespace serialization
    {
        template <typename Archive>
        void serialize(Archive& ar, Hoge& hoge, unsigned int)
        {
            // 何もしない
        }

        template <typename Archive>
        void save_construct_data(Archive& ar, Hoge const* p, unsigned int)
        {
            auto const& d = p->data();// ar << xxxや、make_nvpは左辺値参照を引数に取るので、右辺値を左辺値に変換する必要が有る
            ar << boost::serialization::make_nvp("data", d);
            ar << boost::serialization::make_nvp("ref", p->ref());
        }

        template <typename Archive>
        void load_construct_data(Archive& ar, Hoge* p, unsigned int)
        {
            int d;
            double* r = new double{};// ここで確保したメモリは手動で開放する必要があるので、shared_ptrとか使ったほうがいい
            ar >> boost::serialization::make_nvp("data", d);
            ar >> boost::serialization::make_nvp("ref", *r);
            ::new(p) Hoge(d, *r);
        }
    }
}

#include <fstream>
#include <iostream>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>

int main()
{
    {
        double r = 3.14;
        Hoge h{42, r};
        auto* p = &h;
        std::ofstream ofs{"hoge.xml"};
        boost::archive::xml_oarchive oa{ofs};
        oa << boost::serialization::make_nvp("Hoge", p);
    }
    {
        Hoge* p = nullptr;
        std::ifstream ifs{"hoge.xml"};
        boost::archive::xml_iarchive ia{ifs};
        ia >> boost::serialization::make_nvp("Hoge", p);

        std::cout << p->data() << ", " << p->ref() << std::endl;
        delete &(p->ref());// load_construct_dataで確保したメモリを開放する
        delete p;
    }
}