#navi_header|Perl| Perl(正確にはPerl5.X系列まで)にはtry-catch構文は存在しない。しかし、evalやcoderefを引数にとることができるなどの特徴を用い、幾つかの疑似try-catch的テクニックは存在する。Error.pmとうモジュールも公開されており、こちらを活用するとOOP的なtry-catchを実現できる。 参考ページ: - Error.pm -- http://search.cpan.org/~shlomif/Error-0.17008/lib/Error.pm -- http://www.perl.com/pub/a/2002/11/14/exception.html -- http://www.perl.com/pub/a/2002/11/14/exception.html?page=2 - 日本語のError.pm紹介ブログentry -- http://qootas.org/blog/archives/2006/08/perltry_catch.html * eval{}中のdieを$@で判別する基本形 eval{}コードブロック中でのdieは、コードを抜けた後、 '' $@ '' 変数で参照できる。これを利用して、以下のような疑似try-catch機能を利用できる。 eval { # do-something if ( exception-condition ) { die "exception message"; } }; if ($@) { &someErrorHandler($@); } これが、perl.comにも載っているし、自分自身、恐らくどこかしかで目にしたのだろう、いつの間にかおぼえていた疑似コードである。(ひょっとしたらperl.comで目にした記憶が時間軸を前後したのかも知れない。) これについてはわざわざコードピースを示すまでもない。 * Error.pmの使用例 perl.comでも掲載されている、Perlでtry-catch, そしてOOPな例外処理機構を使用するのにお奨めのモジュール、それがError.pmらしい。perl.comに掲載されている記事やCPANのPODを元に、簡単なコードピースで実験をしてみる。 - コードピース:スクリプトの引数に応じて発生させるErrorオブジェクトを変化させ、独自Errorや基本的なErrorを発生させてみる。 #!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Error qw(:try); use Switch; my $d = shift || 0; my $a = -1; try { switch($d) { case -2 { # Error::Simpleをそのままthrowしてみる。 throw Error::Simple("d = -2"); } case -1 { # 元になるErrorオブジェクトを直にthrowしてみる。 throw Error(-text => "d = -1"); } case 0 { # Error::Simpleをシンプルに継承したものをthrowしてみる。 throw Error::TestException("d = 0"); } # 以下はErrorを元にした独自Error case 1 { throw Test1HogeException("d = 1"); } case 2 { throw Test2HogeException("d = 2"); } else { throw HogeException("d = else"); } } $a = $d; } catch HogeException with { my $e = shift; print "======== Hoge Exception ========\n"; print Dumper($e), "\n"; } catch Error with { my $e = shift; print "e = $e\n"; print Dumper($e), "\n"; print "========warn======\n"; warn $e->text; } finally { print "\n======= last a = [$a]\n"; }; package BoheException; use base qw(Error); package HogeException; use base qw(Error); use overload ('""' => 'stringify'); sub new { my $self = shift; my $text = "".shift; my @args = (); local $Error::Depth = $Error::Depth + 1; local $Error::Debug = 1; $self->SUPER::new(-text => $text, @args); } package Test1HogeException; use base qw(HogeException); package Test2HogeException; use base qw(HogeException); package Error::TestException; use base qw(Error::Simple); 引数に応じて何パターンか試してみる。 - 引数 = -2 e = d = -2 at ./try03.pl line 15. $VAR1 = bless( { '-file' => './try03.pl', '-text' => 'd = -2', '-line' => 15, '-package' => 'main' }, 'Error::Simple' ); ========warn====== d = -2 at ./try03.pl line 43. ======= last a = [-1] - 引数 = -1 e = d = -1 $VAR1 = bless( { '-file' => './try03.pl', '-text' => 'd = -1', '-line' => 18, '-package' => 'main' }, 'Error' ); ========warn====== d = -1 at ./try03.pl line 43. ======= last a = [-1] - 引数 = 0 e = d = 0 at ./try03.pl line 21. $VAR1 = bless( { '-file' => './try03.pl', '-text' => 'd = 0', '-line' => 21, '-package' => 'main' }, 'Error::TestException' ); ========warn====== d = 0 at ./try03.pl line 43. ======= last a = [-1] - 引数 = 1 ======== Hoge Exception ======== $VAR1 = bless( { '-stacktrace' => 'd = 1 at ./try03.pl line 24 ', '-file' => './try03.pl', '-text' => 'd = 1', '-line' => 24, '-package' => 'main' }, 'Test1HogeException' ); ======= last a = [-1] - 引数 = 2 ======== Hoge Exception ======== $VAR1 = bless( { '-stacktrace' => 'd = 2 at ./try03.pl line 27 ', '-file' => './try03.pl', '-text' => 'd = 2', '-line' => 27, '-package' => 'main' }, 'Test2HogeException' ); ======= last a = [-1] - 引数 = 3 ======== Hoge Exception ======== $VAR1 = bless( { '-stacktrace' => 'd = else at ./try03.pl line 30 ', '-file' => './try03.pl', '-text' => 'd = else', '-line' => 30, '-package' => 'main' }, 'HogeException' ); ======= last a = [-1] 細かい解説は見れば自明なので省略するが、とりあえず独自のErrorクラスを構築し、Javaなどと同じ使い心地で使用できるのを確認できた。 * try{}中で直接 warn, die を使ってみる。 上記例はあくまでも教科書通りに、Errorオブジェクトをthrowしている。では実際に、try {} 中で die や warn が発生するとどうなるかを確認しておく。 - コードピース #!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Error qw(:try); try { warn "warning!!"; die "die!!"; } catch Error with { my $e = shift; print "e = $e\n"; print Dumper($e), "\n"; print "========warn======\n"; warn $e->text; } finally { print "\n======= last \n"; }; - 出力 warning!! at ./try04.pl line 9. e = die!! at ./try04.pl line 10. $VAR1 = bless( { '-file' => './try04.pl', '-text' => 'die!!', '-line' => '10', '-package' => 'Error' }, 'Error::Simple' ); ========warn====== die!! at ./try04.pl line 16. ======= last '' die の場合、Error::Simpleにラッピングされてthrowされていることを確認できた。 '' * try{}中で直接 carp, croak を使ってみる。 warn, die と同じ効果になると思われる。 - コードピース #!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Carp; use Error qw(:try); try { carp "carp!!"; croak "croak!!"; } catch Error with { my $e = shift; print "e = $e\n"; print Dumper($e), "\n"; print "========warn======\n"; warn $e->text; } finally { print "\n======= last \n"; }; - 出力 carp!! at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 428 e = croak!! at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 428. $VAR1 = bless( { '-file' => '/usr/lib/perl5/site_perl/5.8.5/Error.pm', '-text' => 'croak!!', '-line' => '428', '-package' => 'Error' }, 'Error::Simple' ); ========warn====== croak!! at ./try04.pl line 19. ======= last ほぼ予想通りである。[[Perl/codepiece/carp01]]では、(carp,croak)と(cluck,confess)系で違いが見られなかった、とあるが、ここでようやく明らかな出力の差異が認められた。上記コードピースのcarp, croakをそれぞれcluck, confessで置き換えた結果を下に示す。 cluck!! at ./try04.pl line 12 main::__ANON__() called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 428 eval {...} called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 420 Error::subs::try('CODE(0x816446c)', 'HASH(0x8164448)') called at ./try04.pl line 24 e = confess!! at ./try04.pl line 15 main::__ANON__() called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 428 eval {...} called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 420 Error::subs::try('CODE(0x816446c)', 'HASH(0x8164448)') called at ./try04.pl line 24. $VAR1 = bless( { '-file' => './try04.pl', '-text' => 'confess!! at ./try04.pl line 15 main::__ANON__() called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 428 eval {...} called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 420 Error::subs::try(\'CODE(0x816446c)\', \'HASH(0x8164448)\') called', '-line' => '24', '-package' => 'Error' }, 'Error::Simple' ); ========warn====== confess!! at ./try04.pl line 15 main::__ANON__() called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 428 eval {...} called at /usr/lib/perl5/site_perl/5.8.5/Error.pm line 420 Error::subs::try('CODE(0x816446c)', 'HASH(0x8164448)') called at ./try04.pl line 21. ======= last このように、cluck, confessを使用すると、細かいスタックトレースが出力できることが分かった。 結論として '' Error.pm と Carp 系は共存可能 '' であることを確認できた。 #navi_footer|Perl|