2006 年 4 月 18 日 18 時 23 分

シンボルテーブルへの項目追加


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


文字列として与えられたパッケージに対して、
動的にメソッドを登録には、もう一つ壁がある

汎用化するため、プロパティを登録するパッケージは、
caller 関数で得られる「名前」となる。
「名前」は文字列のデータなので、
文字列を元にパッケージを探して、
それにメソッドを追加する必要がある。

自身のパッケージ(カレントパッケージ)の場合、
以下のようにコードを登録していた。

    package Employee;

    *method = sub { print 'method' };
    *Employee::method = sub { print 'method' };

上記は両方とも、シンボルテーブルを意識しない
通常の変数記法であり、どちらも同じ効果を持つ。
上がカレントパッケージにおける記法であり、
下が、パッケージ名を明示しているだけの違いだ。
これは、結局、以下の記述と同等であるというのも述べた。

    sub method {
        print 'method'
    }

これらは通常の記法であるため、コードでは
シンボルテーブル %Employee:: を直接操作していない。
Perl によって暗黙的に %Employee:: が編集されている。

Perl は *method や、sub method という記述を見つけ、
シンボルテーブルに新しく 'method' を追加する。
そして、その項目(型グロブ)のコード用のスロットに
サブルーチンを設定し、パッケージに登録完了となる。

ルーチンを動的に追加する場合に問題となるのはここだ。

型グロブは、シンボルテーブルの項目を指す特別な型なので、
ユーザによって作成することはできない。
シンボルテーブルに項目を確保するためには、
Perl によって作成してもらう必要がある。

そのためには上記のような通常の記法が必要になる。
パッケージ名や名前に文字列を使って指定することはできず、
それらを識別子として明示しなければならないと言うことだ。

では、試してみよう。

    *Employee::method = sub { print 'method' };

これは通常の記法に当たるので OK だ。
%::Employee シンボルテーブルに、'method' が追加され、
$::Employee{'method'} 型グロブのコードスロットに、
sub { print 'method' } が登録される。
これにより、パッケージのメソッドとして利用可能になる。

    my $symbols = \%Employee::;

    $symbols->{'method'} = sub { print 'method' };
    *{$symbols->{'method'}} = sub { print 'method' };

これは、シンボルテーブルを直接操作する例だ。
$symbols にシンボルテーブルのリファレンスを得たが、
その後の代入で問題が起きる。

シンボルテーブルの項目は型グロブでなければならないため、
対象のパッケージに $method や @method などが既に存在し、
$symbols->{'method'} が型グロブの場合は偶然成功するが、
そうでない場合は失敗する。

上の方は、$symbols->{'method'} が型グロブでなく、
コードリファレンスになってしまっている。
これはシンボルテーブルを破壊してしまう結果となり、
コードが正常に動作するか予測できない。

下の方は、$symbols->{'method'} が型グロブであることを
デリファレンス記法を用いて明記しているが、
$symbols->{'method'} は undef なので、
グロブへのデリファレンスに失敗してエラーとなる。

さて、これは困ったことになった。
今回は、文字列で受け取ったパッケージ名を元に、
メソッドを追加登録する必要がある。
なにか解決策はあるのだろうか。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.