From: Takeshi Horinouchi Date: 2001-12-13T19:32:58+09:00 Subject: [ruby-list:32914] rb_define_module を一回だけ呼びたい 堀之内と申します。 拡張ライブラリーでモジュールを定義するのに使う rb_define_module に関する質問です。Ruby では、 module NumRu class Hoge end end module NumRu module Uge end end のようにすると、NumRu::Hoge はそのままに、NumRu::Uge を追加でき るのですが、拡張ライブラリーで rb_define_module を繰り返すと、前 の定義を上書きしてしまいます。そこをなんとか追加したいというのが 悩みでした。結局その方法は見つかったのですが、Ruby本体の eval.c から何行もコピーすることになり、「これはむしろ rb_define_module の中で対応すべきことなんじゃないか」と思いましたので、是非を伺お うという次第です。 まず、「そもそもなんでそんなことをしたいのか」と思われるかもしれ ませんので、背景を説明します。現在、互いに(それなりに)独立しつ つ、ある程度まとまった目的で使うための、複数のクラス/モジュール 群を開発してます。それらををまとめるため、共通のモジュールの傘に くるもうとしています。具体的には、多次元配列を用いた統計計算や描 画等々なのですが、クラスやモジュールの数が多くなるので、ユーザー が併用するかもしれない他のライブラリーとの名前の競合を避けるよう、 全体を一つのモジュールの傘(上の例では NumRu)にくるみたいと考え てのことです。インストール先も傘に対応したディレクトリーを掘って、 require 'numru/toukei' require 'numru/byouga' これを include するなら、 include NumRu または include NumRu::Byouga などとする、というわけです。このため、拡張ライブラリー内では、そ れぞれ、 mNumRu = rb_define_module("NumRu"); を呼びたい、しかし、単純にやると上書きになってしまう、というのが 悩みでした。 結局それは、eval.c を参考に以下のようにすることで、対応出来まし た。 ID id; id = rb_intern("NumRu"); if (rb_const_defined_at(ruby_class, id)) { mNumRu = rb_const_get(ruby_class, id); } if (mNumRu) { if (TYPE(mNumRu) != T_MODULE) { rb_raise(rb_eTypeError, "%s is not a module", rb_id2name(id)); } if (rb_safe_level() >= 4) { rb_raise(rb_eSecurityError, "extending module prohibited"); } } else { mNumRu = rb_define_module("NumRu"); } ここで ruby_class はグローバルな変数ですので、上の方で extern VALUE ruby_class; と宣言してます。 しかし、もしも rb_define_module が、Ruby におけるモジュール定義 と同じ振る舞いをすべきなら、このようなプログラミングを必要としな いよう、rb_define_module で解決すべきと思います。この「もしも」 が成り立つかどうかがポイントですが、如何でしょうか。なお、モジュー ルでなくクラスを定義する場合に関しては調べてませんが、恐らく同じ ような状況になっていることと思います。 よろしくお願いします。