【Python/C++】オブジェクトの状態をまるっとSAVEしたい!【シリアライズ】
シリアライズって何?
ゲームを中断したい場合って、セーブコマンドを選んでゲームの状態を保存して、次に遊びたいときは、ロードコマンドを選んで途中から再開できますよね。
┏━━━━━━━━┓
┃ → SAVE ┃
┃ LOAD ┃
┗━━━━━━━━┛
それ以外にも、復活の呪文をチラシの裏に書き留めておいて、次に遊びたいときに、その呪文を入力して続きを遊ぶというのもありますね!
ゆうて いみや おうきむ
こうほ りいゆ うじとり
やまあ きらぺ ぺぺぺぺ
ぺぺぺ ぺぺぺ ぺぺぺぺ
ぺぺぺ ぺぺぺ ぺぺぺぺ ぺぺ
同じことを、PythonやC++のクラスのオブジェクトに対してできると、計算した結果を保存しといて、また後で使うとか、他のマシンに持っていって使うなど、色々と便利です。このような、動作しているプログラムのデータをまるっと、ファイルに保存したり、ネットワークで送信したりする形式に変換することを、シリアライズ(Serialize)といいます。反対に、そのような形式になっているデータをプログラムで使用できるデータに復元することをデシリアライズ(Deserialize)といいます。
Pythonの場合は?
Pythonの場合は、pickle というモジュールを使って、酢漬けにして保存します。
今回は、このようなキャラクターのクラスを使います。
class Character:
def __init__(self, name, lv, hp, mp):
self.name = name
self.lv = lv
self.hp = hp
self.mp = mp
def showStatus(self):
print(f"NAME:{self.name} / LV:{self.lv} / HP:{self.hp} / MP:{self.mp}")
シリアライズするには、pickle.dump
というメソッドを使います。
キャラクターの名前などを設定したオブジェクトを作り、そのオブジェクトをpickle.dump
を使ってファイルに保存します。
import pickle
from character import Character
chara = Character('もょもと', 48, 229, 0)
with open('pickled_chara.dump', 'wb') as f:
pickle.dump(chara, f)
デシリアライズするには、pickle.load
というメソッドを使います。
さっきのファイルをpickle.load
を使って読み込み、もょもとオブジェクトをプログラム上で使えるように復元します。
import pickle
from character import Character
with open('pickled_chara.dump', 'rb') as f:
chara = pickle.load(f)
chara.showStatus()
pybind11の場合は?
pybind11は、C++とPythonの橋渡しをするライブラリで、C++で作ったコードをPythonでかんたんに動かすことができるようになります。くわしくは前に書いた記事を見てください。
pybind11を使って、C++のクラスをPython上でシリアライズしたい場合は、このように書きます。
#include <iostream>
#include <string>
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Character {
public:
Character(const std::string& name, int lv, int hp, int mp)
: name_{ name }, lv_{ lv }, hp_{ hp }, mp_{ mp } {}
void showStatus() const {
std::cout << "NAME:" << name_ << " / ";
std::cout << "LV:" << lv_ << " / ";
std::cout << "HP:" << hp_ << " / ";
std::cout << "MP:" << mp_ << std::endl;
}
// シリアライズ
py::tuple _getstate(const Character& chara) const {
return py::make_tuple(chara.name_, chara.lv_, chara.hp_, chara.mp_);
}
// デシリアライズ
Character _setstate(const py::tuple& params) {
return Character(params[0].cast<std::string>(), params[1].cast<int>(),
params[2].cast<int>(), params[3].cast<int>());
}
private:
std::string name_;
int lv_;
int hp_;
int mp_;
};
PYBIND11_MODULE(mychara, m) {
py::class_<Character>(m, "Character")
.def(py::init<const std::string&, int, int, int>())
.def("showStatus", &Character::showStatus)
.def(py::pickle(&Character::_getstate, &Character::_setstate))
;
}
C++の場合は?
C++でシリアライズしたい場合は、いくつかライブラリがあります。
-
boost::serialization
Boost c++ Libraries に含まれているシリアライズ ライブラリです。使用するにはライブラリをビルドする必要があります。2004年頃からあり、古いC++コンパイラでも使えます。 -
cereal
C++11準拠の比較的新しいライブラリなので、とても使いやすいです。ヘッダーファイルのみで、インクルードすれば使用できます。バイナリ/XML/JSONなどに対応しています。
cerealの使い方
cerealの使い方について、かんたんに説明します。
対象のクラスに以下のような、serialize
というメンバーテンプレート関数を作ります。関数内でシリアライズしたいメンバー変数をar(hogehoge)
のように書いていくだけです。
#include <iostream>
#include <string>
#include <cereal/cereal.hpp>
#include <cereal/types/string.hpp>
class Character {
public:
Character()
: name_{}, lv_{ 0 }, hp_{ 0 }, mp_{ 0 } {}
Character(const std::string& name, int lv, int hp, int mp)
: name_{ name }, lv_{ lv }, hp_{ hp }, mp_{ mp } {}
void showStatus() const {
std::cout << "NAME:" << name_ << " / ";
std::cout << "LV:" << lv_ << " / ";
std::cout << "HP:" << hp_ << " / ";
std::cout << "MP:" << mp_ << std::endl;
}
// シリアライズ/デシリアライズ
template <class Archive>
void serialize(Archive& ar) {
ar(name_, lv_, hp_, mp_);
}
private:
std::string name_;
int lv_;
int hp_;
int mp_;
};
シリアライズの例は↓こちらです。出力ファイルストリームをBinaryOutputArchive
のコンストラクタの引数にして、それのoperator()
の引数に対象のオブジェクトを渡すだけです。例ではBinaryにしましたが、XMLやJSONの形式で出力することも出来ます。
#include "character.hpp"
#include <fstream>
#include <cereal/archives/binary.hpp>
int main() {
Character chara("もょもと", 48, 229, 0);
std::ofstream ofs("chara.bin", std::ios::binary);
cereal::BinaryOutputArchive archive(ofs);
archive(chara);
return 0;
}
デシリアライズの例は↓こちらです。シリアライズの場合と同様に、入力ファイルストリームをBinaryInputArchive
のコンストラクタの引数にして、それのoperator()
の引数に対象のオブジェクトを渡すだけです。
#include "character.hpp"
#include <fstream>
#include <cereal/archives/binary.hpp>
int main() {
std::ifstream ifs("chara.bin", std::ios::binary);
cereal::BinaryInputArchive archive(ifs);
Character chara;
archive(chara);
chara.showStatus();
return 0;
}
おわりに
今回は、シリアライズ(pickle🥒とcereal🥣)について説明しました!
メンバー募集中です
アダコテックは上記のような画像処理技術を使って、大手メーカーの検査ラインを自動化するソフトウェアを開発している会社です。
機械学習や画像処理の内部ロジックに興味がある方、ご連絡下さい!
我々と一緒にモノづくりに革新を起こしましょう!
Discussion