本章ではクラスとモジュールの作りだすデータ構造について詳細を見ていく。
まずはCレベルでRubyのクラスを定義する方法を少しだけ見ておきたいと思う。 この章ではある意味「特殊な」ところばかりを追求するので、圧倒的多数を占 める通常ルートを先に知っておいてほしいのだ。
クラスとメソッドを定義する主要なAPIは以下の六つである。
rb_define_class()
rb_define_class_under()
rb_define_module()
rb_define_module_under()
rb_define_method()
rb_define_singleton_method()
多少の派生バージョンはあるものの、拡張ライブラリはもちろん、基本クラス ライブラリまでのほとんどがこれだけのAPIで定義されている。順番に紹介し ていこう。
rb_define_class()
はトップレベルのクラスを定義する。
Rubyの配列クラス、Array
の定義を例に取ろう。
19 VALUE rb_cArray; 1809 void 1810 Init_Array() 1811 { 1812 rb_cArray = rb_define_class("Array", rb_cObject); (array.c)
rb_cObject
とrb_cArray
がそれぞれRubyレベルで言うObject
とArray
に
相当する。ruby
に属することを示すプリフィクスrb
と、クラスオブジェク
トであることを示すc
を追加してあるのだ。この名前付け規則はruby
の全
体を通して一貫している。
さて、このrb_define_class()
の呼び出しでまずObject
を継承したクラス
Array
を定義できた。rb_define_class()
はクラスオブジェクトを作ると同時に
定数も定義するので、この時点で既にRubyプログラムからもArray
を参照できる
ようになっている。つまり以下のRubyプログラムに相当する。
class Array < Object
end
がないのに注意してほしい。これはわざと書かなかった。
rb_define_class()
ではクラス文本体は実行されないからである。
続いてrb_define_class_under()
。
この関数は他のクラス/モジュールにネストした
クラスを定義する。今度の例はstat(2)
の返り値であるFile::Stat
だ。
78 VALUE rb_cFile; 80 static VALUE rb_cStat; 2581 rb_cFile = rb_define_class("File", rb_cIO); 2674 rb_cStat = rb_define_class_under(rb_cFile, "Stat", rb_cObject); (file.c)
このコードは以下のRubyプログラムに相当する。
class File < IO class Stat < Object
今度もあえてend
は抜かした。
rb_define_module()
は簡単なのであっさりと終わろう。
17 VALUE rb_mEnumerable; 492 rb_mEnumerable = rb_define_module("Enumerable"); (enum.c)
rb_mEnumerable
の頭のm
はクラスのc
と似ていて、モジュールであることを
示している。そして対応するRubyプログラムはこうだ。
module Enumerable
rb_define_module_under()
はあまり使わないので省略する。
今度はメソッドを定義するrb_define_method()
、
これは非常にたくさん使う。例はまたArray
から取ろう。
1818 rb_define_method(rb_cArray, "to_s", rb_ary_to_s, 0); (array.c)
これでArray
クラスにto_s
という名前のメソッドが定義される。
メソッドの実体は関数ポインタで与える(rb_ary_to_s
)。第四引数は
メソッドの受ける引数の数である。to_s
は引数を取らないので0だ。
また対応するRubyプログラムを書くと、こうなる。
class Array < Object def to_s # rb_ary_to_s()の中身 end end
もちろんclass
文の部分はrb_define_method()
には含まれないので正確には
def
の部分だけだが、class
文をなくしてしまうと関数風メソッドを定義している
ように見えてしまうので仕方なくclass
文の囲いも書いた。
もう一つ、今度は引数を取るものを見てみよう。
1835 rb_define_method(rb_cArray, "concat", rb_ary_concat, 1); (array.c)
定義するクラスはrb_cArray
(Array
)、メソッド名はconcat
、
その実体はrb_ary_concat()
で引数の数は1である。対応する
Rubyプログラムを書くならこうだ。
class Array < Object def concat( str ) # rb_ary_concat()の中身 end end
特定のオブジェクトだけに定義されているメソッド、
特異メソッドの定義もできる。第1章『Ruby言語ミニマム』では
さんざんFile.unlink
を例にしたので、ここでもFile.unlink
の
定義を見よう……かと思ったのだが、とある重要な理由により
File.link
に変更する。
2624 rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2); (file.c)
rb_define_method()
と使いかたは同じである。
第一引数が定義する「オブジェクト」になるだけだ。
この場合はrb_cFile
に定義する。
さて、以上のように定義できるのはいいとして、こういった定義関数の呼び出
しはどこに書いてあり、どうやって実行されるのだろう。
これはInit_xxxx()
という名前の関数にまとめて書いてある。
例えばArray
のためには次のようなInit_Array()
が用意されている。
1809 void 1810 Init_Array() 1811 { 1812 rb_cArray = rb_define_class("Array", rb_cObject); 1813 rb_include_module(rb_cArray, rb_mEnumerable); 1814 1815 rb_define_singleton_method(rb_cArray, "allocate", rb_ary_s_alloc, 0); 1816 rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1); 1817 rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1); 1818 rb_define_method(rb_cArray, "to_s", rb_ary_to_s, 0); 1819 rb_define_method(rb_cArray, "inspect", rb_ary_inspect, 0); 1820 rb_define_method(rb_cArray, "to_a", rb_ary_to_a, 0); 1821 rb_define_method(rb_cArray, "to_ary", rb_ary_to_a, 0); 1822 rb_define_method(rb_cArray, "frozen?", rb_ary_frozen_p, 0); (array.c)
組み込みライブラリのInit
関数はruby
の起動時に明示的に呼ばれる。
具体的にはinits.c
の中である。
47 void 48 rb_call_inits() 49 { 50 Init_sym(); 51 Init_var_tables(); 52 Init_Object(); 53 Init_Comparable(); 54 Init_Enumerable(); 55 Init_Precision(); 56 Init_eval(); 57 Init_String(); 58 Init_Exception(); 59 Init_Thread(); 60 Init_Numeric(); 61 Init_Bignum(); 62 Init_Array(); (inits.c)
このように、ちゃんとInit_Array()
も呼ばれている。
組み込みライブラリはこれでいいとして、拡張ライブラリではどうなるのだろ う。実は拡張ライブラリでも約束事は同じである。例えば
require "myextention"
という形でロードされる拡張ライブラリmyextention.so
ならば、ロードされた
ときにInit_myextention()
という名前の(extern
)関数が呼ばれるようになっ
ている。どうやって呼んでいるのか、についてはこの章の範囲を越える。
第18章『ロード』を読んでいただきたい。ここではInit
関数の
例だけ出して終わりとする。
以下の例はruby
に添付されている、
つまり組み込みではない拡張ライブラリ、stringio
だ。
895 void 896 Init_stringio() 897 { 898 VALUE StringIO = rb_define_class("StringIO", rb_cData); 899 rb_define_singleton_method(StringIO, "allocate", strio_s_allocate, 0); 900 rb_define_singleton_method(StringIO, "open", strio_s_open, -1); 901 rb_define_method(StringIO, "initialize", strio_initialize, -1); 902 rb_enable_super(StringIO, "initialize"); 903 rb_define_method(StringIO, "become", strio_become, 1); 904 rb_define_method(StringIO, "reopen", strio_reopen, -1); (ext/stringio/stringio.c)
rb_define_singleton_method()
通常のメソッドを定義する方法はなんとなくわかる。なにやらメソッドの実体を
作って、m_tbl
に登録すればいいのだ。しかし特異メソッドのほうは全く
わからない。そこでまずは特異メソッドの定義に踏み込んでいくことにしよう。
721 void 722 rb_define_singleton_method(obj, name, func, argc) 723 VALUE obj; 724 const char *name; 725 VALUE (*func)(); 726 int argc; 727 { 728 rb_define_method(rb_singleton_class(obj), name, func, argc); 729 } (class.c)
解説した通り、rb_define_method()
は普通のメソッド定義に使う関数なので、
違いはrb_singleton_class()
だけだ。singleton class、日本語にすると
特異クラスだが、これはいったい何だろうか。
一言で言うと、特異クラスとは特異メソッドを実装するためだけに使われる 仮想クラスである。特異メソッドとは特異クラスに定義されたメソッドのことな のだ。クラスというもの自体がそもそもオブジェクトとメソッドを結びつける ための(ある意味での)「実装」だったわけだが、特異クラスはさらに実装寄 りの存在だ。Rubyの言語仕様にも公式には含まれないことになっており、Ruby レベルにはほとんど姿を現さない。
rb_singleton_class()
では特異クラスの仕組みを確認していくことにする。 しかし毎回毎回いきなり関数を眺めるのでは芸がない。 今回は新兵器コールグラフ(call graph)を使ってみよう。
rb_define_singleton_method rb_define_method rb_singleton_class SPECIAL_SINGLETON rb_make_metaclass rb_class_boot rb_singleton_class_attached
コールグラフというのは関数(より一般には手続き)の呼び出し関係を 図にしたものである。ソースコードに書いてある呼び出しを素直に全部 表示したのが静的なコールグラフ(static call graph)で、実行時に 呼び出した手続きだけを表示したものが動的なコールグラフ( dynamic call graph)と呼ばれる。
この図は静的なコールグラフで、
インデントが呼び出しを表している。例えばrb_define_singleton_method()
が
rb_define_method()
とrb_singleton_class()
を呼んでいる。その
rb_singleton_class()
はまたSPECIAL_SINGLETON()
と
rb_make_metaclass()
を呼んでいる。コールグラフを採取するには
cflow
\footnote{cflow
:添付CD-ROMのdoc/callgraph.html
を参照}などが
使える。
ちなみに本書では関数限定で取りたかったのでruby
専用ツールを自分で
作ってしまった。コード解析部分を修正すれば一般化できるはずなので、
出版前後までにはなんとかしたいと思う。そのへんの事情も
添付CD-ROMのdoc/callgraph.html
に書いてある。
さてコードに戻ろう。コールグラフを見ると、rb_singleton_class()
の呼び出し
は随分と深い。これまでの関数はどれもこれも呼び出しレベルが浅かったので
いきなり読んでも迷わなかったのだが、これくらい深くなってくると自分が何
をしていたのかすぐに忘れてしまう。こういうときは常にコールグラフを置い
ておき、自分がいる位置を把握しながら読まなければならない。今回はその実
例としてrb_singleton_class()
以下の全手続きを並行解読する。読むべきポ
イントは次の二点である。
特異クラスとは特殊なクラスである。つまり、基本的には普通のクラス と同じではあるけれど、少しだけ違うところがあるということだ。その違うと ころを見付けることこそが「具体的に特異クラスを理解すること」だと言える。
違いを見付けるにはどうするか。通常のクラスを生成する関数と特異クラスを
生成する関数の意味的な差分を取ればよい。そのためには通常のクラスを作成
する関数を見付けなければならない。ここで、通常のクラスは
rb_define_class()
で定義できたから、きっとその先に通常クラスを作成する
関数があるだろうと予想できる。今はrb_define_class()
の中身ではなくてそ
の先にあるものに興味があるわけだから、ここはまずrb_define_class()
の
コールグラフを眺めてみるのがよさそうだ。
rb_define_class rb_class_inherited rb_define_class_id rb_class_new rb_class_boot rb_make_metaclass rb_class_boot rb_singleton_class_attached
rb_class_new()
が気になる。どう見てもこの名前はクラスを作っているうに
見えるではないか。確かめてみよう。
37 VALUE 38 rb_class_new(super) 39 VALUE super; 40 { 41 Check_Type(super, T_CLASS); 42 if (super == rb_cClass) { 43 rb_raise(rb_eTypeError, "can't make subclass of Class"); 44 } 45 if (FL_TEST(super, FL_SINGLETON)) { 46 rb_raise(rb_eTypeError, "can't make subclass of virtual class"); 47 } 48 return rb_class_boot(super); 49 } (class.c)
Check_Type()
はオブジェクト構造体の型チェックだ。つまり、無視してよい。
rb_raise()
もエラー処理だから無視してよい。とすると残りは
rb_class_boot()
だけだ。これも見よう。
21 VALUE 22 rb_class_boot(super) 23 VALUE super; 24 { 25 NEWOBJ(klass, struct RClass); /* struct RClassを割り当てる */ 26 OBJSETUP(klass, rb_cClass, T_CLASS); /* RBasic部分の初期化 */ 27 28 klass->super = super; /*(A)*/ 29 klass->iv_tbl = 0; 30 klass->m_tbl = 0; 31 klass->m_tbl = st_init_numtable(); 32 33 OBJ_INFECT(klass, super); 34 return (VALUE)klass; 35 } (class.c)
NEWOBJ()
とOBJSETUP()
は組み込みの構造体型(struct Rxxxx
)を持つ
Rubyオブジェクトを生成するときの決まり文句である。両方ともマクロだ。
NEWOBJ()
でstruct RClass
が生成され、第一引数のklass
にポインタが入
る。OBJSETUP()
ではそのRClass
のうちRBasic
部分、つまり
basic.klass
とbasic.flags
を初期化する。
OBJ_INFECT()
はセキュリティ関係のマクロ。以後は全て無視してよい。
そして(A)では引数のsuper
をsuper
メンバにセットしている。つまり
rb_class_boot()
はsuper
を継承したクラスを生成する関数だとまとめられ
そうだ。
以上から、rb_class_boot()
はクラスを作る関数で、rb_class_new()
はそれと
ほぼ等しいとわかった。
ここでもう一度rb_singleton_class()
のコールグラフを見てみる。
rb_singleton_class SPECIAL_SINGLETON rb_make_metaclass rb_class_boot rb_singleton_class_attached
ここにもrb_class_boot()
が現れている。つまりここまでは普通のクラスと同
じだということだ。その続きでやっている部分が通常クラスと特異クラスの違
い、即ち特異クラスの特徴ということになるだろう。ここまでわかればあとは
rb_singleton_class()
とrb_make_metaclass()
を読むだけである。
rb_singleton_class()
の圧縮
rb_singleton_class()
は少し長いので、
まずは本質的でないところを削り圧縮していこう。
678 #define SPECIAL_SINGLETON(x,c) do {\ 679 if (obj == (x)) {\ 680 return c;\ 681 }\ 682 } while (0) 684 VALUE 685 rb_singleton_class(obj) 686 VALUE obj; 687 { 688 VALUE klass; 689 690 if (FIXNUM_P(obj) || SYMBOL_P(obj)) { 691 rb_raise(rb_eTypeError, "can't define singleton"); 692 } 693 if (rb_special_const_p(obj)) { 694 SPECIAL_SINGLETON(Qnil, rb_cNilClass); 695 SPECIAL_SINGLETON(Qfalse, rb_cFalseClass); 696 SPECIAL_SINGLETON(Qtrue, rb_cTrueClass); 697 rb_bug("unknown immediate %ld", obj); 698 } 699 700 DEFER_INTS; 701 if (FL_TEST(RBASIC(obj)->klass, FL_SINGLETON) && 702 (BUILTIN_TYPE(obj) == T_CLASS || 703 rb_iv_get(RBASIC(obj)->klass, "__attached__") == obj)) { 704 klass = RBASIC(obj)->klass; 705 } 706 else { 707 klass = rb_make_metaclass(obj, RBASIC(obj)->klass); 708 } 709 if (OBJ_TAINTED(obj)) { 710 OBJ_TAINT(klass); 711 } 712 else { 713 FL_UNSET(klass, FL_TAINT); 714 } 715 if (OBJ_FROZEN(obj)) OBJ_FREEZE(klass); 716 ALLOW_INTS; 717 718 return klass; 719 } (class.c)
空行で前半後半に区切られており、前半が特殊条件、後半が一般条件を扱って いる。つまり本流は後半ということになる。だからこれを後に残してここでは 残りの部分のことを簡単に話しておこう。
前半で扱っているのは全てVALUE
がポインタでないもの、つまりオブジェク
ト構造体が存在しないものだ。まずFixnum
とSymbol
は明示的に弾いている。そ
してrb_special_const_p()
はポインタでないVALUE
に対して真を返す関数なの
で、ここではQtrue Qfalse Qnil
だけがひっかかるはずだ。それ以外には
非ポインタのVALUE
はないのでrb_bug()
でバグを報告する。
DEFER_INTS()
とALLOW_INTS()
はINTS
が同じなので一対と見るべき
だろう。そして実際そのとおりで、シグナルがらみのマクロである。なぜなら
rubysig.h
で定義されているし、INTS
はinterruptsの略だろうと想像が
付くからだ。無視してよい。
rb_make_metaclass()
の圧縮142 VALUE 143 rb_make_metaclass(obj, super) 144 VALUE obj, super; 145 { 146 VALUE klass = rb_class_boot(super); 147 FL_SET(klass, FL_SINGLETON); 148 RBASIC(obj)->klass = klass; 149 rb_singleton_class_attached(klass, obj); 150 if (BUILTIN_TYPE(obj) == T_CLASS) { 151 RBASIC(klass)->klass = klass; 152 if (FL_TEST(obj, FL_SINGLETON)) { 153 RCLASS(klass)->super = RBASIC(rb_class_real(RCLASS(obj)->super))->klass; 154 } 155 } 156 157 return klass; 158 } (class.c)
rb_class_boot()
は既に見た。引数のsuper
をスーパークラスとして(普通の)
クラスを生成するのだった。ここではそのクラスに対してFL_SINGLETON
という
フラグを立てている。これは明らかに怪しい。名前から考えて、これが特異
クラスの印ではないかと考えられる。
以上の過程を終え、さらに引数・返り値・ローカル変数は全部VALUE
なので宣
言も捨ててしまうと、これくらいに圧縮できる。
rb_singleton_class(obj) { if (FL_TEST(RBASIC(obj)->klass, FL_SINGLETON) && (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE) && rb_iv_get(RBASIC(obj)->klass, "__attached__") == obj) { klass = RBASIC(obj)->klass; } else { klass = rb_make_metaclass(obj, RBASIC(obj)->klass); } return klass; } rb_make_metaclass(obj, super) { klass = superをスーパークラスにしたクラスを生成; FL_SET(klass, FL_SINGLETON); RBASIC(obj)->klass = klass; rb_singleton_class_attached(klass, obj); if (BUILTIN_TYPE(obj) == T_CLASS) { RBASIC(klass)->klass = klass; if (FL_TEST(obj, FL_SINGLETON)) { RCLASS(klass)->super = RBASIC(rb_class_real(RCLASS(obj)->super))->klass; } } return klass; }
rb_singleton_class()
のif
の条件が手強そうだ。しかしこの条件は
本流のrb_make_metaclass()
と外れているのでまた後回しにする。
まずは無条件で偽側に行ったとして考えよう。
またrb_make_metaclass()
のBUILTIN_TYPE()
というのはTYPE()
と同じく
構造体型フラグ(T_xxxx
)を得るマクロだ。つまり「obj
がクラスだったら」
という条件なのだが、今はobj
をクラスとは限定したくない。だから
これも消しておこう。
以上の条件を考えて関数を合成してしまうと、こうなった。
rb_singleton_class(obj) { klass = RBASIC(obj)->klassをスーパークラスにしたクラスを生成; FL_SET(klass, FL_SINGLETON); RBASIC(obj)->klass = klass; return klass; }
だがまだイマイチわかりにくいような気がする。
どうしてかと言うとklass
という名前が多すぎるからだ。
そこで変数のklass
はsclass
にしてしまおう。
rb_singleton_class(obj) { sclass = RBASIC(obj)->klassをスーパークラスにしたクラスを生成; FL_SET(sclass, FL_SINGLETON); RBASIC(obj)->klass = sclass; return sclass; }
これなら非常にわかりやすい。さらにわかりやすくするために、 やっていることを図にまとめてみた(図1)。 横の矢印が「インスタンス〜クラス」関係で、縦の矢印が継承であ る(上がスーパークラス)。
図1: rb_singleton_class
この図で最初と最後を見比べてみると、構造を全く変えずにsclass
だけが挿
入されていることがわかる。これが特異クラスの仕組みの全てだ。つまり仮想
的に継承を一段増やすのである。そこにメソッドを定義すれば、klass
の他
のインスタンスには全く関係ないメソッドを定義できるという仕組みなのだ。
ところで、圧縮の過程でrb_singleton_class_attached()
の呼び出しが
何も言及なくこっそり消されていたことに気付いただろうか。ここだ。
rb_make_metaclass(obj, super) { klass = superをスーパークラスにしたクラス生成; FL_SET(klass, FL_SINGLETON); RBASIC(obj)->klass = klass; rb_singleton_class_attached(klass, obj); /* これ */
この関数は何をしているのか、中を見てみよう。
130 void 131 rb_singleton_class_attached(klass, obj) 132 VALUE klass, obj; 133 { 134 if (FL_TEST(klass, FL_SINGLETON)) { 135 if (!RCLASS(klass)->iv_tbl) { 136 RCLASS(klass)->iv_tbl = st_init_numtable(); 137 } 138 st_insert(RCLASS(klass)->iv_tbl, rb_intern("__attached__"), obj); 139 } 140 } (class.c)
klass
にフラグFL_SINGLETON
が立っていたら……つまり特異クラスだった
ら、klass
のインスタンス変数テーブル(iv_tbl
)に
__attached__
→obj
という関連付けを登録する。と読めそうだ
(今はklass
は常に特異クラスである……つまりFL_SINGLETON
が付いている)。
__attached__
にはプリフィクスの@
が付いていないが、インスタンス変数テー
ブルに格納されるのだからいちおうインスタンス変数ということになる。この
ようなインスタンス変数はRubyレベルからは絶対に参照できなくなるので、ユー
ザには見せたくないシステム専用の値を保存するのに使えるというわけだ。
そしてklass
とobj
の関係も考えよう。klass
はobj
の特異クラスである。
つまりここでは特異クラスにそのインスタンスを覚えさせているようだ。この
値は、特異クラスが変更されたときにそのインスタンス(つまりobj
)に対
してフックメソッドを呼んだりするために使う。例えば特異クラスにメソッド
が追加されるとobj
のsingleton_method_added
というメソッドが呼ばれる。
これは「そういう仕様だから」そうしたのであって論理的に必然性があるので
はない。
だがこれで大丈夫なのだろうか。このように__attached__
にインスタンスを
記録するという方式では、特異クラス一つにつきインスタンス一つしか持てな
いことになる。例えば特異クラスをなんらかの手段で取り出してnew
したら
一つの特異クラスで複数のインスタンスを持ててしまうのではないだろうか。
結論から言うと、そういうことはない。特異クラスはインスタンスを生成 できないようにちゃんとチェックされているからだ。
そもそも特異クラスは特異メソッドのためにあり、特異メソッドとは特定オブ ジェクトだけにあるメソッドのことだ。もし特異クラスが複数のインスタンス を持ってしまえたら普通のクラスと変わらなくなってしまう。だから特異クラ スはインスタンスをただ一つしか持たない……と言うより、一つにしなければ ならないのだ。
グチャグチャといろいろやってきて混乱してしまったかもしれないので最後に 結論だけまとめよう。
特異クラスとは何か。特異クラスとはFL_SINGLETON
フラグが付いたクラスで、
ただ一つしかインスタンスを持たないクラスのことである。
特異メソッドとは何か。オブジェクトの特異クラスに定義されたメソッドの ことである。
クラスにも「そのクラス」があり、それはClass
だった。そしてClass
のクラス
はまたClass
だった。つまりここで無限ループができている(図2)。
図2: クラスの無限ループ
ここまでは既にやったことである。ここからがこの章のテーマだ。 なぜクラスはループしていなくてはいけないのだろうか。
まず、Rubyでは全てのデータはオブジェクトである。そしてまたRubyではク ラスもデータなのでクラスもオブジェクトでなければならない。
オブジェクトであるからにはメソッドに反応できなくてはならない。そして 「メソッドに反応するためにはなんらかのクラスに属していなければいけない」 というルールにしたほうが処理が楽だ。ここから、クラス自身にもクラスを持 たせる必要が出てくる。
このことを踏まえて現実に実装することを考えてみよう。まず最も素朴な
方法として、Class
のクラスはClassClass
、ClassClass
のクラスは
ClassClassClass
……というふうに、クラスのためのクラスを次々と連鎖する
ことが考えられる。しかし論理的にはともあれ、こんなものは効率的には実装
できない。そういうわけでクラスがオブジェクトであるオブジェクト指向言語は、
Class
のクラスをClass
自身にしてしまうことでループを作りだし、無限のイン
スタンス〜クラス関係を仮想的に作りだすのが常套手段なのである。
繰り返すが、Class
のクラスがClass
なのは実装を楽にするためだ。
論理的に重要なのではない。
よくRubyに関して「なんでもオブジェクト」という宣伝文句を目にする。そし てその一環として「クラスもオブジェクトだ!」という言葉が出てくることも ある。しかしこの表現はあまりにナーバスにすぎる。この問題について考える ときは、以下の二つは区別して考えなければいけない。
データとかコードとか言い出すとどうも話がややこしくなる。ここでは非常に 限定的に、「プログラムで変数に入れられるもの」をデータと呼ぶことにして おく。
クラスをプログラムから操作できるのはRubyプログラムに自分自身(プログ ラム)を操作できる能力を与えるためである。そのような能力を リフレクション(reflection)と言う。オブジェクト指向言語で しかもクラスを持つRubyにおいてはクラスを直接操作できることが相当する。
それでも、クラスをオブジェクトにしない方法もある。例えば関数風メソッ ドとしてクラスを操作する機能を提供しても全く問題はない。ただしクラスを 表現するインタプリタ内部のデータ構造があるのだからそれを直接出すほうが オブジェクト指向言語として自然ではある。そしてRubyはそうした。
さらに、Ruby言語ではデータは全てオブジェクトにするという方針である。だ からクラスもオブジェクトにするのが適切だ。
ちなみに、Rubyでクラスがオブジェクトでなければいけないのにはリフクレショ ン以外の理由もある。それはインスタンスから独立したメソッド、つまり JavaやC++で言うスタティックメソッドの定義先を用意することだ。
そしてスタティックメソッドを実現するためにもう一つ必要になるのが特異メ ソッドである。すると連鎖的に特異クラスが必要になる。つまりこのあたりの 要求関係を図示すると図3のようになる。
図3: 要求の連鎖
Rubyではクラスに定義された特異メソッドのことをクラスメソッドと 呼ぶのだが、そのクラスメソッドにはちょっと不思議な仕様がある。 クラスメソッドは、なぜか継承するのだ。
class A def A.test # Aに特異メソッドを定義する puts("ok") end end class B < A end B.test() # 呼べてしまう
こんなことはクラス以外のオブジェクトの特異メソッドでは絶対に起こらない。 つまりクラスだけが特別扱いされているのである。この節ではクラスメソッド が継承する仕組みを追っていこう。
クラスメソッドを継承させるとしたら、その作業はどこでやっているだろうか。 クラスを定義(生成)するときか、特異メソッドを定義するときか、どちらか のタイミングでやっているとしか考えられない。そこでまずはクラスを定義す るコードを眺めてみることにしよう。
クラスを定義すると言えばrb_define_class()
だろう。そこでこの関数のコール
グラフを出してみる。
rb_define_class rb_class_inherited rb_define_class_id rb_class_new rb_class_boot rb_make_metaclass rb_class_boot rb_singleton_class_attached
どこかで見たことがあると思ったらこのグラフは前の節でも見たのだった。
その時は気付かなかったが、よく見るとなぜかrb_make_metaclass()
が登場し
ている。この関数は先程見たとおり、特異クラスを導入する関数だ。これがと
ても怪しい。なぜ特異メソッドを定義してもいないのにこんなものを呼ぶのだ
ろう。しかも、rb_singleton_class()
ではなく一段下の
rb_make_metaclass()
を使うのはどうしてだろうか。このあたりを改めて
確認しなければならないようだ。
rb_define_class_id()
まずはその呼び出し元のrb_define_class_id()
から読んでみよう。
160 VALUE 161 rb_define_class_id(id, super) 162 ID id; 163 VALUE super; 164 { 165 VALUE klass; 166 167 if (!super) super = rb_cObject; 168 klass = rb_class_new(super); 169 rb_name_class(klass, id); 170 rb_make_metaclass(klass, RBASIC(super)->klass); 171 172 return klass; 173 } (class.c)
rb_class_new()
はsuper
をスーパークラスにしてクラスを生成する関数であっ
た。rb_name_class()
のname
は動詞「命名する」だ。今は名前のことはどうで
もいいので飛ばす。そして問題のrb_make_metaclass()
がある。気になるのは
前回rb_singleton_class()
から呼んだときと引数が違うことだ。前回はこうだっ
た。
rb_make_metaclass(obj, RBASIC(obj)->klass);
だが今回はこうだ。
rb_make_metaclass(klass, RBASIC(super)->klass);
この通り微妙に違う。これによって結果がどう変わってくるのか、簡約版の
rb_make_metaclass()
を再度見ていくことにしよう。
rb_make_metaclass
(再)rb_make_metaclass(obj, super) { klass = superをスーパークラスにしたクラスを生成; FL_SET(klass, FL_SINGLETON); RBASIC(obj)->klass = klass; rb_singleton_class_attached(klass, obj); if (BUILTIN_TYPE(obj) == T_CLASS) { RBASIC(klass)->klass = klass; if (FL_TEST(obj, FL_SINGLETON)) { RCLASS(klass)->super = RBASIC(rb_class_real(RCLASS(obj)->super))->klass; } } return klass; }
前回は後半のif
文をさっくり無視したが、改めて見るとT_CLASS
つまり
クラスに限定して何かやっている。これが明らかに重要そうだ。
rb_define_class_id()
では
rb_make_metaclass(klass, RBASIC(super)->klass);
という形で呼んでいたから、rb_make_metaclass()
のパラメータ変数を
この実際の値で展開してしまおう。
rb_make_metaclass(klass, super_klass /* == RBASIC(super)->klass */) { sclass = super_klassをスーパークラスにした特異クラスを生成; RBASIC(klass)->klass = sclass; RBASIC(sclass)->klass = sclass; return sclass; }
これを図にすると図4のようになった。括弧付きのものが特異
クラスを示している。この表記は本書で何度も使うので覚えておいてほしい。
つまり一般にobj
の特異クラスを(obj)
と書く。ここにある(klass)
なら
オブジェクトklass
のための特異クラスである。どうやら特異クラスがクラ
スとそのスーパークラスのクラスの間にはさまるようになっているようだ。
図4: クラスの特異クラス導入
この結果からさらに想像力を広げてみると、スーパークラスのクラス
(図4のc
)もまた特異クラスのはずだ、と考えられる。もう一
段階継承してみればわかる(図5)。
図5: 多段の継承で形成される階層
super
とklass
の関係はklass
と
klass2
の関係と同じだから、c
は特異クラス(super)
であるはずだ。この考えか
たを進めていくと最終的には、Object
のクラスは(Object)
でなければならない、
というところまで行き着くだろう。そして実際にその通りなのである。例えば
次のプログラムのように継承すると、
class A < Object end class B < A end
内部では図6のような構造が作られている。
図6: クラス階層とメタクラス
このようにクラスの特異クラス自身がクラスと連動して継承するため、 クラスメソッドが継承されるのである。
これでクラスメソッドの継承に関する仕組みはわかったが、そうすると逆に いくつか疑問が出てくる。まず、クラスの特異クラスのクラスは なんだろうか。これはデバッガを使って見てみればいい。この調査結果を 図にしたのが図7だ。
図7: クラスの特異クラスのクラス
クラスの特異クラスは自分自身をクラスとするようだ。ややこしい。
二つめの疑問。Object
のクラスはClass
だったはずだ。
第1章『Ruby言語ミニマム』でちゃんとclass()
メソッドで確かめたではないか。
p(Object.class()) # Class
確かに「Rubyレベルでは」その通りだ。しかし「Cレベルでは」特異クラス
(Object)
なのである。Rubyレベルに(Object)
が出てこないのは
Object#class
が特異クラスをスキップするようになっているからだ。
メソッドの実体であるrb_obj_class()
を見て確かめてみよう。
86 VALUE 87 rb_obj_class(obj) 88 VALUE obj; 89 { 90 return rb_class_real(CLASS_OF(obj)); 91 } 76 VALUE 77 rb_class_real(cl) 78 VALUE cl; 79 { 80 while (FL_TEST(cl, FL_SINGLETON) || TYPE(cl) == T_ICLASS) { 81 cl = RCLASS(cl)->super; 82 } 83 return cl; 84 } (object.c)
CLASS_OF(obj)
がobj
のbasic.klass
を返す。そしてrb_class_real()
で
は特異クラスを全て(スーパークラス方向に)スキップしている。特異クラス
はそもそもスーパークラスとの間にはさまれる「代理」のようなクラスだった
から、「真の」クラスが必要なときはスーパークラスに戻らなければいけない
というわけだ(図8)。
またI_CLASS
は後でインクルードの話をするときに登場する。
図8: 特異クラスと真のクラス
さて、クラスに導入された特異クラスもやはりクラスの一種である。 ということはクラスのクラスである。つまりメタクラスと呼べる。
ただしここで注意したいのは、特異クラス「が」メタクラスなのではないと いうことだ。クラスに導入された特異ク ラスがメタクラスである。特異クラスであることが重要なのではなくて、クラ スのクラスであることが重要なのだ。この点は筆者がRubyを学びはじめのころ にひっかかっていたことで、もしかすると他にも仲間がいるかもしれないので ここに明記しておく。
そういう点を考えるとrb_make_metaclass()
という関数名はあまりよくない。
クラスに使うときは確かにメタクラスを作っているが、それ以外のオブジェク
トに使うときはメタクラスではないからだ。
それから最後に、とあるクラスがメタクラスだとわかっても何か具体的な 御利益があるわけではない。あまり気にしすぎないようにしていただきたい。
ここまででクラスとメタクラスの話についてはほぼ話し終わった。だがまだ
一つだけ問題が残っている。それは三つのメタオブジェクト、
Object
・Module
・
Class
のことである。この三つだけはrb_define_class()
などの通常のAPIでは
作成できない。クラスを作るためにはそのメタクラスを作る必要
があるが、先程見た通り、メタクラスのスーパークラスにはClass
があった。
だがもちろんClass
はまだできていないので、メタクラスが作れないのである。
そこでruby
ではこの三つだけ特別扱いにして生成する。
ではコードを見ていこう。
1243 rb_cObject = boot_defclass("Object", 0); 1244 rb_cModule = boot_defclass("Module", rb_cObject); 1245 rb_cClass = boot_defclass("Class", rb_cModule); 1246 1247 metaclass = rb_make_metaclass(rb_cObject, rb_cClass); 1248 metaclass = rb_make_metaclass(rb_cModule, metaclass); 1249 metaclass = rb_make_metaclass(rb_cClass, metaclass); (object.c)
まず前半、boot_defclass()
はrb_class_boot()
とほぼ同じである。
つまりスーパークラスだけをセットしたクラスを作る。
ここまででリンクは図9左のようになっている
そして後半の三行で(Object) (Module) (Class)
が生成され、セットされる
(図9右)。(Object)
や(Module)
のクラス……つまり自分自身……
はrb_make_metaclass()
の中で既にセットされているので問題ない。これで
メタオブジェクトのブートストラップが完了した。
図9: メタオブジェクトの作成
以上の全てを考慮した最終的な姿は図10のようになる。
図10: Rubyのメタオブジェクト
この節ではクラスとクラス名、つまり定数の相互変換を成立させる手順に
ついて解析する。具体的にはrb_define_class()
とrb_define_class_under()
が
対象である。
まずはrb_define_class()
を読んでいこう。
この関数を終えると定数からクラスが検索できるようになる。
183 VALUE 184 rb_define_class(name, super) 185 const char *name; 186 VALUE super; 187 { 188 VALUE klass; 189 ID id; 190 191 id = rb_intern(name); 192 if (rb_autoload_defined(id)) { /*(A)オートロード */ 193 rb_autoload_load(id); 194 } 195 if (rb_const_defined(rb_cObject, id)) { /*(B)rb_const_defined */ 196 klass = rb_const_get(rb_cObject, id); /*(C)rb_const_get */ 197 if (TYPE(klass) != T_CLASS) { 198 rb_raise(rb_eTypeError, "%s is not a class", name); 199 } /*(D)rb_class_real */ 200 if (rb_class_real(RCLASS(klass)->super) != super) { 201 rb_name_error(id, "%s is already defined", name); 202 } 203 return klass; 204 } 205 if (!super) { 206 rb_warn("no super class for `%s', Object assumed", name); 207 } 208 klass = rb_define_class_id(id, super); 209 rb_class_inherited(super, klass); 210 st_add_direct(rb_class_tbl, id, klass); 211 212 return klass; 213 } (class.c)
rb_define_class_id()
の前後で大きく話が分かれる。前がクラスの取得または
生成。後が定数への代入である。以下詳しく見ていく。
(A)Rubyには、とある定数が参照されたときに自動的にライブラリをロード
するオートロードという機能がある。このrb_autoload_xxxx()
という名前の
関数はそのチェックだ。無視して問題ない。
(B)クラスklass
に定数name
が定義されているかどうか判定する。
(C)クラスklass
の定数name
の値を得る。
第6章『変数と定数』で詳述。
(D)rb_class_real()
は先程見た。クラスc
が特異クラスやICLASS
であったら
そうでないクラスまでsuper
をたぐり、それを返す。ようするにRubyレベルに
出してはいけない仮想クラスを飛ばすための関数。
というあたりで読めるだろうか。
このあたりは定数が絡んでくるので非常にやりにくい。しかし定数の章でクラ ス定義の話をするのもなんだし、ということでどっちつかずの中途半端な記述 になってるわけだ。
さらにrb_define_class_id()
後のこれだが、
st_add_direct(rb_class_tbl, id, klass);
これがクラスを定数に代入している部分である。しかしどう見てもそうは見え
ない。実はトップレベルのクラスだけは通常の定数とは分けてrb_class_tbl
に
まとめられているのである。分けてあるのにはGCが少し関係している。あまり本質
的な意味はない。
クラス名からクラスが取れるような仕組みがあるのはわかったが、その逆はど
うなのだろう。例えばp
したりClass#name
を呼んだりするとクラスからその名
前が取れるが、どうやって実装されているのだろうか。
実はそれはずっと昔に出てきたrb_name_class()
によって行
われている。呼び出し位置はこのあたりだ。
rb_define_class rb_define_class_id rb_name_class
内容を見てみよう。
269 void 270 rb_name_class(klass, id) 271 VALUE klass; 272 ID id; 273 { 274 rb_iv_set(klass, "__classid__", ID2SYM(id)); 275 } (variable.c)
__classid__
もRubyからは見えないインスタンス変数だ。インスタンス変数
テーブルにはVALUE
以外の値は入れられないので、ID
はID2SYM()
で
Symbol
に変換して入れておく。
これでクラス→定数名という方向の検索もできるようになった。
これでネストレベル1の場合の名前とクラスの相互リンクはわかった。
あとはネストレベル2以上の場合で、こちらはもう少し複雑だ。
レベル2以上のクラスを定義するのはrb_define_class_under()
である。
215 VALUE 216 rb_define_class_under(outer, name, super) 217 VALUE outer; 218 const char *name; 219 VALUE super; 220 { 221 VALUE klass; 222 ID id; 223 224 id = rb_intern(name); 225 if (rb_const_defined_at(outer, id)) { 226 klass = rb_const_get(outer, id); 227 if (TYPE(klass) != T_CLASS) { 228 rb_raise(rb_eTypeError, "%s is not a class", name); 229 } 230 if (rb_class_real(RCLASS(klass)->super) != super) { 231 rb_name_error(id, "%s is already defined", name); 232 } 233 return klass; 234 } 235 if (!super) { 236 rb_warn("no super class for `%s::%s', Object assumed", 237 rb_class2name(outer), name); 238 } 239 klass = rb_define_class_id(id, super); 240 rb_set_class_path(klass, outer, name); 241 rb_class_inherited(super, klass); 242 rb_const_set(outer, id, klass); 243 244 return klass; 245 } (class.c)
構造はrb_define_class()
と同じで、rb_define_class_id()
呼び出しの前が再
定義チェック、後が定数とクラスの相互リンク作成である。
前半はrb_define_class()
とほとんど同じでつまらないので飛ばす。
後半ではrb_set_class_path()
が目新しい。ここに絞って見ていこう。
rb_set_class_path()
この関数は、クラスunder
にネストしたクラスklass
に名前name
を付ける。
関数名
のclass path(クラスパス)というのは例えば
「Net::NetPrivate::Socket
」のようにトップ
レベルからのネスト情報を全て含んだ定数名のことである。
210 void 211 rb_set_class_path(klass, under, name) 212 VALUE klass, under; 213 const char *name; 214 { 215 VALUE str; 216 217 if (under == rb_cObject) { /* トップレベルに定義した */ 218 str = rb_str_new2(name); /* nameからRubyの文字列を作る */ 219 } 220 else { /* ネストした定義 */ 221 str = rb_str_dup(rb_class_path(under)); /* 返り値をコピー */ 222 rb_str_cat2(str, "::"); /* それに"::"を連結 */ 223 rb_str_cat2(str, name); /* それにnameを連結 */ 224 } 225 rb_iv_set(klass, "__classpath__", str); 226 } (variable.c)
最後の一行以外でクラスパスを作り、最後の一行でクラスに自分の名前を覚え
させている。__classpath__
もやはりRubyプログラムからは見えないインスタ
ンス変数だ。rb_name_class()
では__classid__
というのが出てきたが、
id
のほうはネスト情報を含まないところが違う(以下の表を参照)。
__classpath__ Net::NetPrivate::Socket __classid__ Socket
つまりrb_define_class()
などで定義したクラスには__classid__
か
__classpath__
のどちらかは定義されているはずなので、under
のクラスパスを
求めるにはそれを検索すればいい。それをやっているのが
rb_class_path()
だ。内容は省略する。
と言った直後になんだが、実は__classpath__
も__classid__
もセットされてい
ない場合がありうる。Rubyでは次のようにメソッドを使ってクラスを作ること
ができるからだ。
c = Class.new()
このような作られかたをしてしまうとrb_define_class_id()
を通らないのでク
ラスパスがセットされない。その場合クラスc
は名前がないクラス、つまり無
名クラスとなる。
しかし後で定数に代入するとその時点で名前が付くことになる。
SomeClass = c # クラス名はSomeClass
厳密に言うと定数に代入したあと初めて名前を要求したときに名前が付く。例
えばこのSomeClass
クラスをp
したときやClass#name
というメソッドを呼んだと
きだ。そのときにはrb_class_tbl
を検索して値が等しいものを探し、名前を
作らなければならない。また、
class A class B C = tmp = Class.new() p(tmp) # ここで名前が検索される end end
というようにネストしている場合もありうるので、最悪の場合は定数空間全体 を検索しなければならない。しかし普通、定数はあまり多くないので、全体 を検索してもそう時間がかかることはない。
散々クラスについて話してきたので最後は目先を変えてモジュールの インクルードについて話すことにしよう。
rb_include_module
(1)
インクルードは普通のメソッドModule#include
で行われるのであった。そ
のCでの実体はrb_include_module()
である。ただし本当に正確に言うと
rb_mod_include()
が実体で、そこからModule#append_features
が呼ばれ、そ
のデフォルトの実装がrb_include_module()
を呼ぶようになっている。
RubyとCの手続きを混ぜてコールグラフを書けばこうだ。
Module#include (rb_mod_include) Module#append_features (rb_mod_append_features) rb_include_module
なんにしても普段インクルードだと思っている操作はrb_include_module()
で
行われている。この関数はやや長いので半分ずつ見ていく。
/* klassにmoduleをインクルードする */ 347 void 348 rb_include_module(klass, module) 349 VALUE klass, module; 350 { 351 VALUE p, c; 352 int changed = 0; 353 354 rb_frozen_class_p(klass); 355 if (!OBJ_TAINTED(klass)) { 356 rb_secure(4); 357 } 358 359 if (NIL_P(module)) return; 360 if (klass == module) return; 361 362 switch (TYPE(module)) { 363 case T_MODULE: 364 case T_CLASS: 365 case T_ICLASS: 366 break; 367 default: 368 Check_Type(module, T_MODULE); 369 } (class.c)
ここまでは全てセキュリティや型のチェックで、従って無視してよい。 以下が本処理だ。
371 OBJ_INFECT(klass, module); 372 c = klass; 373 while (module) { 374 int superclass_seen = Qfalse; 375 376 if (RCLASS(klass)->m_tbl == RCLASS(module)->m_tbl) 377 rb_raise(rb_eArgError, "cyclic include detected"); 378 /*(A)スーパークラスで既にmoduleをインクルードしていたらスキップ */ 379 for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) { 380 switch (BUILTIN_TYPE(p)) { 381 case T_ICLASS: 382 if (RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) { 383 if (!superclass_seen) { 384 c = p; /* 挿入地点をずらす */ 385 } 386 goto skip; 387 } 388 break; 389 case T_CLASS: 390 superclass_seen = Qtrue; 391 break; 392 } 393 } 394 c = RCLASS(c)->super = include_class_new(module, RCLASS(c)->super); 395 changed = 1; 396 skip: 397 module = RCLASS(module)->super; 398 } 399 if (changed) rb_clear_cache(); 400 } (class.c)
まず(A)のブロックでやっていることはコメントに書いてある。これは 特殊条件らしいのでまずは読み飛ばしてしまおう。残りから重要そうな ものだけ残すとこうなる。
c = klass; while (module) { c = RCLASS(c)->super = include_class_new(module, RCLASS(c)->super); module = RCLASS(module)->super; }
つまりmodule
のsuper
に対する繰り返しだ。module
のsuper
に入ってい
るのは、module
にインクルードしたモジュールだろう(理由は勘)。そして
インクルードする対象のクラスのスーパークラスを何かで差し替えている。何
に差し替えているのかはよくわからないが、これを見た瞬間に「あ、これはリ
ストの要素追加(cons)の形じゃないか?」と気付けるととても話が早くなる。
つまり
list = new(item, list)
という形である。そう考えるとこのループではc
とc->super
の間に
module
を挟み込もうとしているのかな、と予想できる。そういう仕組みなら
モジュールの仕様にも一致する。
しかし本当にそうなのかはinclude_class_new()
を読まないとわからない。
include_class_new()
319 static VALUE 320 include_class_new(module, super) 321 VALUE module, super; 322 { 323 NEWOBJ(klass, struct RClass); /*(A)*/ 324 OBJSETUP(klass, rb_cClass, T_ICLASS); 325 326 if (BUILTIN_TYPE(module) == T_ICLASS) { 327 module = RBASIC(module)->klass; 328 } 329 if (!RCLASS(module)->iv_tbl) { 330 RCLASS(module)->iv_tbl = st_init_numtable(); 331 } 332 klass->iv_tbl = RCLASS(module)->iv_tbl; /*(B)*/ 333 klass->m_tbl = RCLASS(module)->m_tbl; 334 klass->super = super; /*(C)*/ 335 if (TYPE(module) == T_ICLASS) { /*(D)*/ 336 RBASIC(klass)->klass = RBASIC(module)->klass; /*(D-1)*/ 337 } 338 else { 339 RBASIC(klass)->klass = module; /*(D-2)*/ 340 } 341 OBJ_INFECT(klass, module); 342 OBJ_INFECT(klass, super); 343 344 return (VALUE)klass; 345 } (class.c)
見たことのないものは飛ばしてしまうのが吉だ。
(A)まず新しいクラスを作り、
(B)module
のインスタンス変数テーブル・メソッドテーブルをそれに移植、
(C)インクルード先クラスのスーパークラス(super
)をスーパークラスにする。
つまりこの関数ではmodule
の「化身」とも言うべきクラスを作っているようだ。
(B)でテーブルをコピーせずポインタだけを移しているのがポイントで、後
からメソッドを追加されても実体のモジュールと化身クラスが全く同じメソッ
ドを持つようになる(図11)。
図11: 化身クラス
また(A)をよく見ると構造体型フラグをT_ICLASS
というのにしている。これ
が化身クラスの印らしい。この関数の名前がinclude_class_new()
なのだ
からICLASS
のI
はinclude
だろう。
そしてここまでの作業とrb_include_module()
を合わせて考えると先程の予
想は間違っていなかったとわかる。つまりインクルードとはクラスとその
スーパークラスの間にモジュールの化身クラスを挿入することなのだ
(図12)。
図12: インクルード
そして(D-2)では実体のモジュールを化身クラスのklass
に保存している。す
るとその次からは(D-1)でその実体モジュールが取り出される……と言いた
いのだが、実はここのチェックは必要ない。この関数の最初のほうで既に
T_ICLASS
のチェックをしているから、ここに来るときにまだT_ICLASS
というこ
とはありえないのだ。ruby
はかなり長期間ちょこちょこと変更が重なってきて
いるので、こういう細かい見落としは結構ある。
もう一つ考えることがある。どうやら化身クラスのbasic.klass
は実体のモジュー
ルを示すためだけに使われているようなので、化身クラスに対してメソッドが
呼ばれたりする状況は非常にまずい。だから化身クラスは絶対にRubyプログラ
ムから見えてはならないはずである。そして実際に全てのメソッドは化身クラ
スをスキップする。例外は一切ない。
ややこしかったのでinclude
で起こる操作を具体例を通じて見てみよう。
図13(1)を見てほしい。m2
をインクルードしたモジュール
m1
と、クラスc1
がある。ここからc1
にm1
をインクルードしたときの
変化が(2)と(3)である。im
と書いてあるのはもちろん化身クラスのことだ。
図13: インクルード
rb_include_module
(2)
さてここまでくるとrb_include_module()
の読み飛ばしていた部分を
理解できるようになる。
378 /*(A)スーパークラスで既にmoduleをインクルードしていたらスキップ */ 379 for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) { 380 switch (BUILTIN_TYPE(p)) { 381 case T_ICLASS: 382 if (RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) { 383 if (!superclass_seen) { 384 c = p; /* 挿入地点をずらす */ 385 } 386 goto skip; 387 } 388 break; 389 case T_CLASS: 390 superclass_seen = Qtrue; 391 break; 392 } 393 } (class.c)
klass
のスーパークラス(p
)のうちT_ICLASS
で(化身クラスで)、インクルー
ドしようとしているモジュール(module
)と同じメソッドテーブルを持つもの
があったらそれはp
がmodule
の化身であるということだ。だから二回インクルー
ドしないようスキップする。ただしそのモジュールがインクルードしているモ
ジュール(p->super
)があったらそれは再確認する、というわけだ。
しかし、p
は一度インクルードしたモジュールなのだから、それにインクルー
ドされたモジュールも既にインクルードしているはず……と一瞬思ったのだ
が、以下のような状況がありうる。
module M end module M2 end class C include M # まだM2はMにインクルードされてない。 end # 従ってCのスーパークラスにM2は存在しない。 module M include M2 # 今度はMにM2がインクルードされているので、 end class C include M # ここではM2だけ追加したい end
これを逆に言うと、include
の結果がリアルタイムに伝わらない場合もあると
いうことである。
またクラスの継承の場合にはクラスの特異メソッドも一緒に継承したが、モ
ジュールの場合は特にそのような仕組みはなかった。従ってモジュールの特異
メソッドはインクルード先クラス(またはモジュール)には継承されない。特
異メソッドも継承したいときはModule#append_features
をオーバーライドする
のが常套手段だ。
御意見・御感想・誤殖の指摘などは 青木峰郎 <aamine@loveruby.net> までお願いします。
『Rubyソースコード完全解説』 はインプレスダイレクトで御予約・御購入いただけます (書籍紹介ページへ飛びます)。
Copyright (c) 2002-2004 Minero Aoki, All rights reserved.