2010年9月19日日曜日

Perl best practices[Perlベストプラクティス] 13章 エラー処理 13.1~13.6


このエントリーをはてなブックマークに追加


2つの原則



  1. 検出可能なランタイムエラーをすべて検出し、分類し、報告する

  2. 検出したエラーを無視できないようにする



失敗したときは、フラグなどの値でなく例外を送ることでプログラムを終了させる。


 呼び出したサブルーチンが失敗した場合、そのサブルーチンがエラーを表す値をreturnしても、呼び出し元がそのエラーを無視したり、適切に処理できなければ意味がない。
 フラグ等を使用することの欠点は2つある。
 1つ目は、開発者(サブルーチンを使用して、呼び出し元コードを欠いている人)がエラーを無視することに慣れること。
 2つ目は、何重にもネストして呼び出されたサブルーチンは、一番最初の呼び出し元まで、バケツリレーのように、エラーを適切に処理&上位に送信、を繰り返さなければならない。これでは、すべてのサブルーチンのコードが肥大することになる。バケツリレーでエラーを運ぶのではなく、失敗したサブルーチン内で例外を出すこと。
 この例外をスルーするにはevalを明示的に使用する努力が呼び出し元に要求される。そして、この例外を一番先頭の呼び出し元で補足することで、各ルーチンすべてにバケツリレー処理を書く必要がなくなる。バケツリレー処理を書かないで済む理由は、例外は自動的にバケツリレーをして、上位呼び出し元に運んでくれるから。そして最上位呼び出し元で、明示的に例外が補足されないとプログラムは終了する。
 

 以下のように、ネストではなく、並列に並んだ処理のすべての値をチェックしたい場合も、例外を使用したほうがいい。
コードがすっきりする。



SOURCE_FILE:
for my $filename (@source_files) {
my $fh = locate_and_open($filename);
next SOURCE_FILE if !defined $fh;  #このエラー処理を無くしたい

my $head = load_header_from($fh);
next SOURCE_FILE if !defined $head; #このエラー処理を無くしたい

print $head;
}

for my $filename (@directory_path) {
#例外が出されたときは、evalによって無視されるので、nextと同じ動作になる。
eval {
my $fh = locate_and_open($filename); #サブルーチン内でエラー処理。その際、例外を出すようにする。
my $head = load_header_from($fh);  #サブルーチン内でエラー処理。その際、例外を出すようにする。
print $head;
}
}




組み込み関数のエラーで、例外を出させるようにする


Fatalモジュール

 組み込み関数や、サブルーチンの名前を指定すると、falseを返すのではなく、例外が出されるように黒魔術がかけられる。
 その際、:voidという特殊なマーカを使用しない。これは、voidコンテキスト以外ではFatalが例外を送出しないようにするためのマーカ。紛らわしいので使わない。


やっかいなsystem組み込み関数


 systemコマンドは、成功するとfalseを返し、失敗するとtrueを返す。
これはFatalも効かない。POSIX標準モジュールのWIFEXITEDサブルーチンを使用するか、Perl6モジュールを使用する。(Perl6では、system関数は成功するとture、失敗するとfalseを出すように修正される)



use POSIX qw( WIFEXITED );

WIFEXITED(system $cmd)
or croak "Couldn't run: $cmd ($OS_ERROR)";



use Perl6::Builtins qw( system );

system $cmd
or croak "Couldn't run: $cmd ($OS_ERROR)";



回復可能なエラーでも例外を送出する


 回復可能なエラーではundefを使用した方が良いと思われるが、ここでも例外を送出したほうが良い。理由は、今までと同じで、開発者がエラー処理をするとは限らないから。(フラグ等、戻り値は無視される)


例外は呼び出し元から出す


 サブルーチン内でエラーを返されても役に立たない。呼び出し元からエラーを送出すること。
理由は、あなたが作ったサブルーチンを使用している開発者が知りたいのは、あたたのコードのどこで問題が検出されたのかではなく、自分たちのコードのどこに問題があるか、だから。


dieではなくcroak( )を使用する

 呼び出されたサブルーチンの立場でエラーを報告するdieを使って例外を出すのは、呼び出されたサブルーチンに落ち度があるときだけ。そのときのメッセージは常に"Internal error:"で始まるようにすること。その他の場合はcroak( )を使用すること。
 しかし、内部エラーであってもcroak( )を使用したほうが良いときもある。例えば、内部エラーを呼び出し元の視点に立って報告することが出来る点。さらに重要なのはCarpモジュールにコマンドラインから使用できる'verbose'オプションがあること。このオプションを使用してプログラムを実行すると、croak( )の呼び出しごとに、エラーメッセージと完全なバックトレースが提供される。


組み込みwarnではなく、carp( )を使用する

 dieと同じ理由。警告でもcarp( )を使用すること。





0 件のコメント:

コメントを投稿