next up previous contents index
Next: Consumer object Up: ジェネリック・オブジェクトの概略 Previous: ジェネリック・オブジェクトの種類

   
Data object

Data objectについては浮動小数点型を実装するために用いられている floatクラスのオブジェクトを例として説明する(runtime/gfloat.c)

Data objectに関してはinclude/klic/gdobject.h, include/klic/gd_macro.h 中に定義されたマクロを用いて記述する。

データ構造:
Float型は、基本的に、

struct {
  struct data_object_method_table *method_table;
  double value;
};
という型である。これを定義するのに、

GD_OBJ_TYPE {
  struct data_object_method_table *method_table;
  double value;
};
とする。各々、メソッド表ポインタ、浮動小数点の値、である。

以下でメソッドの説明をする。データオブジェクトのメソッドは``GDDEF_'' で開始されるマクロにより行われている。

単一化:
KL1でデータに対して行う単一化操作は、汎用的なものとして、 以下の2つがある。

GUNIFY メソッド:
引数である他の data object と自分の passive unification を試みる。 メソッド実行の結果としては、成功、失敗、中断の3種類である。 それぞれの状態に対応する値 [*] をメソッドの返り値としている。

Float型では、まず、メソッドテーブル同士を同じかどうか比較し、 その後、value部分を比較している。

UNIFY メソッド:
引数である他の object と自分の active unification を試みる。 メソッド実行の結果としては、成功、中断の2種類である。 失敗ならばGD_GUNIFY_FAIL により、abortする。

印字:
実行時カーネルのプリントルーチンが Data object を発見した場合に呼びだされる。 次のメソッドを呼ぶ。

PRINT メソッド:
引数は、 UNIX の FILE構造体へのポインタ、 オブジェクトのプリントの深さや幅を示す値である。 その FILE 構造体へのポインタが示す UNIX のストリームに自分の印字内容を流す。

PRINTメソッドはGD_RETURN_FROM_PRINTにてreturnする必要がある。

GC:
GC時に生きているオブジェクトの(新領域への)コピーの仕方を記述した メソッドである。実行時カーネルのGCルーチンがデータ 追跡中にジェネリック・オブジェクトを発見したときに利用される。 このメソッドがあるため、タグを持たない要素(例えば、floatのvalue部) を持ったオブジェクトでも安心して作成することができる。 通常は、自分自身をコピーする。内部にKL1の項を持つ場合などは 再帰的にコピーさせる必要がある。

なお、KLICのGCは幅優先ではなく、深さ優先の順で データのコピーを行う。よっていわゆる``scan''は行わず、 ジェネリック・オブジェクト内部で保持しているタグのないデータも 上手く扱うことができる。

メソッドでは、コピー先ヒープのヒープ割付点へのポインタを引数に持つ。 自分のサイズに従って、コピー先ヒープ上に領域を確保し(ヒープ割付点を進める)、 その自分をそこへコピーする。 メソッドの返り値はコピー後のヒープ割付点へのポインタを返す。

gfloatでは、value部が8byte alignされている必要がある場合が多いため、 alignを合せるためヒープを進めている。

ジェネリック・メソッド:
今までのメソッドは、基本的には実行時カーネルから直接呼び出され、 実行時カーネルが行うべき仕事を肩代りして行うために記述された メソッドであった。これらのオブジェクトは実装されていることが必須である。

これらのメソッド の他に、ユーザが自由に定義し、ジェネリック・オブジェクトを任意の時 点で任意に操作するための様々なメソッドが定義できる。 例えば、floatでは、各種の演算が行えなければ、 floatを実装する意味はあまりない。 このような、オブジェクトに依存するようなメソッドをジェネリック・メソッドと 呼ぶ。

ジェネリック・メソッドにはガード用とボディ用があり、 2 つ (1 つのジェネリック・オブジェクトクラス当たり 2 つ) のメソッドとして 実装される。 ジェネリック・メソッドを呼ぶ時には、そのメソッド群の中の 1 メソッド を指定するメソッド記述子を引数として呼ぶことで、 実行したいメソッドを指定する。

body generic メソッド:
ボディ用のジェネリック・メソッドである。 引数は、メソッド記述子およびジェネリック・メソッド引数、 そしてヒープ割付点へのポインタである。

Body generic メソッドは、以下の2種より構成される。

  • 様々な独立した処理を記述したメソッド。floatでは、 print_1 とint_1が定義されている。

  • 上記も含めた様々な処理にdispatchするためのメソッド。 つまり、コンパイルコードからはこのメソッドが直接的に呼びだされ、 dispatchを行い、必要であれば、上記個別処理に飛ぶようになっている。

前者はGDDEF_METHOD(メソッド名_アリティ)として定義される。 後者は、GDDEF_GENERICにより定義される。floatの場合を例にして基本的な処理を 以下で説明する。

1.
GD_SWITCH_ON_METHOD なるマクロで、method名/アリティにより dispatchされる。個別の処理(GDDEF_METHODにより定義される処理)に 飛ぶ為には、このSWITCHの中でGD_METHOD_CASE(method名/アリティ) として 定義をしておく。
2.
残りについてdefault: で受け、 アリティでSWITCHするため、GD_SWITCH_ON_ARITY を用いる。

その結果、case 1(アリティが1)、 case 2(アリティが2)の場合で各々 GD_SWITCH_ON_METHOD により再度dispatchしている。 その結果行うべき処理を GD_METHOD_CASE_DIRECTで記述する。別にGDDEF_METHODに より定義せずに、直接記述する(単順な処理の場合にはそのような 記述が適当であろう)場合にはこの記述が便利である。

guard generic メソッド:
ガード用のジェネリック・メソッドである。 この場合も、以下を除きbodyとほぼ同様である。

  • 個々の処理のためにはGDDEF_GMETHODマクロを利用する。

  • DispatchのためにはGDDEF_GGENERICマクロを利用する。 その他、GD_SWITCH_ON_GMETHOD, GD_GMETHOD_CASE などと記述する。

引数は、メソッド記述子およびジェネリック・メソッド引数である。 このメソッドはガードゴールに相当する。 メソッドを実行した結果は成功/失敗/中断のいずれかになるが、 それは戻り値により表現する(GENERIC_SUCCEEDED 成功、 GENERIC_FAILED: 失敗、それ以外: 中断の原因となった変数への参照)。 詳細はinclude/klic/generic.h のguard_genericマクロを参照のこと。

new ルーチン:
メソッドは、既にヒープ上に生成されたジェネリック・オブジェクトのメソッド 表に登録されたものである。 メソッド表には、ジェネリック・オブジェクトを生成するためのメソッドを 含めず、new ルーチンとして、メソッド表とは別に、 各ジェネリック・オブジェクトに対して定義される。 何故なら、生成の時にはオブジェクトは存在せず、結果、 メソッド表へのポインタも存在しないためである。

new ルーチンは、ジェネリック・メソッドと同様に、コンパイルされた実行 時ルーチンから呼ばれ得るルーチンである。 また、newルーチンに引数としてデータを渡すことも可能である。 これを new ルーチン引数と呼ぶことにする。

このルーチンは引数として、newルーチン引数とそのサイズおよび ヒープ割付点へのポインタを取る。 ヒープ上に領域を取り、そこにジェネリック・オブジェクトを生成する。 新たなヒープ割付点をルーチンの返り値として返す。



Sekita Daigo
1998-05-18