Perlで例外をrethrowする
例外のキャッチ&スローに挑戦してみました。
野球とかそうだと思うのですが、
ゴロとか捕球したら、すぐさまファーストに投げなきゃいけないですよね?
今回はそういうの、まったく関係ないです。
という訳で、Exception::Tiny
を使っていきたいと思います。
package MyException; use parent 'Exception::Tiny'; package main; use strict; use warnings; use v5.10; use Try::Lite; my $val = 0; try { if ( $val == 0 ) { MyException->throw( message => 'oops!', value => $val ); } } ( 'MyException' => sub { say $@->dump; }, '*' => sub { say $@; } ); say '=== End of Script ===';
これを実行すると、こんな感じ。
$ perl aaa.pl
bless( {
'subroutine' => 'main::__ANON__',
'value' => 0,
'file' => 'aaa.pl',
'message' => 'oops!',
'package' => 'main',
'line' => 14
}, 'MyException' )
=== End of Script ===
なるほど、これは便利ですね。
例外送出時の状況が詳しく分かれば、デバッグが捗りますね。
ですが、関数の呼び出し元で例外を捕捉したい場合もあるので、
呼び出し先で例外を送出して、呼び出し元で捕捉してみたいと思います。
package MyException; use parent 'Exception::Tiny'; package main; use strict; use warnings; use v5.10; use Try::Lite; main(); say '=== End of Script ==='; sub main { my $val = 0; try { foo( 0 ); } ( 'MyException' => sub { say $@->dump; }, '*' => sub { say $@; } ); } sub foo { my $val = shift; if ( $val == 0 ) { MyException->throw( message => 'oops!', value => $val ); } }
これを実行すると、
$ perl bbb.pl
bless( {
'subroutine' => 'main::foo',
'value' => 0,
'file' => 'bbb.pl',
'message' => 'oops!',
'package' => 'main',
'line' => 31
}, 'MyException' )
=== End of Script ===
呼び出した先で例外が起きても、これだけ情報が得られれば捗りますね。
続いては、キャッチ&スロー。
package MyException; use parent 'Exception::Tiny'; package main; use strict; use warnings; use v5.10; use Try::Lite; main(); say '=== End of Script ==='; sub main { try { foo( 'foo', 'foo', 'bar', 'foo' ); } ( 'MyException' => sub { say $@->dump; }, '*' => sub { say $@; } ); } sub foo { say 'scalar(@_) = ', scalar(@_); my $arg = shift @_; try { if ( $arg ne 'foo' ) { MyException->throw( message => 'oops!' ); } } ( 'MyException' => sub { say $@->dump; $@->{arg} = $arg; # デバッグ用に情報を追加 $@->rethrow; } ); if ( @_ ) { foo( @_ ); } }
実行すると、こんな感じ。
$ perl ccc.pl
scalar(@_) = 4
scalar(@_) = 3
scalar(@_) = 2
bless( {
'subroutine' => 'main::__ANON__',
'file' => 'ccc.pl',
'message' => 'oops!',
'package' => 'main',
'line' => 27
}, 'MyException' )
bless( {
'subroutine' => 'main::__ANON__',
'arg' => 'bar',
'file' => 'ccc.pl',
'message' => 'oops!',
'package' => 'main',
'line' => 27
}, 'MyException' )
=== End of Script ===
こんな感じで、一旦捕捉して、
デバッグ用に情報を追加してから送出してる訳ですが、
このように再帰してても、上位で捕捉できるので便利ですね。
ところで、この例だとfoo
の中でMyException
しか捕捉してないけど、
MyException
以外の例外はどうなるんですかね?
package MyException; use parent 'Exception::Tiny'; package main; use strict; use warnings; use v5.10; use Try::Lite; main(); say '=== End of Script ==='; sub main { try { foo(); } ( 'MyException' => sub { say $@->dump; }, '*' => sub { say sprintf("=== at line %d. ===", __LINE__); say $@; } ); } sub foo { try { die 'oops!'; } ( 'MyException' => sub { say $@->dump; } ); }
結果はこんな感じ。
$ perl ddd.pl
=== at line 20. ===
oops! at ddd.pl line 28.
=== End of Script ===
何のことはなかったですね、
無事、呼び出し元で例外を捕捉できました。
おしまい。
Leave a Comment