[Moscow.pm] $SIG{__DIE__} и require/use

Nikita Zubkov nikzubkov на gmail.com
Пн Авг 8 07:06:24 PDT 2011


Всем привет.

В своих проектах использую объекты исключений для отслеживания места
их возникновения и прочих вкусных штук. Я использую не
Exception::Class в связи с некоторыми специфическими потребностями, но
проблема касается так же и Exception::Class.

Реализуется все примерно следующим кодом:

my $base_class  = '...'; # базовый класс исключения
my $error_class = '...'; # класс исключения-ошибки

sub rethrow {
	my $e = @_ ? $_[0] : $@;

	die $e
		if blessed( $e ) && $e->isa( $base_class );

	die $error_class->new( $e );
}

sub try(&) {
    my $code = $_[0];

    eval {
        local $SIG{__DIE__} = \&rethrow;
        &$code();
    };

    $@;
}

При создании объекта исключения собирается стэк вызовов.
Используется это все примерно так:

my $e = try {
    # тут потенциально падающий код
};

# показываем расширенную информацию об ошибке
show_error( $e ) if $e;

Все работает отлично, кроме одного случая. Если внутри try я попытаюсь
загрузить модуль, во время загрузки которого происходит ошибка или
вызывается исключение, то обработчик $SIG{__DIE__} вызывается 2 раза и
во-второй раз на входе у него не первоначально созданный объект
исключения, а его "стрингифицированный" вариант (+ дополнительная
информация от компилятора) и в контексте второго вызова уже другое
состояние стэка (без первоначального места возникновения исключения).
Это вызывает 2 проблемы:

1. Теряется точное место возникновения проблемы (его конечно можно в
теории восстановить из сообщения об ошибке, но это костыль)
2. Если делать расширенную стрингификацию (с полный стэком вызовов),
то сообщение об обшибке после второго вызова будет замусорено.

Есть ли какая-либо возможно побороть эту особенность?

-- 
С уважением,
Никита Зубков


Подробная информация о списке рассылки Moscow-pm