Practice of Programming

プログラム とか Linuxとかの話題

local $@ と同一スコープで例外を吐いてはいけない

id:lestrratさんの
http://perl-users.jp/articles/advent-calendar/2008/03.html
を読んで。


そういや、つけたり、つけなかったりしてた...特にポリシーもなく。
全部につける必要はないとのことですが、つけてテストを書いたらはまった。


書いたコードを単純化すると、

eval {
  xxx();
};
print $@ =~/Undefined Subroutine/ ? "ok\n" : "not ok\n# $@\n";

sub xxx {
  local $@;
  eval {
    use Ktat::DummyModule;
  };
  if ($@) {
    die "Got exception: $@";
  }
}

Ktat::DummyFunc()がないというエラーが出るのを確かめるテストだったのだが、取れない。


出力するには。xxx を下記のように変更する必要がある。

sub xxx {
  my $err;
  {
    local $@;
    eval {
      Ktat::DummyFunc();
    };
    $err = $@;
  }
  if ($err) {
    die "Got exception: $err";
  }
}

要するに、local $@; と同一スコープで例外吐いてはいけない、ということ。


local $@と同一スコープの例外も同じ$@には入るため、当然、スコープを抜けた時点で、$@の値が消える。
なので、それをさらに evalしたようなコードでは、$@の値を取得できなくなってしまう。


まぁ、確かに当たり前なんだがー...意識してないとよろしくないですね。


DBD::Proxyにこんなコードがありました。こんなんでもいいですね。
http://www.google.com/codesearch?hl=ja&q=%22local+%24%40%22+die+show:VQ_zqwVcauw:X5g8WUd-O6k:nHF7_TZer6k&sa=N&cd=1&ct=rc&cs_p=http://www.cpan.org/authors/id/T/TI/TIMB/DBI-1.55.tar.gz&cs_f=DBI-1.55/lib/DBD/Proxy.pm#l281

   286:   local $SIG{__DIE__} = 'DEFAULT';
          my $err = do { local $@; eval $method_code.2; $@ };
          die $err if $err;