[Perl]モジュールの動的ロードの方法メモ
Perlでモジュールの動的ロードをする話。
$modName = "Test::Module";
require $modName;
これが通らない。いや通らないのは別にいい。
本来の使い方が違うだけなのだから。
と言う訳でその方法を調べてみた。
他の人はどうやってモジュールの動的ロードを行っているのかと思って色々調べていると、次のような方法があるらしい。
1. evalに文字列としてスクリプトを与え、文字列展開を行ってモジュールをロードする方法
requireの仕様として
require Test::Module;
というように直接モジュール名を指定した場合は、ライブラリにあるTest/Module.pmをロードしてくれる。
但し、
require $modName;
とした場合は、$modNameを文字列そのものとして扱うため、ライブラリにあるTest::Moduleファイルを探しに行ってしまう。
当然、そんなファイルはある訳がないので、
Can't locate Test::Module in @INC (@INC contains: ./lib /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .)
こんな感じのエラーが出る訳だ。
そのため、
eval "require $modName";
とすることで、文字列展開したスクリプトをevalで実行することで、うまくモジュールをロードしてもらおうと言うのがこの方法。
しかし、この方法は遅いらしいし(未検証)、$modNameにPerlスクリプトが書いてあった場合、それも実行してしまうため、$modNameが自由に書き換えられる状態ではセキュリティ的にも危ないので、あまり使い方としてはよくないらしい。(人から聞いた話)
2. Test/Module.pmを直接requireする方法
use './lib';
とかをして、自分でモジュール用のファイルディレクトリを掘ってその中のモジュールファイルをrequireしたい場合などに使える方法
既に言ったが、
require $modName;
では、$modNameを文字列そのものとして扱うため、この中にモジュールファイルを指すパスを指定してやれば、正しく動く。
大昔のjcode.plのロードと同じ使い方。
require './jcode.pl';
require './lib/Test/Module.pm'
但し、これはモジュールが存在している場所を開発者が知らない場合は使えない。
モジュールとして既にインストールされている場合、モジュールファイル自体はシステムのディレクトリの方にあるため、自分でそれを拾いに行くのは難しい。
もしくは、許可されていない可能性もある。
自分の場合はここに入っていた
/usr/local/share/perl/5.10.0
上記は間違いでした。
require "Test/Module.pm";
で、Perlのライブラリとして登録されている場所を探索し、存在した場合それを読み込むようです。
3. UNIVERSAL::requireを使う方法
今はこれがモダンな方法らしい?具体的な意味はよく知らない。
use UNIVERSAL::require;
$modName = "Test::Module";
$modName->require;
のように書けば、指定したモジュールをロードしてくれる。
これを使おうとしてやってみたところ、UNIVERSALとか書いてあるくせに標準で入ってなかった…
use UNIVERSAL::require;
$modName = "Test::Module";
$modName->require;
結果
Can't locate UNIVERSAL/require.pm in @INC (@INC contains: ./lib /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .)
しかもUNIVERSALであるので勝手にロードされてると思い込んで使おうとしたら、ロードされてなかった…
明示的にUNIVERSAL::requireをuseしてやる必要があるらしい
#use UNIVERSAL::require;
$modName = "Test::Module";
$modName->require;
結果
Can't locate object method "require" via package "Test::Module" (perhaps you forgot to load "Test::Module"?)
但し、このUNIVERSAL::requireは、結局内部的に
my $file = $module . '.pm';
$file =~ s{::}{/}g;
return eval { 1 } if $INC{$file};
my $return = eval qq{
CORE::require(\$file);
};
こーいう事をしてるらしい。
安全性は高くなったものの、ロード時間に関しては1の方法と大して変わらないのかも(未検証)。
参考
UNIVERSAL::requireによるモジュールの動的ロード
Perl::Critic / UNIVERSAL::require
$modName = "Test::Module";
require $modName;
これが通らない。いや通らないのは別にいい。
本来の使い方が違うだけなのだから。
と言う訳でその方法を調べてみた。
他の人はどうやってモジュールの動的ロードを行っているのかと思って色々調べていると、次のような方法があるらしい。
1. evalに文字列としてスクリプトを与え、文字列展開を行ってモジュールをロードする方法
requireの仕様として
require Test::Module;
というように直接モジュール名を指定した場合は、ライブラリにあるTest/Module.pmをロードしてくれる。
但し、
require $modName;
とした場合は、$modNameを文字列そのものとして扱うため、ライブラリにあるTest::Moduleファイルを探しに行ってしまう。
当然、そんなファイルはある訳がないので、
Can't locate Test::Module in @INC (@INC contains: ./lib /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .)
こんな感じのエラーが出る訳だ。
そのため、
eval "require $modName";
とすることで、文字列展開したスクリプトをevalで実行することで、うまくモジュールをロードしてもらおうと言うのがこの方法。
しかし、この方法は遅いらしいし(未検証)、$modNameにPerlスクリプトが書いてあった場合、それも実行してしまうため、$modNameが自由に書き換えられる状態ではセキュリティ的にも危ないので、あまり使い方としてはよくないらしい。(人から聞いた話)
2. Test/Module.pmを直接requireする方法
use './lib';
とかをして、自分でモジュール用のファイルディレクトリを掘ってその中のモジュールファイルをrequireしたい場合などに使える方法
既に言ったが、
require $modName;
では、$modNameを文字列そのものとして扱うため、この中にモジュールファイルを指すパスを指定してやれば、正しく動く。
大昔のjcode.plのロードと同じ使い方。
require './jcode.pl';
require './lib/Test/Module.pm'
モジュールとして既にインストールされている場合、モジュールファイル自体はシステムのディレクトリの方にあるため、自分でそれを拾いに行くのは難しい。
もしくは、許可されていない可能性もある。
自分の場合はここに入っていた
/usr/local/share/perl/5.10.0
上記は間違いでした。
require "Test/Module.pm";
で、Perlのライブラリとして登録されている場所を探索し、存在した場合それを読み込むようです。
3. UNIVERSAL::requireを使う方法
今はこれがモダンな方法らしい?具体的な意味はよく知らない。
use UNIVERSAL::require;
$modName = "Test::Module";
$modName->require;
のように書けば、指定したモジュールをロードしてくれる。
これを使おうとしてやってみたところ、UNIVERSALとか書いてあるくせに標準で入ってなかった…
use UNIVERSAL::require;
$modName = "Test::Module";
$modName->require;
結果
Can't locate UNIVERSAL/require.pm in @INC (@INC contains: ./lib /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .)
しかもUNIVERSALであるので勝手にロードされてると思い込んで使おうとしたら、ロードされてなかった…
明示的にUNIVERSAL::requireをuseしてやる必要があるらしい
#use UNIVERSAL::require;
$modName = "Test::Module";
$modName->require;
結果
Can't locate object method "require" via package "Test::Module" (perhaps you forgot to load "Test::Module"?)
但し、このUNIVERSAL::requireは、結局内部的に
my $file = $module . '.pm';
$file =~ s{::}{/}g;
return eval { 1 } if $INC{$file};
my $return = eval qq{
CORE::require(\$file);
};
こーいう事をしてるらしい。
安全性は高くなったものの、ロード時間に関しては1の方法と大して変わらないのかも(未検証)。
参考
UNIVERSAL::requireによるモジュールの動的ロード
Perl::Critic / UNIVERSAL::require
time stamp:2010/03/27 11:07:13
トラックバック(0)|コメント(0)
トラックバック(0)|コメント(0)
この記事のトラックバックURL:
コメントを書く
何かしら