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.構造体定義
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | //############################################################################ // 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.メイン・プログラム
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | //############################################################################ // 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.保存された内容
01 02 03 04 05 06 07 08 09 10 11 12 13 14 | { "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.まとめ
以上で、簡単なデータ構造を保存/回復する使い方の説明は終わりです。単純な対象ですが、保存/回復処理を簡単に行えることを説明できたと思います。
次回以降で、もう少し複雑なデータ構造をもう少し複雑な保存/回復するための使い方を解説していきます。保存/回復のために少し工夫は必要ですが、かなり複雑なものでも実処理は数行で保存/回復できます。それが分かるような解説にする予定です。