使い方

「スキーマファイル」の記述

アプリケーションのデータ構造(データ型定義の集合)を、 SERでは「スキーマ」と呼びます。 アプリケーションプログラマは、 まず「スキーマファイル」を記述します。 このファイルは、Cのヘッダファイルに似ていますが、 unionが実はどれかをenumで限定する等の修正が加えられています (見た目は全然Cには似てないような気もします。 Cの(腐った)宣言の構文を真似すると、パーサを書くのが面倒だったので)。

スキーマファイルの例

スキーマファイルの文法


スキーマファイルのコンパイル

「スキーマファイル」を「スキーマコンパイラ」にかけると、 「AP向けヘッダファイル」と「内部情報ヘッダファイル」が生成されます。

スキーマ名(スキーマファイルの冒頭で指定)が"hoge"の場合、 「AP向けヘッダファイル」のファイル名はhoge.hに、 「内部情報ヘッダファイル」のファイル名はhoge_i.hになります。

% schemac hoge.schema
% ls
hoge.schema     hoge.h          hoge_i.h
%

「AP向けヘッダファイル」には、アプリケーション向けの型定義が 記述されており、アプリケーションプログラマは これを元にアプリケーションをコーディングします。

「内部情報ヘッダファイル」には、 SER向けの内輪の情報が記述されています。 アプリケーションプログラムは、ソースファイルのどこか一箇所で、 内部情報ヘッダファイルを#includeする必要があります。 内部情報ヘッダファイルの内容については、 アプリケーションプログラマは関知する必要はありません。


ストレージの生成

「ストレージ」とは、アプリケーションが生成する オブジェクトの貯蔵庫です。SERでは、シリアライズ・デシリアライズ・ ガベージコレクト等の操作は、ストレージを単位に行ないます。

ストレージは、ひとつのスキーマに対応します(n:1対応)。 アプリケーションは、スキーマ情報へのポインタ(具体的な内容は 内部情報ヘッダファイル中に定義されている)を使用して、 ストレージを生成します。

/* スキーマ名が"hoge"の場合のストレージの生成 */
/* AP向けヘッダファイル内でSER_hoge_schemaというグローバル変数が
   extern宣言されている。 */
SER_Storage storage;

storage = SER_create_storage(SER_hoge_schema);

オブジェクトの確保

アプリケーションプログラムは、malloc()の代わりに、 ストレージとデータ型を指定して領域を確保します。

static const SER_Type type[] = {SER_TYPE_Hoge};

p = SER_malloc(storage, type);

ここで、"SER_TYPE_Hoge" は、スキーマファイル中で "Hoge"という名前で定義したデータ型を示す記号定数であり、 「AP向けヘッダファイル」で #define されています。

ここで、typeにstatic const指定が付いていますが、 これは重要です。 シリアライズやガベージコレクトを行なうためには、 確保したオブジェクトについてそれぞれ型情報が必要ですが、 SERは、効率向上のため、わざわざ別に領域を確保して 型情報を保持するようなことは行いません(デシリアライズの時は別)。 SER_mallocの際に与えられたポインタを保持するだけです。

よって、ここでstatic指定なしで、単なる自動変数として typeを宣言すると、関数を抜けた後、型情報が壊れてしまいます (constは、まあ、どっちでもいいですが)。

SERにより確保した領域は、 そのデータ型の初期値で初期化されています。

配列を確保する場合は以下のようになります。

/* SER_TYPE_Hoge 10個分の配列を確保 */
static const SER_Type type[] = {SER_TYPE_ARRAY, 10, SER_TYPE_Hoge};

p = SER_malloc(storage, type);

可変長配列を確保する場合は、以下のようにします。

static SER_Type type[] = {SER_TYPE_Hoge};
Hoge   *p;

/* size個の可変長配列を確保 */
p = SER_malloc_array(storage, type, size);

SER_malloc_arrayで確保した可変長配列は、 その要素数をSER側で記憶しています。

size = SER_get_array_size(storage, p); /* 要素数の取得 */

リサイズも可能です。

p = SER_resize(storage, p, new_size); /* new_sizeは要素数 */

リサイズにより配列を縮めた場合、要素が後ろから単純に捨てられます。

逆に、伸ばした場合、後ろの要素は、 そのデータ型のデフォルト値で初期化されています。


ルートオブジェクト

アプリケーションプログラムは、 データ構造のルートとなるデータ(ルートオブジェクト)のポインタを、 ライブラリに教える必要があります。 また、ルートオブジェクトが必要な時には、ライブラリから取得します。 ルートオブジェクト(に限らず全てのオブジェクト)のアドレスは、 コンパクションにより変更される可能性があるので、 アプリケーションが継続的に保持すべきではありません。

/* ルートオブジェクトの設定 */
SER_set_root_object(storage, root);

/* ルートオブジェクトの取得 */
root = SER_get_root_object(storage);

ガベージコレクション

ルートオブジェクトから 必要な全てのオブジェクトを辿ることができる状態のとき、 アプリケーションプログラムは「ガベージコレクト」を 行うことができます。

SER_garbage_collect(storage);

ガベージコレクトでは、以下の処理を行ないます。

  1. ルートオブジェクトから到達不能な全てのオブジェクトを解放する。
  2. コンパクションを行なう。

コンパクションの際、オブジェクトのアドレスは変更される可能性があります。 SERの管理下にあるオブジェクト内にポインタが格納されている場合、 そのポインタは自動的に更新されますが、 アプリケーションが独自に(自動(ローカル)変数やグローバル変数で) オブジェクトへのポインタを保持していた場合、整合性が崩れてしまいます。


シリアライズ

ルートオブジェクトから 必要な全てのオブジェクトを辿ることができる状態のとき(つまり ガベージコレクトと同じ条件)、 アプリケーションプログラムは「シリアライズ」を行なうことができます。

SER_serialize(storage, encode_type_name, fp);

シリアライズにより、指定したストレージの全オブジェクトがシリアライズされ、 指定したストリーム(大抵はファイルポインタ)に出力されます。

第2引数(encode_type_name)は、 シリアライズの際のエンコード方式を指定しますが、 現時点では、デフォルトで使用できるencode_type_nameは、 "ascii_text"のみです。(^^;


デシリアライズ

シリアライズしたデータを復元(デシリアライズ)する時には、 以下のように記述します。

status = SER_deserialize(SER_new_hoge_schema,
                         encode_ype_name, fp, &storage);

この時、スキーマを指定していますが、このスキーマは必ずしも シリアライズした時のスキーマと完全に同一である必要はありません。 多少の違い(拡張)であれば、SERがその違いを吸収し、 新しいスキーマに合わせてデータ構造を復元します。


ひとつ上のページに戻る | prev | next(動作原理) |