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