2008年06月30日 04:00 [Edit]

perl - use Carp; # warn() と die() だけじゃなくて

camel

以下のエラー処理の方法にちょっと口を挟みたくなったので。


warn() vs. die()

まず、warn()の使い方。エラーに対して使ってはいけません。warnはその名の通り、あくまでもエラーではなく警告です。エラーの際にはdie()を使わなければなりません

警告とエラーの違いですが、警告は「続けて処理してもいいけど、これはあなたの意図ではないかもしれません」という場合、エラーは「このままでは処理を続けられません」という場合です。

たとえばperlの場合、抜けた引数はundefなので、数値としては0、文字列としては''として解釈されます。それらをデフォルト値として使っていいのであれば警告で済ませても構いませんが、そうでない場合はエラーとして処理するべきです。

仮に関数でdie()してしまう場合でも、ユーザーはそこでperlをexitせずに処理を続けることも可能です。

404 Blog Not Found:perl - There's more than one way to 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.

その逆に、以下のようにして単なる警告をエラー化することも可能です。

[Run via codepad]
use strict;
use warnings;

warn "Just a warning.";
local $SIG{__WARN__} = sub{ die @_ };
warn "This warning is fatal.";
print "If you see this, your perl is kaputt\n";

見ての通り、最後のprint()が実行されることはありません。

迷った時はどちらにするかですが、die()にしとくことをお勧めします。特にCGIなどはそうです。セキュリティもセイフティもその方が上がります。

use Carp; # 使われる側は

で、本題です。perlにはCarpというモジュールが標準添付されています。これって何でしょうか?

実際の動きを見てみましょう。

[Run via codepad]
use strict;
use warnings;
use Carp;

sub warn_me{ warn @_ }
sub carp_me{ carp @_ }

warn_me "Hey!";
carp_me "You!";
Hey! at t.pl line 5.
You! at t.pl line 6
	main::carp_me('You!') called at t.pl line 9

見ての通り、carp()では「どこで警告を発した」かだけではなく、「どの呼び出しが警告を発したか」がわかります。呼び出し元がわかるのです。

これは、モジュールのように、呼び出し元と呼び出し先が異なるスクリプトで実装されている場合、特に有用です。たいていの場合、知りたいのはモジュールのどこで止まったかではなく、それを利用したスクリプトのどこで止まったかなのですから。

warn()に対してcarp()があるように、die()に対してはcroak()があります。以下が対応表となります。

w/o stacktracew/ stacktrace
warn()carp()cluck()cluckは明示的にimportの必要あり
die()croak()confess()

モジュール中の関数やメソッドでは、必ずこちらを使うよう心がけましょう。.pmならcarp/croakを、.plならwarn/dieと覚えておけばよいでしょう。

Dan the Perl Monger

See Also:

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

この記事へのコメント
DQNさん、
あれまほんとだ。Codepadの方はちゃんとしているのに。というわけで訂正。
# ちなみにuse Carp;抜けだと、「carpなんてしらね」というエラーで死にます。
Dan the Typo Generator
Posted by at 2008年07月02日 03:45
Danさん
use Carp;
が抜けております。
Posted by DQN at 2008年07月02日 01:18