2017年6月7日 Theolizer®最新版v1.1.0へ対応するために修正しました。
最新版のソース一式をGistに置いてます。
こんにちは。田原です。
以前、C#のシリアライザを使っていてenum型がシンボル名で保存されることに感心したことがあります。enum型は各シンボルの値を処理系任せで使う場合も少なくありません。その時、値で保存するとシンボルの追加/削除を自由にできないため、たいへん辛いです。そこで、Theolizer®は値での保存/回復だけでなくシンボル名での保存/回復もできるようしました。
実は、C#のシリアライザで辛かった問題があります。シンボル名を変更するとデシリアライズできなくなります。シンボル名で保存している以上 当たり前なのですが、enum型を定義した当初は結構シンボル名を変更するため、これが地味に辛かったです。
そこで、シンボル名の変更にも対応しました。同じ手法で値で保存/回復する時の値の変更にも対応してみました。
これらの使い方をご紹介します。
1.基本操作
enum型はその内容を保存/回復する時、シンボル名を使うかシンボルに割り当てられた値を使うか選択できます。
1-1.シンボル名保存
デフォルトはシンボル名ですので、特に何も指定しなければシンボル名で保存されます。
以下に関連部を抜粋します。これをビルドして走らせるとJsonファイルが出力されます。
enum WeekDay { ewdWeekDay0, ewdWeekDay1, ewdWeekDay2, ewdWeekDay3, ewdWeekDay4, ewdWeekDay5, ewdWeekDay6, };
WeekDay aWeekDay = ewdWeekDay1; { std::ifstream aStream("test.json"); theolizer::JsonOSerializer<> js(aStream); THEOLIZER_PROCESS(js, aWeekDay ); }
"ewdWeekDay1"
1-2.値保存
値で保存する時はTHEOLIZER_ENUM_VALUE()マクロで指定します。
enum Color { ecColor0, ecColor1, ecColor2 }; THEOLIZER_ENUM_VALUE(Color, 1);
Color aColorA=ecColor1; { std::ifstream aStream("test.json"); theolizer::JsonOSerializer<> js(aStream); THEOLIZER_PROCESS(js, aColorA); }
1
2.互換性のある定義変更
enum型の定義変更は容易です。そして、特定の制限を守ればかなり自由に変更できます。
2-1.シンボル名保存の値割り当て変更
ある意味当たり前ですが、シンボル名で保存しているenum型の各シンボルへの値の割当は自由に変更できます。
例えば「1-1.シンボル名保存」のWeekDayを下記のように変更しても出力されるJsonファイルは変化しませんので、変更前のプログラムで保存したデータを変更後のプログラムで回復できますし、逆も可能です。
enum WeekDay { ewdUnknown, ewdWeekDay0=100, ewdWeekDay1, ewdWeekDay2, ewdWeekDay3, ewdWeekDay4, ewdWeekDay5, ewdWeekDay6 };
2-2.値保存のシンボル名変更
同様に、値保存しているenum型のシンボル名を自由に変更できます。
例えば「1-2.値保存」のColorを下記のように変更しても出力されるJsonファイルは変化しませんので、変更前のプログラムで保存したデータを変更後のプログラムで回復できますし、逆も可能です。
enum Color { ecActiveBorder, ecActiveCaption, ecAppWorkspace }; THEOLIZER_ENUM_VALUE(Color, 1);
なお、次のように変更しています。
- ecColor0 → ecActiveBorder
- ecColor1 → ecActiveCaption
- ecColor2 → ecAppWorkspace
3.より大きな定義変更
冒頭で述べたようにC#では値の割り当てを変更できるので便利でしたが、開発の初期にシンボル名を変更出来ない点でちょっと苦しみました。
Theolizer®は下位互換(変更後のプログラムが出力したデータを変更前のプログラムで回復できること)を断念すれば、シンボル名の変更にも対応しています。
3-1.シンボル名保存時のシンボル名変更
原理は単純です。シンボル名を変更する時に、変更前のシンボル名をTheolizer®に登録するだけです。Theolizer®はそれを見て、古いシンボル名を検出したら新しいシンボル名へ回復します。
この指定はTHEOLIZER_ANNOTATE()マクロをシンボルの直後において指定します。(値指定や,の後に置くと無視されてしまいますので要注意です。)
THEOLIZER_ANNOTATE(ES:旧シンボル名)です。
旧シンボル名は,(カンマ)で区切って複数指定できます。
enum WeekDay { ewdUnknown, ewdSunday THEOLIZER_ANNOTATE(ES:ewdWeekDay0)=100, ewdMonday THEOLIZER_ANNOTATE(ES:ewdWeekDay1), ewdTuesday THEOLIZER_ANNOTATE(ES:ewdWeekDay2), ewdWednesday THEOLIZER_ANNOTATE(ES:ewdWeekDay3), ewdThursday THEOLIZER_ANNOTATE(ES:ewdWeekDay4), ewdFriday THEOLIZER_ANNOTATE(ES:ewdWeekDay5), ewdSaturday THEOLIZER_ANNOTATE(ES:ewdWeekDay6) };
この定義を行うことで、旧バージョンで保存したシリアライズ・データを適切に回復できます。
そして、定義変更後のenum型を出力すると、当然ですが定義変更後のシンボル名で出力されます。
"ewdMonday"
さて、将来の変更を予測できないため旧プログラムはewdMondayを解釈するために必要な情報を持ちません。従って、この機能を使った場合は、新プログラムで保存したデータを旧プログラムは回復できません。もしも、無理に回復させようとすると下記エラーとなります。
Can not find loaded value(0) in Version.1's symbol. Please check THEOLIZER_ANNOTATE() for Color.
3-2.値保存時の値変更
シンボル名を変更する方法と同じ原理です。値を変更する時に、変更前の値をTheolizer®に登録するだけです。Theolizer®はそれを見て、古い値を検出したら新しい値へ回復します。
この指定はTHEOLIZER_ANNOTATE()マクロを使います。
THEOLIZER_ANNOTATE(ES::旧値)です。(:が2つあります。)
旧値は,(カンマ)で区切って複数指定できます。
enum Color { ecDefault =0x00000000, ecBlack =0x00000000, ecWhite =0x00ffffff, ecRed =0x00ff0000, ecGreen =0x0000ff00, ecBlue =0x000000ff, ecActiveBorder THEOLIZER_ANNOTATE(ES::0) =0x10000000, ecActiveCaption THEOLIZER_ANNOTATE(ES::1), ecAppWorkspace THEOLIZER_ANNOTATE(ES::2) }; THEOLIZER_ENUM_VALUE(Color, 1);
THEOLIZER_ANNOTATE()を書く場所について:
自分でテスト用のプログラムを作っていても時々失敗するのですがTHEOLIZER_ANNOTATE(ES:)を書く場所はシビアです。
上記のように「シンボル THEOLIZER_ANNOTATE()=値,」の位置に置かないと無視されます。不正な位置に記述していたらエラーにしたいのですか、現時点ではlibToolingから不正な位置に置かれていることを取得できておらず、エラーを検出できません。将来的には対応したいと考えていますが、まだノーアイデアです。
4.ソース・コードと実行結果
ソース・コードと実行結果をGistへ置いています。
ファイル名 | 説明 |
---|---|
CMakeLists.txt | プロジェクト・ビルド用のCMake設定ファイルです。 |
enum1.cpp enum1.h |
「1.基本操作」のソースです。 |
enum2.cpp enum2.h |
「2.互換性のある定義変更」のソースです。 |
enum3.cpp enum3.h |
「3.より大きな定義変更」のソースです。 |
enum1.cpp.theolizer.hpp enum2.cpp.theolizer.hpp enum3.cpp.theolizer.hpp |
Theolizer®の自動生成ソースです。 |
test1.json | enum1とenum2が出力したJsonファイルです。 この2つの内容は全く同じですので、1つだけ置いてます。 |
test3.json | enum3が出力したJsonファイルです。 |
enum11.log enum12.log enum13.log |
test1.jsonを、それぞれenum1, enum2, enum3にて回復した時の出力です。 |
enum31.log | enum3が出力したJsonデータをenum1, enum2で回復した時のエラー・メッセージです。2つとも同じエラーを出力するので1つだけ置いてます。 |
enum33.log | enum3が出力したデータをenum3で回復した時の出力です。 |
最初にenum1にてaWeekDayA, aWeekDayB, aColorA, aColorBに値を設定し、各enum値の変更は全くしないままプログラム側の定義を変更して回復処理しました。プログラム側の定義変更により、下記のように値が変化しています。
この様子はnum12.log, enum13.logで確認できます。
変数名 | enum1が保持する値 | enum2が保持する値 | enum3が保持する値 |
---|---|---|---|
初期設定 | 値変更 | シンボル名変更 | |
aWeekDayA | ewdWeekDay1(1) | ewdWeekDay1(101) | ewdMonday(101) |
aWeekDayB | ewdWeekDay2(2) | ewdWeekDay2(102) | ewdTuesday(102) |
初期設定 | シンボル名変更 | 値変更 | |
aColorA | ecColor1(1) | ecActiveCaption(1) | ecActiveCaption(0x10000001) |
aColorB | ecColor2(2) | ecAppWorkspace(2) | ecAppWorkspace(0x10000002) |