web-dev-qa-db-ja.com

evalを使用せずにPerlモジュールを動的に含めるにはどうすればよいですか?

Perlモジュールを動的に含める必要がありますが、作業コーディング標準のため、可能であればevalを避けたいと思います。これは機能します:

$module = "My::module";
eval("use $module;");

しかし、可能であればevalなしでそれを行う方法が必要です。すべてのグーグル検索はevalメソッドにつながりますが、他の方法ではありません。

evalなしでそれを行うことは可能ですか?

26
user226723

require を使用して、実行時にモジュールをロードします。モジュールをロードできない場合に備えて、これを(文字列ではなく)ブロックevalでラップすることをお勧めします。

eval {
    require My::Module;
    My::Module->import();
    1;
} or do {
   my $error = $@;
   # Module load failed. You could recover, try loading
   # an alternate module, die with $error...
   # whatever's appropriate
};

eval {...} or do {...}構文と$@のコピーを作成する理由は、$@がさまざまな方法で設定できるグローバル変数であるためです。他の何かが値を別の値に設定する競合状態を回避するために、値をできるだけアトミックに取得する必要があります。

実行時までモジュールの名前がわからない場合は、モジュール名(My :: Module)とファイル名(My/Module.pm)の間の変換を手動で行う必要があります。

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = $@;
    # ...
};
48
Michael Carman

コアモジュールを使用するのはどうですか Module :: Load

あなたの例で:

use Module::Load;
my $module = "My::module";
load $module;

「Module :: Load-モジュールとファイルの両方のランタイム要件」

「ロードにより、ファイルとモジュールのどちらが必要かを知る必要がなくなります。」

失敗すると、 "@ INCでxxxが見つかりません(@INCに含まれるもの:...")のようなもので終了します。

16
DavidG

まあ、常に require のようにあります

require 'My/Module.pm';
My::Module->import();

実行時ではなくコンパイル時に呼び出されるimportから得た効果はすべて失われることに注意してください。

編集:これとevalの方法のトレードオフは次のとおりです。evalを使用すると、通常のモジュール構文を使用でき、モジュール名が無効な場合(単に見つからない場合ではなく)、より明確なエラーが発生します。 OTOH、評価方法は(潜在的に)任意のコードインジェクションの影響を受けやすくなります。

10
Dan

いいえ、evalがなくてはなりません。これは、 perldoc -f require で説明されているように、require()がベアワードモジュール名を必要とするためです。ただし、任意のコードの挿入を許可しないため、evalの悪用ではありません(もちろん、requireingしているファイルの内容を制御できると仮定します)。

EDIT:コードは以下で修正されましたが、完全を期すために最初のバージョンを残しています。

私が使う私は実行時に動的ロードを行うためにこの小さな砂糖モジュールを使用していました

_package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;
    eval "require $class" or do { die "Ack, can't load $class: $@" };
}

1;
_

PS。私はこの定義を見つめています(私はかなり前にそれを書きました)そして私はこれを追加することを考えています:$class->export_to_level(1, undef, @imports);... itshouldwork、しかし、テストされていません。

[〜#〜] edit [〜#〜]:バージョン2になりました。評価がなくても、はるかに優れています(ysthに感謝)::)

_package MyApp::Util::RequireClass;

use strict;
use warnings;

use Exporter 'import'; # gives you Exporter's import() method directly
our @EXPORT_OK = qw(requireClass);

# Usage: requireClass(moduleName);
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports);
sub requireClass
{
    my ($class) = @_;

    (my $file = $class) =~ s|::|/|g;
    $file .= '.pm';
    require $file;  # will die if there was an error
}

1;
_
4
Ether

CPANのClass :: MOPには、このためのload_classメソッドがあります: http://metacpan.org/pod/Class::MOP

1

私は次のようなことをするのが好きです。

require Win32::Console::ANSI if ( $^O eq "MSWin32" );

0
ShoeLace