2017年6月7日 Theolizer®最新版v1.1.0へ対応するために修正しました。
最新版のソース一式をGistに置いてます。
こんにちは。田原です。
今回はTheolizerで簡単な構造体の保存/回復を行うための手順を説明します。
と言ってもそれほど難しい内容はありません。
1.開発に必要とする知識について
Theolizer®はC++言語(C++11規格準拠)で開発しています。ですのでTheolizer®を適用するアプリもC++11規格対応したC++コンパイラでビルドする必要があります。ですが、アプリを開発する人がC++言語について深く知っている必要はありません。C++をベターCとしてお使いの場合でも十分使えるように設計しました。
今回のシリーズでは、なるべくC言語の知識で理解できる記述で解説したいと思います。
ただし、下記のC++機能はC言語の欠点をカバーするために有用な機能ですので使わせて下さい。基本的な使い方について簡単ですがベターCとして便利なC++機能の紹介で解説してます。また、検索すれば多数の解説サイトがありますから、必要に応じてそれらも参考にして下さい。
名前 | 機能 |
---|---|
std::cout | 標準出力 |
std::ofstream | ファイル出力 |
std::ifstream | ファイル入力 |
std::string | 文字列 |
std::list<> | 双方向の線形リスト |
2.サンプルとして開発するアプリケーションについて
Theolizer®の使い方を説明するに当たって、対象とするアプリを想定しました。簡単な家計簿です。
ただし、あまりに単純な家計簿では面白くありませんので、科目を階層構造で定義し、各科目について、商品なら底値や必要なら在庫数量(普通は管理しないと思いますがサンプルですので)、財布や預金通帳なら残高等を管理することを目標にします。
なお、完全なアプリを開発していると時間がかかりますので、ここではデータ保存/回復部分を中心に開発・説明します。ユーザインターフェースについては特に触れません。
Theolizer®を使えば保存/回復は一発ですので、どんなデータ構造をTheolizer®が取り扱えるのか、そして、それを保存/回復するためにどのようにしてTheolizer®へ指示を与えるのかについて解説していきたいと思います。
3.シリアライズ対象となるデータ構造定義
今回は簡単な構造体を1つ保存/回復してみます。
構造体として日付構造体(Date)と取引構造体(Trade)の2つを定義します。
Dateは見ての通りです。Tradeは商品の購入を記録します。この先、「銀行からお金を下ろしてくる」などもTradeで記録するように拡張しますので少し汎用な名前を付けます。
3-1.構造体定義
//############################################################################ // Theolizer解説用サンプル・プログラム // // 簡単な家計簿用データ構造定義 // データの保存と回復のサンプルなので、 // ユーザ・インタフェースは実装しない //############################################################################ #if !defined(MAIN_H) #define MAIN_H // *************************************************************************** // インクルード // *************************************************************************** // 標準ライブラリ #include <string> // *************************************************************************** // 構造体定義 // *************************************************************************** struct Date { short mYear; // 年 char mMonth; // 月(1-12) char mDay; // 日(1-31) }; struct Trade { Date mDate; // 取引日 std::string mItem; // 費目 int mAmount; // 金額 }; #endif
4.データを設定し、保存/回復する
データ構造を定義した後は、一般にその構造でデータ領域を確保し、各メンバに値を設定します。
そのデータの保存と回復のコードです。
4-1.メイン・プログラム
//############################################################################ // Theolizer解説用サンプル・プログラム // // 簡単な家計簿用データ構造定義 // データの保存と回復のサンプルなので、 // ユーザ・インタフェースは実装しない //############################################################################ // *************************************************************************** // インクルード // *************************************************************************** // 標準ライブラリ #include <iostream> #include <fstream> // 構造体定義 #include "main.h" // Theolizerライブラリ(各種シリアライズ指示する時は構造体定義前に#include) #include <theolizer/serializer_json.h> // Theolizer自動生成先 #include "main.cpp.theolizer.hpp" // *************************************************************************** // メイン // *************************************************************************** int main(int argc, char* argv[]) { //---------------------------------------------------------------------------- // 保存 //---------------------------------------------------------------------------- // 保存するデータの設定 Trade aTradeSave; aTradeSave.mDate.mYear = 2015; aTradeSave.mDate.mMonth = 1; aTradeSave.mDate.mDay = 1; aTradeSave.mItem = u8"お米10Kg"; aTradeSave.mAmount = 4600; // 保存処理 { std::ofstream aStream("test.log"); theolizer::JsonOSerializer<> js(aStream); THEOLIZER_PROCESS(js, aTradeSave); } //---------------------------------------------------------------------------- // 回復 //---------------------------------------------------------------------------- // 回復先の領域 Trade aTradeLoad; // 回復処理 { std::ifstream aStream("test.log"); theolizer::JsonISerializer<> js(aStream); THEOLIZER_PROCESS(js, aTradeLoad); } // 結果表示 std::cout << theolizer::print("%04d/%02d/%02d %s %u\n", aTradeLoad.mDate.mYear, aTradeLoad.mDate.mMonth+0, aTradeLoad.mDate.mDay+0, aTradeLoad.mItem, aTradeLoad.mAmount); return 0; }
保存処理は下記の4ステップです。
- std::ofstream wStream(“test.log”);
test.logファイルを書き込みオープンします。 -
theolizer::JsonOSerializer<> js(wStream);
Theolizer®に対して上記でオープンしたファイルへシリアライズするよう指示します。 -
THEOLIZER_PROCESS(js, wTradeSave);
事前に設定していたwTradeSave変数の内容をtest.logファイルへ出力するよう指示しています。 -
{ }ブロック終了
ブロックから抜けた時にファイルがクローズされて書き込みが完了します。
ところで、領域確保やメンバへの値設定はユーザ・インタフェース部で通常行います。その部分については解説の対象としませんので、今回は直接設定しています。
4-2.保存された内容
{ "SerialzierName":"JsonTheolizer", "GlobalVersionNo":1, "TypeInfoList":[1] } { "mDate":{ "mYear":2015, "mMonth":1, "mDay":1 }, "mItem":"お米10Kg", "mAmount":4600 }
下記の4ステップで回復されます。
1.std::ifstream wStream(“test.log”);
test.logファイルを読み出しオープンします。
- theolizer::JsonISerializer<> js(wStream);
Theolizer®に対して上記でオープンしたファイルからデシリアライズするよう指示します。 -
THEOLIZER_PROCESS(js, wTradeLoad);
事前に確保していたwTradeLoad変数へtest.logファイルの内容を回復するよう指示しています。 -
{ }ブロック終了
ブロックから抜けた時にファイルがクローズされて読み出しが完了します。
4-3.実行結果
2015/01/01 お米10Kg 4600
4-4.CMakeLists.txt
4-5.自動生成されたソース
5.インクルードするヘッダについて
main.cppで#include <theolizer/serializer_json.h>をインクルードしています。これはJsonフォーマットでシリアライズ/デシリアライズするための派生Serializerです。使いたい派生Serializerのヘッダをインクルードすることになります。現在はJsonとBinary、ちょっと特殊なFastの3種類を用意しています。
そして、main.cppで#include “main.cpp.theolizer.hpp”をインクルードしています。これはある意味Theolizer®を使うためのオマジナイです。
THEOLIZER_PROCESS()を呼び出しているcppファイルで、保存/回復したい構造体やクラスの定義の後、THEOLIZER_PROCESS()を呼び出す前にインクルードして下さい。
6.Theolizerで用いているコア技術
構造体やクラスをシリアライズ/デシリアライズするためには、それらのメンバ・リストをC++プログラムが把握する必要があるのですが、リフレクション機能のないC++単体ではそれを自動化することができません。
そこで、外部ツールで構文解析を行い、必要な情報をソース・コードとして自動生成しています。そのファイルがmain.cpp.theolizer.hppです。
その外部ツールであるTheolizer®ドライバがユーザ・プログラムのソース・コードをclangのlibToolingを用いて構文解析し、シリアライズ/デシリアライズするために必要なコードを自動生成しているのです。(リンク先の記事の著者Chironianは私です。プライベートではChironianのハンドルで活動します。)
以上により、リフレクション機能のないC++言語でもメンバを追加した際に保存/回復のためのコードを手で修正することなく保存/回復できるようになりました。
7.まとめ
以上で、簡単なデータ構造を保存/回復する使い方の説明は終わりです。単純な対象ですが、保存/回復処理を簡単に行えることを説明できたと思います。
次回以降で、もう少し複雑なデータ構造をもう少し複雑な保存/回復するための使い方を解説していきます。保存/回復のために少し工夫は必要ですが、かなり複雑なものでも実処理は数行で保存/回復できます。それが分かるような解説にする予定です。