Theolizer
Version.1.2.0
serializer for C++ / Do you want to update your classes easily ?
|
ここでは、Theolizerの使い方を説明します。
TheolizerはAPIの機能テストをできるだけ自動化してます。
そして、APIの機能テストは詳細仕様を規定することでもあります。複雑なプログラムでは文書として読み取ることは困難ですが、単純なプログラムであれば、文書としての機能を果たせるのではないかと考えています。
そこで、各機能についてそれをテストするための多数のテスト群の内、代表的なものを使って説明し、詳細テストの関連部分のソースも提示します。できるだけ読み取るのに苦労しない形式でプログラムを記述し、詳細仕様書として有用であることを目指します。
まず、1~3節で全体的な説明を行い、4節以降で各機能について説明します。
TheolizerのAPIは名前空間に入れています。また、マクロは決まったプリフィクスを付けています。 少し意味を持たせていますので説明します。
ほぼ全てのシンボルを theolizer 名前空間へ入れています。
一部、入れると実装が難しいものについては、シンボルに Theolizer を含みます。
これにより既存のコードと被ることはまずない筈です。
次に、内部的に使用するシンボルは internal 名前空間へ入れています。
これらのシンボルが付けられたクラスや関数等は、Theolizerのアップデート時、上位互換性を考慮しませんので使用しないようお願いします。
全てのマクロは THEOLIZER_ で始めています。
これにより既存のコードと被ることはまずない筈です。
次に、内部的に使用するマクロは、 THEOLIZER_INTERNAL_ で始めています。
また、Theolizerが自動生成するマクロを、 THEOLIZER_GENERATED_ で始めています。
これらのマクロは、Theolizerのアップデート時、上位互換性を考慮しませんので使用しないようお願いします。
Theolizerをインストールした後、あなたのデータをTheolizerでシリアライズするために、あなたのプログラムを下記の順序を守るようにして下さい。
一部例外(*1)はありますが原則として、シリアライズするクラスとenum型の定義 前にシリアライザのヘッダをインクルードして下さい。
Json形式シリアライザ・ヘッダのインクルードと型定義の例:(source/samples/example/example.cpp)
現在、サポートしているシリアライザはJson形式、独自Binary形式、Xml形式、メモリ内専用のFast形式の4種類です。 それぞれ、下記ヘッダをインクールドして下さい。
形式 | インクルードするヘッダ | 注意事項 |
---|---|---|
Json形式 | <theolizer/serializer_json.h> | fstreamはテキスト・モードでオープンする |
独自Binary形式 | <theolizer/serializer_binary.h> | fstreamはバイナリ・モード(std::ios_base::binary)でオープンする |
Xml形式 | <theolizer/serializer_xml.h> | fstreamはテキスト・モードでオープンする |
メモリ内専用Fast形式 | 上記のどちから、もしくは、<theolizer/serializer.h> | fstreamはバイナリ・モード(std::ios_base::binary)でオープンする |
シリアライズするクラスとenum型の定義後に、Theolizerの自動生成ファイルをインスクルード します。この順序に例外はありません。
なお、シリアライズ対象の型を含まないヘッダ・ファイルは、Theolizerに関する順序制限はありません。
4.Theolizerの仕組み で説明したようにTheolizerはシリアライズに必要なソース・コードを自動生成します。
このファイルはコンパイル単位(通常は.cppファイル)毎に生成され、当該.cppファイルと同じフォルダへ自動的に生成されます。
Theolizerは、バージョンを上げた時に古いクラス定義やenum型定義をここに残します。これにより古いシリアライズ・データを回復できます。
そこで、このファイルをお使いのバージョン管理システム(gitやsvn等)へ登録することをお勧めします。
自動生成するファイルのファイル名は、そのコンパイル単位のファイル名に".theolzier.hpp"を繋げたものです。
例えば、example.cpp の場合は、example.cpp.theolizer.hpp となります。
クラス定義と*.theolizer.hppインクルード指定例:(source/samples/example/example.cpp)
この例では、example.cppの頭でクラスを定義していますが、example.hで定義し、#include "example.h" にてインクルードするのが一般的です。
シリアライズは下記の3つで行います。
その際、シリアライズ先のデータ・ストリームを指定します。ファイル・ストリームやTCP/IPストリームを指定して下さい。
保存や送信時はstd::ostreamを、回復や受信時はstd::istreamを与えて下さい。
下記マクロでシリアライズします。保存用シリアライザを指定すると保存、回復用シリアライザを指定すると回復動作となります。
これはいつくでも記述して良いです。
THEOLIZER_PROCESS(dSerializer, dInstance)
dInstanceを保存/回復します。
ポインタ型を指定した場合は、アドレス回復のためにポイント先のオブジェクト追跡を行います。
THEOLIZER_PROCESS_POINTEE(dSerializer, dInstance)
THEOLIZER_PROCESS_OWNER(dSerializer, dInstance)
最後にシリアライザ・インスタンスを破棄することでシリアライズ処理を完了します。
生成時に例外発生を禁止していた場合に、エラー状態をリセットしないまま破棄すると、プロセスをアボートします。これはエラー状態の見逃しを回避するための仕様です。
例外発生を禁止している場合は、破棄する前には必ずエラー情報をチェクした上で、エラー状態をリセットして下さい。
具体的手順はエラー報告 にて説明します。
ファイルへの保存例:(source/samples/example/example.cpp)
ファイルからの回復例:(source/samples/example/example.cpp)
現在、サポートしているシリアライザはJson形式、独自Binary形式、メモリ内専用のFast形式の3種類です。
ここではそれぞれの使い方を説明します。
全てのシリアライザについて共通な事項について説明します。
Theolizerのシリアライザは回復時に型が一致していることをチェックできます。
その方法としてシリライズ・データ内に「型名を保存する方法」と「型に割り当てたインデックス番号(TypeIndex)を保存する方法」の2種類を用意しています。
列挙値 | 意味 |
---|---|
NoTypeCheck | 型チェック無し |
TypeCheck | 型名による型チェック |
TypeCheckByIndex | TypeIndexによる型チェック |
NoTypeCheckは型情報をシリライズ・データに含みませんのでデータ量が少ない場合の効率は良いです。しかし、メンバ名をヘッダではなくデータ側に含むためデータ量が多くなると効率は悪化します。
TypeCheckはテキスト型の場合、データを目視確認し易いです。データ量が少ない時の効率はNoTypeCheckの次に良いです。
TypeCheckByIndexはデータ量が多い時は3種のCheckModeの中で最大の効率を発揮します。
型情報等の管理データを下記のように記録します。
回復時に回復先の変数の型と上記の情報と照らし合わせることで型チェックを行います。
幾つかの制御のため、各シリアライザは下記のメンバ関数を公開しています。
メンバ名 | 意味 |
---|---|
unsigned getGlobalVersionNo() const; | 処理中のグローバル・バージョン番号を返却します。 |
void clearTracking() ; | オブジェクト追跡の区切り(オブジェクト追跡について 参照) |
bool getRequireClearTracking() const; | clearTracking()が必要な時trueを返却します。 |
theolizer::CheckMode getCheckMode() const; | 現在のCheckModeを返却します。 |
void setCharIsMultiByte(); | Windowsにおいて、EncodedStringがtrueのシリアライザにおいて std::string変数の文字エンコードをMultiByte文字列として処理するかどうかを指定します。 |
theolizer::ErrorInfo const& getErrorInfo() const; | エラー情報を返却します。 |
bool isError() const; | エラーが発生している時trueを返却します。 |
void resetError(); | エラー状態を解除します。(3.Theolizerで検出したエラーの通知方法 参照) |
各シリアライザは、その属性をプロバティとして提供しています。
プロバティ名 | 意味 | Json | Binary | Fast |
---|---|---|---|---|
IsSaver | 保存処理用ならtrue、回復処理用ならfalse | – | – | – |
EncodedString | 文字列のエンコードを処理する | true | false | false |
SupportModifying | クラスやenum型の定義変更に対応する | true | true | false |
BinaryOpen | fstreamをstd::ios_base::binaryモードでオープンする必要がある<td>false | true | true |
プロパテイは以下の構文で受け取ります。
テキスト型のシリアライザは、文字列を読める形式で記録されます。
そのため、例えばJsonフォーマットはデフォルトではUTF-8でエンコードすると規定されています。
そして、C++の各std::string
シリーズもUnicodeでエンコードされることが期待されます。(そうしないことも可能です。)
そこで、TheolizerのEncodedStringプロパティをサポートしたシリアライザ(現在はJsonのみ)は、下記のようにstd::string
シリーズを処理します。
std::string
はUnicodeでエンコードされているものとして処理する。(そのまま保存/回復する。)std::wstring
はUTF-16かUTF-32(wchar_tのサイズによる)でエンコードされているものとして処理する。std::u16string
はUTF-16でエンコードされているものとして処理する。std::u32string
はUTF-32でエンコードされているものとして処理する。つまり、例えば、UTF-8でエンコードされたstd::string
型の変数を保存し、それをUTF-16でエンコードされたstd::u16string
型の変数へ回復できます。
サンプル・ソース(source/reference_and_test/basic/test_basic_process.cpp)
バイナリ形式のシリアライザをfstreamで用いる時は、必ずバイナリ・モード(std::ios_base::binary)でfstreamをオープンする必要があります。
Windowsの場合、fstreamがテキスト・モードでオープンされ、ストリームへ数値26(0x1A)が出力されると、0x1AはWindowsではEOFコードなので回復時にEOFエラーになります。また、Windowsで数値10(0x0A)が出力されるとCR LFへ展開されてしまい、適切に回復できません。
このような事態をさけるため、Theolizer側でエラーにしたいのですが、iostreamではそのオープン・モードを確認できないためチェックが困難なのです。
バイナリ形式のシリアライザは、hasProperty(theolzier::Property::BinaryOpen)がtrueになります。 また、各シリアライザはstd::ios_base::openmode型の静的定数kOpenModeを定義しています。 バイナリ形式のシリアライザではstd::ios_base::binary、それ以外のシリアライザで 0 となっています。
サンプル・ソース(source/reference_and_test/basic/test_basic_process.cpp)
Json形式でシリアライズする場合は、theolizer/serializer_json.hをインクルードして下さい。
GlobalVersionNo以外のオプションを指定するコンストラクタ
パラメータ名 | 意味 |
---|---|
iOStream | 出力先のストリーム(ofstreamはテキスト・モードでオープンして下さい) |
iGlobalVersionNo | 保存するグローバル・バージョン番号(省略時は最新版) |
iCheckMode | 型チェック・モード(省略時はNoTypeCheck) |
iNoPrettyPrint | 整形出力しない時true(省略時はfalse) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
パラメータ名 | 意味 |
---|---|
iIStream | 入力元のストリーム(ofstreamならテキスト・モードでオープンして下さい) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
独自Binary形式でシリアライズする場合は、theolizer/serializer_binary.hをインクルードして下さい。
Big Endianでエンコードします。Little Endianの処理系の場合Big Endianとの間で自動変換します。
整数型は値を表現するために十分なバイト数で保存します。例えば、long long型でも値が10ならタグと値で合わせて2バイトで保存します。
浮動小数点型はIEEE754フォーマットのみサポートします。バイト単位でEndian変換します。
long doubleは「radix==2、digits==64、max_exponent==16384」の80ビット拡張精度形式である処理系(gcc)とbinary64である処理系(msvc)に対応しています。
文字コードの変換は行いません。Endianのみ変換してシリアライズします。
GlobalVersionNo以外のオプションを指定するコンストラクタ
パラメータ名 | 意味 |
---|---|
iOStream | 出力先のストリーム(ofstreamはstd::ios_base::binaryでオープンして下さい) |
iGlobalVersionNo | 保存するグローバル・バージョン番号(省略時は最新版) |
iCheckMode | 型チェック・モード(省略時はNoTypeCheck) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
パラメータ名 | 意味 |
---|---|
iIStream | 入力元のストリーム(ofstreamはstd::ios_base::binaryでオープンして下さい) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
Xml形式でシリアライズする場合は、theolizer/serializer_xml.hをインクルードして下さい。
名前空間
名前空間としてhttps://theolizer.com/theoride/xml-1
を使います。(特に何も置いていません。識別用だけです。) 現時点では下記要素名と属性名を定義しています。(接頭辞はthを用いていますので接頭辞付きで表記します。)
識別子 | 要素名/ 属性名 | c++の型 or 意味 |
---|---|---|
th:bool | 要素名 | bool 型 |
th:int8 | 要素名 | int8_t 型 |
th:int16 | 要素名 | int16_t 型 |
th:int32 | 要素名 | int32_t 型 |
th:int64 | 要素名 | int64_t 型 |
th:unit8 | 要素名 | unit8_t 型 |
th:uint16 | 要素名 | uint16_t 型 |
th:uint32 | 要素名 | uint32_t 型 |
th:uint64 | 要素名 | uint64_t 型 |
th:float32 | 要素名 | IEEE754のbinary32型 |
th:float64 | 要素名 | IEEE754のbinary64型 |
th:float80 | 要素名 | IEEE754の拡張型(仮数部64bit、指数部15bit) |
th:string | 要素名 | UTF-8文字列 |
th:Array | 要素名 | 配列(ネストすることで多次元配列を表現する) |
th:Pointer | 要素名 | ポインタ型(1.オブジェクト追跡の使い方 参照) |
th:Pointee | 要素名 | 被ポインタ(1.オブジェクト追跡の使い方 参照) |
th:OwnerPointer | 要素名 | オーナ-・ポインタ(1.オブジェクト追跡の使い方 参照) |
th:Reference | 要素名 | 動的ポリモーフィズムされた参照(1-2-3.参照について 参照) |
th:Type | 属性名 | ポインタの指す先の型 |
th:ObjectId | 属性名 | ポインタ or 被ポインタのオブジェクトID (2-4.同じ領域を複数回シリアライズする時の動作について特記事項 参照) |
th:MemberName | 属性名 | メンバ変数名 |
th:GlobalVersionNo | 属性名 | グローバル・バージョン番号(ヘッダにのみ出現する) |
ユーザ・クラス名 | 要素名 | ユーザ定義のクラス名 |
ユーザenum型名 | 要素名 | ユーザ定義のenum型名 |
各数値型(intやdouble等)は処理系に毎に異なりますが、適切なth:int??, th:float??名が割り当てられます。
std::numelic_limitsのis_signed, digits, max_exponentを用いて振り分けています。
型チェックと要素名の使い方について
Xmlフォーマットを用いるシリアライザは、要素名としてメンバ変数名を用いるものが多いですが、当Xmlシリアライザは要素名として型名を用いることにしました。
以上に理由により、要素名(Xml要素が必ず必要とする名前)を「型名」とし、回復時に必ず型チェックするようにしました。
従って、CheckModeパラメータは指定しません。
GlobalVersionNo以外のオプションを指定するコンストラクタ
パラメータ名 | 意味 |
---|---|
iOStream | 出力先のストリーム(ofstreamはstd::ios_base::binaryでオープンして下さい) |
iGlobalVersionNo | 保存するグローバル・バージョン番号(省略時は最新版) |
iNoPrettyPrint | 整形出力しない時true(省略時はfalse) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
パラメータ名 | 意味 |
---|---|
iIStream | 入力元のストリーム(ofstreamはstd::ios_base::binaryでオープンして下さい) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
サンプルのsource/samples/example/example.cppをxml用に修正した場合の出力例です。
出力
FastSerializerの使用目的はデータ構造のプログラム内コピーです。外部プログラムとのデータ交換は想定していません
Theolizerが内部的に使用していますので、他のシリアライザのヘッダをインクルードすれば改めてヘッダをインクルードする必要はありません。
もし、他のシリアライザを使用しない時は、theolizer/serializer.hをインクルードして下さい。
また、ストリームはstd::stringstreamを用いることを想定していますが、もしも、ファイル・ストリームを与える場合は必ず std::ios_base::binaryモード でオープンして下さい。
FastSerializerはデータ変換しません。バージョンの相違にも対応していません。
オーナー指定ポインタでない通常のポインタは、ポイント先をシリアライズしていない場合はシャロー・コピーになります。(ポインタ値を単純にコピーする。)
パラメータ名 | 意味 |
---|---|
iOStream | 出力先のストリーム(ofstreamはstd::ios_base::binaryでオープンして下さい) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
パラメータ名 | 意味 |
---|---|
iIStream | 入力元のストリーム(ofstreamはstd::ios_base::binaryでオープンして下さい) |
iNoThrowException | 例外禁止時true(省略時はfalse) |
主な機能テスト・プログラムは、 source/reference_and_test 以下にアップデート/バージョン・アップによる変更(修正版)毎にフォルダを分けて保存しています。
Theolizerはenum型とclass/struct型について定義変更に対応しています。そのテストも行うため、以下のように分類してテストを行います。
現時点では下記修正版を用意しています。
フォルダ名 | バージョン番号 | 説明 |
---|---|---|
basic | 無し | 基本的なシリアライズ機能の定義変更を除く様々なバリエーションのテスト |
basic2 | 無し | 同上 |
ver1a | 1 | 最初のバージョン |
ver1b | 1 | バージョン番号を変えずに可能な定義変更のテスト |
ver1c | 1 | バージョン番号を変えるための準備のテスト |
ver2a | 2 | バージョン番号を変更した時の定義変更のテスト |
ver3a | 3 | 更にバージョン番号を変更のテスト |
ver3b | 3 | そしてバージョン番号を変えずに定義変更のテスト |
class/structでメンバ変数を削除した場合、1つ前のバージョンの自動生成ソースに削除されたことを反映します。なので、最新版と1つ前のバージョンと、更にもう1つ前に問題が出ないかテストするため、3つのバージョンでテストします。
各変更テスト・プログラムは他の変更テスト・プログラムが出力したデータを読み込んで回復できることやエラーを検出できることをテストします。
下記組み合わせとなります。1行毎に各修正版プログラムが出力するデータ概要を記述しています。
列に記載した各バージョンのプログラムが当該データの回復テストをする時◯印を付けています。
プロ グラ ム | バー ジョン 指定 | ファイル名 | basic | basic2 | ver1a | ver1b | ver1c | ver2a | ver3a | ver3b |
---|---|---|---|---|---|---|---|---|---|---|
basic | 指定無し | ①-basic-② | ◯ | |||||||
basic | 1 | ①-basic-basic-② | ◯ | |||||||
basic2 | 指定無し | ①-basic2-② | ◯ | |||||||
basic2 | 1 | ①-basic-basic2-② | ◯ | |||||||
ver1a | 指定無し | ①-ver1a-② | ◯ | ◯ | ◯ | ◯ | ◯ | ◯ | ||
ver1a | 1 | ①-ver1a-ver1a-② | ◯ | ◯ | ◯ | ◯ | ◯ | ◯ | ||
ver1b | 指定無し | ①-ver1b-② | ◯ | ◯ | ◯ | ◯ | ◯ | |||
ver1b | 1 | ①-ver1b-ver1b-② | ◯ | ◯ | ◯ | ◯ | ◯ | |||
ver1c | 指定無し | ①-ver1c-② | ◯ | ◯ | ◯ | ◯ | ||||
ver1c | 1 | ①-ver1c-ver1c-② | ◯ | ◯ | ◯ | ◯ | ||||
ver2a | 指定無し | ①-ver2a-② | ◯ | ◯ | ◯ | |||||
ver2a | 1 | ①-ver1c-ver2a-② | ◯ | ◯ | ◯ | ◯ | ||||
ver2a | 2 | ①-ver2a-ver2a-② | ◯ | ◯ | ◯ | |||||
ver3a | 指定無し | ①-ver3a-② | ◯ | ◯ | ||||||
ver3a | 1 | ①-ver1c-ver3a-② | ◯ | ◯ | ◯ | ◯ | ||||
ver3a | 2 | ①-ver2a-ver3a-② | ◯ | ◯ | ◯ | |||||
ver3a | 3 | ①-ver3a-ver3a-② | ◯ | ◯ | ||||||
ver3b | 指定無し | ①-ver3b-② | ◯ | |||||||
ver3b | 1 | ①-ver1c-ver3b-② | ◯ | ◯ | ◯ | ◯ | ||||
ver3b | 2 | ①-ver2a-ver3b-② | ◯ | ◯ | ◯ | |||||
ver3b | 3 | ①-ver3b-ver3b-② | ◯ |
①にはシリアライザ名と一部のオプションが入ります。
②は以下の通りです。
例えば、json整形出力で、ver3aのプログラムがver1cデータを型チェック無し、保存先指定無しで出力したファイル名は、"json-pp-ver1c-ver3a-NoTypeCheck.json"となります。
basic、および、各変更テスト用プログラムは共通部分があります。それらはreference_and_testフォルダ直下に配置し、ビルドする時に各サブ・フォルダへコピーしています。
ファイル | 関数 | 概要 |
---|---|---|
disable_test.h | 各個別テストをディセーブルするシンボル定義。 デバッグ時の便利のために用意。 | |
all_common.h | テスト用の全バージョン共通定義。 アップデートとバージョン名とバージョン番号対応表のgVersionListを定義。 | |
main.inc | main() | 各サブ・フォルダ内のmain.cppから::includeされる。 コマンドライン解析を行い、パラメータが無い時は保存処理、ある時は回復処理を実行する。 4-1節 表の全組み合わせを生成し、callTests()を呼び出す。 |
↑ | callTests() | 各シリアライザのパラメータを振ってインスタンスを生成し、 saveBasic(), loadBasic(),callSaveDestinations(),callLoadDestinations()を呼び出す。 |
ファイル | 関数 | 概要 |
---|---|---|
main.cpp | 各個別フォルダ内テスト関数を呼び出す。 | |
↑ | saveBasic() | 自動テスト基本部の保存処理。個別テストを呼び出す。 |
↑ | loadBasic() | 自動テスト基本部の回復処理。個別テストを呼び出す。 |
↑ | callSaveDestinations() | 自動テスト保存先指定部の保存処理。個別テストを呼び出す。 |
↑ | callLoadDestinations() | 自動テスト保存先指定部の回復処理。個別テストを呼び出す。 |
各個別テストは別途*.cppファイルを用意し、その中で定義しています。
それぞれについては 使用方法(個別) にて解説します。
テスト用のマクロはtest_tool.hで定義しています。
その内、使い方の説明(兼 自動テスト)で用いるマクロについてここで簡単に説明します。
1.THEOLIZER_EQUAL(dLhs, dRhs, ...)
(dLhs == dRhs) ならばPASS、そうでないならFAILと判定します。
PASSならば、テストの数とPASS数をインクリメントします。
FAILならば、テストの数とFAIL数をインクリメントし、テストを失敗させます。
また、dRhsとそれ以降のアイテム(1個以上7個まで)を標準出力へ出力します。
下記はシリアライザを使って回復したint型のaIntの値が-3000であることをチェックしています。