Private - パッケージのプライベートな変数やサブルーチンを実行時に隠したり見せたりする
# Foo.pm package Foo; use Private; hide Private; $_private_var = 0; sub _private_func { ++$_private_var; } sub public_func { _private_func(); } 1; # other script file use Foo; $Foo::_private_var = 5; # no error but cannot change original private variable print Foo::public_func(),"\n"; # 1 Foo::_private_func(); # Error! Foo->_private_func(); # Error!
モジュールを作成するときに、そのモジュールをuseで使用するスクリプトからは見えない(=プライベートな)変数やサブルーチンを作りたい場合に、その手段を提供する。ただしプライベート変数についてはファイルスコープでのmy変数にすることで目的を達することができるので、実際はプライベートなサブルーチンを作るのに使うことになるであろう。
プライベートとみなす変数やサブルーチンは、特に指定しなければアンダースコアで始まる名前のものとなるが、正規表現や名前のリストで指定することができる。
そのモジュール自身の中では、プライベートな変数やサブルーチンは普通に使用することができる。ただし実行時に名前が検索される場合、例えばメソッドとして呼び出す場合は、そのモジュール自身の中であっても失敗する。これは、プライベートなサブルーチンがメソッドとしてでなく通常のサブルーチンとして呼ばれるように書かれている場合には、誤ってメソッドとして呼んでしまうプログラムミスを防ぐことになる。
プライベートな変数やサブルーチンを見える状態に戻す手段も用意されている。
いまのところこのモジュールはXSは使用せず、Private.pmだけで構成されている。したがって、Private.pmを@INCのどれかのディレクトリに納めれば、それだけで使えるようになる。
モジュールの先頭のパッケージ宣言の後ろに、
package Foo; use Private; hide Private;
と二行書くだけでよい。これでFooパッケージの、名前がアンダースコアで始まる変数やサブルーチンはすべて、このモジュールを使用するスクリプトからは見えなくなる。
プライベートとする名前を指定するには、
hide Private qw(foo bar);
のように名前のリストを指定する。
hide Private '^__';
のように英数字とアンダースコア以外の文字を含んだ指定をすると、正規表現とみなされる。すなわちこの例は、先頭がアンダースコア二つの名前という指定になる。
プライベートとされた変数やサブルーチンは、そのモジュール自身の中からは普通に使うことができる。
# in Foo.pm sub func { $_private_var++; # OK _private_subroutine(); # OK }
ただし、実行時に名前が検索される場合、例えばメソッドとしてプライベートなサブルーチンを呼ぶ場合は、モジュール自身の中からであっても見えないのでエラーになる。次のサブルーチンは、Fooモジュール自身の中に書かれていても、メソッドが見つからないというエラーになる。
# in Foo.pm sub func { Foo->_private_method(); # Error! }
これを避けるために一時的にプライベートな変数やサブルーチンを見えるようにするには、つぎのようにする。
# in Foo.pm sub func { local(%Foo::); show Private; Foo->_private_method(); # OK }
ここで、local(%Foo::); はサブルーチンを抜けたときにFooパッケージのシンボルテーブルを元に戻すという指定。これを忘れると、func()を実行したあとはずっとプライベートな変数やサブルーチンが見えたままの状態になるので、注意が必要。
AUTOLOADで実行時にプライベートなサブルーチンを定義するような場合は、その後で再度 hide Private; を実行しないとそれはプライベートにはならない。したがって、SelfLoaderやAutoLoaderと併用する場合は注意が必要。プライベートでないサブルーチンを定義する場合は問題ない。
いったんhide PrivateでFooパッケージから隠された変数を外部からアクセスしようとすると、エラーにならずに再びFooパッケージにその変数が作られてしまう。これは元の変数とは別物である。モジュール内部のコードからは相変わらず元の変数がアクセスされる。Perl内部には混乱はないのだが、プログラマにとってはこれは混乱の元であり、デバッグを難しくするかもしれない。
したがって、変数のプライベート化にはPrivateを使用せず、ファイルスコープのmyを使うことをお勧めする。なお、my変数はパッケージには属さないので、Privateの対象とはならない。
このモジュールは実験的かつトリッキーなものである。うまく働かない場面も十分予想される。自己責任で試してみていただきたい。
hide Private で隠されたFooパッケージの変数やサブルーチンの名前は、Private::Fooパッケージに移されている。show Private はPrivate::Fooパッケージのすべての名前をFooパッケージへ輸入する。動作がおかしいときは、パッケージのシンボルテーブル %Foo:: や %Private::Foo:: の内容を調べてみて欲しい。(もちろんFooはあなたのパッケージ名に置き換えて。)
モジュール内のコードが実行されるときには、すでにプライベートな変数やサブルーチンの名前はシンボルテーブルから除かれているのだから、使えるのは不思議に思える。これは、名前と実体とのつながりがコンパイル時に認識され、コンパイルが済んでPerlの内部コードに翻訳された時点ではすでに実体への参照に翻訳されているからである。hide Private で名前が隠されても、実体がなくなったわけではないので、問題なく実行できるのである。したがってモジュール内のコードであっても、実行時に名前から実体を探す場合には、やはり見つからないという結果になる。
つまりPrivateの動作は、
・スクリプトがモジュールファイルをuseで読み込むときに ・その全体がコンパイルされ (モジュール内のプライベート変数やサブルーチンが認識される) ・そして実行され (hide Private が実行されてプライベートな名前が隠される) ・その後でスクリプトの残りの部分がコンパイルされる (すでにプライベートな名前は隠されている)
というシーケンスに依っているのである。
Sey Nakajima <sey@jkc.co.jp>
perl(1).