立石です.

Ruby/DLの開発にあたって,悩んでいる点があるので相談にのって頂け
ると嬉しいです.簡単には,オブジェクト自身のtypeを変更して,もっ
ているデータ構造自体も変更してしまうというのはナンセンスかどうか?
という問題です.直観的には,

figure = Figure.new
p figure    => #<Figure:0xXXXXXXX ...>
p figure.id => 1000
figure.cast!(Circle, 4)
p figure    => #<Circle:0xXXXXXXX ...>
p figure.id => 1000

という感じのdowncastみたいなことはナンセンスかどうか? ということ
です.

現在リリースしているRuby/DL v0.6では,PtrDataというCのポインタを
表現するためのクラスがあります.説明をするにあたりこのPtrDataの特
徴をまず説明します.

PtrDataの説明:
  Cのポインタptr1,ptr2の2つがあり,ptr1,ptr2を表すRubyの
  PtrDataオブジェクトをR(ptr1),R(ptr2)とします.
  このとき (ptr1 == ptr) <==> (R(ptr1).id == R(ptr).id)
  が成り立ちます.つまりポインタの値とRubyのオブジェクト
  が1:1に対応する仕組みになっていて,重複したPtrDataオブ
  ジェクトを作れないようになっています.

なぜこのような特徴を持つ必要があるか?
  PtrDataオブジェクトはGCされたときにfreeする関数をユーザ
  が定義可能です.これはPtrData#free=というメソッドによって
  設定できます.そのため,同じポインタを表す複数のPtrDataオ
  ブジェクト間で整合性をとるのが難しいと感じたためです.

さて,このようなPtrDataオブジェクトに対して,StructDataというCの構造
体へのポインタを表現するためのクラスもあります.そして,PtrDataオブ
ジェクトからStructDataオブジェクトへCで言うところのキャストを行ないた
いのですが,ここで先に述べたdonwcastを行なおうと考えています.
別提案も含めて皆さんはどのように思いますか?

また,現在のcvs版のものは以下のコードによって既に実装していますが,別
の簡単な方法や,間違いなどあれば指摘して下さい.

rb_dlptr_cast(int argc, VALUE argv[], VALUE self)
{
  VALUE klass, rest, val, *pass_argv;
  int pass_argc, i;
  struct ptr_data *data;

  Data_Get_Struct(self, struct ptr_data, data);
  rb_scan_args(argc, argv, "1*", &klass, &rest);

  /* prepare arguments of `new' method */
  pass_argc = argc + 1;
  pass_argv = ALLOCA_N(VALUE, pass_argc);
  pass_argv[0] = DLLONG2NUM(data->ptr);
  pass_argv[1] = rb_dlsym_new(data->free, NULL, NULL);
  for( i=2; i < pass_argc; i++ ){
    pass_argv[i] = rb_ary_entry(rest,i-2);
  };

  /* remove the data */
  ((struct ptr_data *)(RDATA(self)->data))->ptr = 0;
  (RDATA(self)->dfree)(RDATA(self)->data);
  
  /* call the `new' method of klass with prepared arguments */
  val = rb_funcall2(klass, rb_intern("new"), pass_argc, pass_argv);

  RDATA(self)->basic.klass = RDATA(val)->basic.klass;
  RDATA(self)->basic.flags = RDATA(val)->basic.flags;
  RDATA(self)->dmark = RDATA(val)->dmark;
  RDATA(self)->dfree = RDATA(val)->dfree;
  RDATA(self)->data = RDATA(val)->data;

  RDATA(val)->dmark = 0;
  RDATA(val)->dfree = 0;

  return Qnil;
};
-- 
Takaaki Tateishi <ttate / jaist.ac.jp>