2008年05月10日 15:00 [Edit]

perl - There's more than one way to die

YAPC::Asia::2008

いよいよ来週はYAPC::Asia::2008ですが、Hotel DANは本日から開業。我が家では本日から20日までが「YAPC旬間」です。

というわけで、その間は少なくとも一つはperlがらみのentryにしていく所存です。

で、第一回はいきなりdieの話題です。


開発日誌 pp-perl: Perlのエラー処理
プログラムを停止して、メッセージ(LIST)をSTRERRに出力します。

間違っていませんが正しいとは言えません。

die = throw

die()とは何か?

まずは、以下のコードをご覧下さい。

[Run via codepad]
eval {
  my $zero = 0;
  my $dead = 1 / $zero;
};
if ($@){
    print STDERR "OUCH: $@";
}

こうなったはずです。

OUCH: Illegal division by zero at t.pl line 3.

見ての通り、ここでperlはすぐに「死んだ」わけではありません。その代わりに、特殊変数$@に「例外情報」を書き込んでeval { }から抜けています。Javaなどのtry/catch、rubyのbegin/rescueとほぼ同じです。

なお、eval {}は、eval ""と違って、文字列を評価しなおすことはありません。速度的なペナルティはゼロです。

ここで、改めてdie()を見てみましょう。

[Run via codepad]
eval {
  die "goodbye";
  print "This line will not be executed.\n";
};
if ($@){
    print STDERR "OUCH: $@";
}

これでおわかりでしょう。

die()はJavaなどのthrow、rubyのraiseなのです。

「プログラムを停止して、メッセージをSTDERRに出力」というのは、die()のデフォルトの振る舞いに過ぎないのです。

exitするな、dieせよ

現代的なPerlプログラミングでは、exit()の使用は戒められています。die()と違って例外を補足できないからです。

[Run via codepad]
eval {
  print STDERR "goodbye";
  exit 255;
  print "This line will not be executed.\n";
};
if ($@){
    print STDERR "OUCH: $@";
}

上のコードでは、"goobye"としか表示されません。

本当の本当にexit()するしかない場合を除いて、なるべくdie()するように心がけましょう。

die $object

実はdieの引数は、文字列ではなくオブジェクトでも構いません。その場合は、$@の値がそのオブジェクトになります。

[Run via codepad]
{
    package Dead;
    sub new{
        my $pkg = shift;
        bless { @_ }, $pkg;
    }
}

eval {
  open my $fh, '>', '.' or die Dead->new(message => $!);
};
if ($@){
    use  Data::Dumper;
    print STDERR Dumper($@);
}

$SIG{__DIE__}

さらにperlでは、__DIE__という疑似シグナルをサポートしています。ここにcodeを指定することにより、die()の前処理を定義することも出来ます。

[Run via codepad]
my $what_happened;
eval{
    local $SIG{__DIE__} = sub {
        $what_happened = shift;
    };
    die "I am not dead yet";
    print "I will not be executed";
};
print STDERR qq(what_happend = '$what_happened'\n);
die "normally";

同様に、__WARN__という疑似シグナルも存在します。

まとめ

  • die()は例外送出である
  • デフォルトではエラーメッセージをSTDERRに吐いてexitしてくれる
  • exit()はなるべく使わずにdie()を使え
  • OOなdie()もアリ
  • $SIG{__DIE__}で前処理も可能

See Also:

Dan the Mortal Perl Monger


この記事へのトラックバックURL

この記事へのトラックバック
以下のエラー処理の方法にちょっと口を挟みたくなったので。 エラー処理を行う。 - サンプルコードによる Perl 入門
perl - use Carp; # warn() と die() だけじゃなくて【404 Blog Not Found】at 2008年06月30日 04:06
 最近、第二弾の執筆でBlogの更新もままならないのですが、以前、告知した掲載Blogの一覧を少しやってみたんですが、現在、90と少しになるようです。  驚くのは、ほとんどがきちんとした書評形式でとりあげていただいており、かつマイナスの評価が見当たらないこ
「ありがとう」x2万5000回は 約7時間【水野俊哉の日記】at 2008年05月10日 18:43
この記事へのコメント
>(eval {}は)速度的なペナルティはゼロです。
それはいいすぎでしょ。eval ""と比較すればかなり効率的ではありますが。

あと、s/補足/捕捉/ですね。
Posted by H.I. at 2008年05月13日 10:53
確かに例外といえばそうだが、補足側で何が起きたかの判別を容易にするような投げ方がなされていない。
せいぜい"$@"に対して正規表現マッチするくらい。
JavaでもPythonでもRubyでも例外クラスできれいに分岐できるのにね。
Posted by 774 at 2008年05月11日 04:47