変換するための雛形
雛形ライブラリ
DLL

!配布条件
Apolloの配布条件は、Rubyの配布条件に従いたい、というのが作者の意向である。しかし、ApolloはDelphi 6で作成したアプリケーションであるから、Borlandが定めた配布条件に縛られる。

!Delphiソースの扱い
Borlandが定めた配布条件を守ること。
: license.txt : Borland Delphi 6 使用許諾契約
: deploy.txt : Delphi アプリケーションの配布について
これらの文書は公開してもよいのかな?

!型チェック
まずい例:
	function StrColGrid_set_cell_params(This, v: Tvalue): Tvalue; cdecl;
	var
	  real: TStrColGrid;
	begin
	  real := ap_data_get_struct(This);
	  real.CellParams := TSCGCellParams(ap_data_get_struct(v)); //危険
	  result := v;
	end;

TSCGCellParamsへのキャストが危険。ap_data_get_struct(v)の型をチェックする必要がある。
Thisからrealへの変換はチェックする必要なし。なぜなら、この関数のThisはStrColGridのインスタンスであることが保障されるから。
不正なキャストを行ったらRubyの例外を飛ばしたい。そのためにap_data_get_objectを使う。

	function StrColGrid_set_cell_params(This, v: Tvalue): Tvalue; cdecl;
	var
	  real: TStrColGrid;
	  CellParams: TSCGCellParams;
	begin
	  real := ap_data_get_struct(This);
	  ap_data_get_object(v, TSCGCellParams, CellParams);
	  real.CellParams := CellParams;
	  result := v;
	end;

ap_data_get_object の定義:
uDefUtils.pas:
	procedure ap_data_get_object(v: Tvalue; AClass: TClass; var Ret);
	var
	  Obj: TObject absolute Ret;
	begin
	  if v = Qnil then
	    TObject(ret) := nil
	  else
	  begin
	    Obj := ap_data_get_struct(v);
	    if not (Obj is AClass) then
	      ap_raise(ap_eArgError,
	        Format('%s %s (expected %s)', [
	           sWrong_arg_type,
	           chopHead(Obj.ClassName),
	           chopHead(AClass.ClassName)
	        ]));
	//    TObject(Ret) := Obj;
	  end;
	end;

これが安全であるのか自信なし。Objに代入した時点でRetは破壊されるのだから、Obj is AClass は気休めでしかないような。Ruby例外が飛んだときには以後Retにアクセスしてはいけない。このことを明記しよう。

ビルド手順:
	D:\apollo\src\ext\strcolgrid>dcc32 -B -Ud:\apollo\src -Ud:\usr\lib\delphi -DVCL strcolgrid.dpr

テスト手順:
	D:\apollo\src\ext\strcolgrid>d:\apollo\bin\ruby_ap -Id:/apollo/bin strcolgridtest.rb

テストコード:
strcolgridtest.rb:
	require 'phi'
	require 'strcolgrid'
	
	include Phi
	
	form1 = Form.new
	
	btn = Button.new form1
	btn.align = Phi::AL_TOP
	
	scgrid1 = StrColGrid.new form1
	scgrid1.cell_params = btn

publishedプロパティはApolloが自動的に定義する。

実際、 TStrColGrid.CellParams はこれに該当するので、 rb_define_method ( 書き込みプロパティの定義には普通 DefineAttrSet を使うが ) によるRuby側の明示的な定義は行う必要がない。 StrColGrid_set_cell_params は定義しなくてよいことになる。

TObjectなプロパティの書き込みメソッドはProp_set_objectになる。
Prop_set_objectは型チェックを行っていない。

	function Prop_set_object(This, v: Tvalue): Tvalue; cdecl;
	var
	  real: TObject;
	  name: PChar;
	  objv: TObject;
	begin
	  real := ap_data_get_struct(This);
	  name := rb_id2name(rb_frame_last_func);
	  objv := ap_data_get_struct(v); //危険
	  try
	    SetObjectProp(real, chopUnder(name), objv);
	  except
	    on E: Exception do
	      ap_raise(eDelphiError, PChar(E.message));
	  end;
	  if objv is TComponent then
	    TComponent(objv).tag := v;  // no effect
	  rb_iv_set(This, PChar('@'+chop(name)), v);
	  result := v;
	end;

今の実装では、Prop_set_objectはvのDelphi側の型を知り得ないから、型チェックは行えない。
テスト結果:
	strcolgridtest.rb:23: undefined method `add' for #<Phi::Button:0xd25d58> (NameError)

…と、これまで考えていたのだが、
	function GetObjectPropClass(Instance: TObject; const PropName: string): TClass; overload;
を使えば、Delphi側の設定すべき型がわかるではないか。

	function Prop_set_object(This, v: Tvalue): Tvalue; cdecl;
	var
	  real: TObject;
	  name: PChar;
	  AClass: TClass;
	  objv: TObject;
	begin
	  real := ap_data_get_struct(This);
	  name := rb_id2name(rb_frame_last_func);
	  AClass := GetObjectPropClass(real, chopUnder(name));
	  ap_data_get_object(v, AClass, objv);
	  if objv is TComponent then
	    TComponent(objv).tag := v;  // no effect
	  rb_iv_set(This, PChar('@'+chop(name)), v);
	  result := v;
	end;

テスト結果:
	strcolgridtest.rb:12:in `cell_params=': wrong argument type Button (expected SCGCellParams) (ArgumentError)

より安全になった。

!Delphiの型チェックの抜け穴
上の考察の中で、Delphiの型チェックを回避する方法がいくつか見つかったので、まとめてみよう。

* var，const，out
""型なしパラメータ
""定数パラメータ，変数パラメータ，出力パラメータは，型を指定しても指定しなくてもかまいません。
""通常，コンパイラは型なしパラメータに対する操作が有効かどうかを確認できません。
* absolute
""絶対アドレス
""ほかの変数と同じアドレスを持つ新しい変数を作成することができます。

* SetObjectProp
  RTTI ( unit TypInfo )
	procedure SetObjectProp(Instance: TObject; const PropName: string;
	  Value: TObject); overload;

