他のPerlコード、特にパッケージ(.pm)をロードするには "use" 或いは "require" が使用される。
一般に"use"はコンパイル時に評価され、"require"は実行時に評価されると言われている。
ここでは、BEGIN/CHECK/INIT/END ブロックでprintさせることで、実際にどちらがどの段階でどう処理されるのかを浅いレベルで確認する。
まず、普通にuseを使用したときのBEGIN/CHECK/INIT/ENDブロックの呼ばれる順序を確認する。前掲のperlmodによれば、各ブロックが複数回定義されている場合以下の順序で呼ばれるはずである。
また、useする方でも複数箇所にBEGIN - ENDを仕掛け、useする/される側でどういった順序でBEGN - ENDブロックがコールされていくのかも確認してみる。
#!/usr/bin/perl use strict; use warnings; use File::Basename; # @INCの調整 BEGIN { my (undef, $__base_dir, undef) = fileparse($0); push(@INC, $__base_dir); print __PACKAGE__, " BEGIN (1)\n"; } CHECK { print __PACKAGE__, " CHECK (1)\n"; } INIT { print __PACKAGE__, " INIT (1)\n"; } END { print __PACKAGE__, " END (1)\n"; } use Hoge::Bohe; BEGIN { print __PACKAGE__, " BEGIN (2)\n" } CHECK { print __PACKAGE__, " CHECK (2)\n" } INIT { print __PACKAGE__, " INIT (2)\n" } END { print __PACKAGE__, " END (2)\n" } BEGIN { print __PACKAGE__, " BEGIN (3)\n" } CHECK { print __PACKAGE__, " CHECK (3)\n" } INIT { print __PACKAGE__, " INIT (3)\n" } END { print __PACKAGE__, " END (3)\n" } &Hoge::Bohe::bohe(); print "end.\n";
package Hoge::Bohe; use strict; use warnings; BEGIN { print __PACKAGE__, " BEGIN (1)\n" } INIT { print __PACKAGE__, " INIT (1)\n" } CHECK { print __PACKAGE__, " CHECK (1)\n" } END { print __PACKAGE__, " END (1)\n" } sub bohe { print "Here in Hoge::Bohe::bohe();\n"; } BEGIN { print __PACKAGE__, " BEGIN (2)\n" } INIT { print __PACKAGE__, " INIT (2)\n" } CHECK { print __PACKAGE__, " CHECK (2)\n" } END { print __PACKAGE__, " END (2)\n" } BEGIN { print __PACKAGE__, " BEGIN (3)\n" } INIT { print __PACKAGE__, " INIT (3)\n" } CHECK { print __PACKAGE__, " CHECK (3)\n" } END { print __PACKAGE__, " END (3)\n" } 1; __END__
$ perl -c test01.pl main BEGIN (1) # BEGINが出現した順番に呼ばれている。 Hoge::Bohe BEGIN (1) # test01.plから見て、途中でuseが入っていても、 Hoge::Bohe BEGIN (2) # 純粋にBEGINの出現順に呼ばれている。 Hoge::Bohe BEGIN (3) main BEGIN (2) main BEGIN (3) main CHECK (3) # CHECKがLIFOで呼ばれている。 main CHECK (2) # -> このネストも純粋に"出現した順のLIFO"で Hoge::Bohe CHECK (3) # 呼ばれている。 Hoge::Bohe CHECK (2) Hoge::Bohe CHECK (1) main CHECK (1) test01.pl syntax OK
$ ./test01.pl main BEGIN (1) Hoge::Bohe BEGIN (1) Hoge::Bohe BEGIN (2) Hoge::Bohe BEGIN (3) main BEGIN (2) main BEGIN (3) main CHECK (3) main CHECK (2) Hoge::Bohe CHECK (3) Hoge::Bohe CHECK (2) Hoge::Bohe CHECK (1) main CHECK (1) main INIT (1) # INITがFIFOで呼ばれている。 Hoge::Bohe INIT (1) # これも純粋に"出現順のFIFO"のようである。 Hoge::Bohe INIT (2) # useされる/する側のネスト構造とは無関係に、 Hoge::Bohe INIT (3) # それこそlexicalにFIFOで呼ばれている。 main INIT (2) main INIT (3) Here in Hoge::Bohe::bohe(); end. main END (3) # ENDもINIT同様に、こちらはLIFOで呼ばれている。 main END (2) Hoge::Bohe END (3) Hoge::Bohe END (2) Hoge::Bohe END (1) main END (1)
以上より、BEGIN, CHECKはperldocの通りコンパイル時に処理され、INIT, ENDは実行時に処理されること。およびその順序がperldocの通りであることを確認できた。
実際にrequireの実験に入る前、なぜuseではなくrequireを使うのか、使う時について簡単にメモしておく。
結論から言うと、パッケージ名を設定ファイルなどで外部化したい時、useのパッケージ名は変数を受け付けないからである。
例えば以下のコードはコンパイルエラーとなる。
my $pkg_name = "Hoge::Bohe"; use $pkg_name;
→
syntax error at ****.pl line XX, near "use $pkg_name" ****.pl had compilation errors.
しかしrequireであれば、パッケージ名に変数を使用できる。結局、Factoryを介してロードするモジュールを切り替えたい時は、useではなくrequireしか利用できない。こういった局面ではrequireの方が効果的である。
Hoge::Bohe.pmの方は変更せず、test01.pl の use を require にして確認してみる。
use Hoge::Bohe; → require Hoge::Bohe; # 注意!! require "Hoge::Bohe"ではない。"bare-word"であること。
$ perl -c test01.pl main BEGIN (1) # Hoge::BoheのBEGIN, CHECKが呼び出されていない main BEGIN (2) # = コンパイルされていない。 main BEGIN (3) main CHECK (3) main CHECK (2) main CHECK (1) test02.pl syntax OK
$ ./test02.pl main BEGIN (1) main BEGIN (2) main BEGIN (3) main CHECK (3) main CHECK (2) main CHECK (1) main INIT (1) main INIT (2) main INIT (3) Hoge::Bohe BEGIN (1) Too late to run INIT block at Hoge/Bohe.pm line 6. Too late to run CHECK block at Hoge/Bohe.pm line 7. Hoge::Bohe BEGIN (2) Too late to run INIT block at Hoge/Bohe.pm line 15. Too late to run CHECK block at Hoge/Bohe.pm line 16. Hoge::Bohe BEGIN (3) Too late to run INIT block at Hoge/Bohe.pm line 20. Too late to run CHECK block at Hoge/Bohe.pm line 21. Here in Hoge::Bohe::bohe(); end. Hoge::Bohe END (3) Hoge::Bohe END (2) Hoge::Bohe END (1) main END (3) main END (2) main END (1)
Hoge::BoheのINIT, CHECKが実行されていない。 代わりにWarningが出力されている。perldiag(http://perldoc.perl.org/perldiag.html)参照。
ちなみにHoge::BoheからINIT, CHECKを除去すればwarningは出力されなくなる。
また require の引数に "bare-word" を使用している点に注目する。ここを下記の用にquoted-stringにすると、エラーとなる。
require "Hoge::Bohe";
→
... main INIT (3) Can't locate Hoge::Bohe in @INC (@INC contains: ... ) at test02.pl line 18. main END (3) ...
perldiagには(F)、すなわちトラップ可能なFatal Errorとして扱われている。とにもかくにも、エラーでPerlインタプリタ自体は終了し、ENDブロックの実行に進んでいる。
実は、これは本当にrequireを使いたい "パッケージ名の外部化" も同様のwarningがでてしまうことを意味する。
my $pkg_name = "Hoge::Bohe"; require $pkg_name;
これは上記と同じ結果になる。
というのも、bare-wordの場合、Perl側で "::" をディレクトリセパレータにし、拡張子に勝手に.pmを付けてくれる。すなわち、
require Hoge::Bohe; イコール require "Hoge/Bohe.pm";
が成立する。というわけで、quoted stringでパッケージ名を指定してしまうと、本当に"Hoge::Bohe"というファイル名を見に行ってしまう。
もちろん、これを回避する方法はしっかりとperldocに記載されている。 requireのperldoc(http://perldoc.perl.org/functions/require.html)を見ると、以下のような回避策が有効とされている。
eval "require $pkg_name";
実際、これで test02.pl も問題なく Hoge/Bohe.pm をロードできた。(ただし、もちろんCHECK, INITのwarning付き)
コメント