Как я могу автоматически выпускать ресурсы RAII-стиля в Perl? - PullRequest
4 голосов
/ 23 марта 2010

Скажем, у меня есть ресурс (например, файловый дескриптор или сетевой сокет), который должен быть освобожден:

open my $fh, "<", "filename" or die "Couldn't open filename: $!";
process($fh);
close $fh or die "Couldn't close filename: $!";

Предположим, что process может умереть.Затем блок кода завершается досрочно, и $fh не закрывается.

Я мог бы явно проверить ошибки:

open my $fh, "<", "filename" or die "Couldn't open filename: $!";
eval {process($fh)};
my $saved_error = $@;
close $fh or die "Couldn't close filename: $!";
die $saved_error if $saved_error;

, но этот тип кода, как известно, трудно исправитьи только усложняется, когда вы добавляете больше ресурсов.

В C ++ я бы использовал RAII , чтобы создать объект, которому принадлежит ресурс, и чей деструктор освободил бы его.Таким образом, мне не нужно освобождать ресурс, и очистка ресурса происходит правильно, как только объект RAII выходит из области видимости - даже если выдается исключение.К сожалению, в Perl метод DESTROY не подходит для этой цели, так как нет гарантий того, когда он будет вызван.

Существует ли способ Perlish, обеспечивающий автоматическое освобождение ресурсов, как это, даже при наличии исключений?Или явная ошибка проверки единственной опции?

Ответы [ 3 ]

4 голосов
/ 23 марта 2010

Мой модуль Область :: OnExit предназначен именно для этого.

4 голосов
/ 23 марта 2010

Я думаю, это то, что Scope :: Guard было разработано, чтобы помочь.

#!/usr/bin/perl

use strict; use warnings;
use Scope::Guard;

my $filename = 'file.test';

open my $fh, '>', $filename
    or die "Couldn't open '$filename': $!";

{
    my $sg = Scope::Guard->new(
        sub {
            close $fh or die "Could not close";
            warn "file closed properly\n";
        }
    );

    process($fh);
}

sub process { die "cannot process\n" }

Однако, как отмечает @Philip в комментариях, Scope::Guard использует метод DESTROY, который создает некоторую неопределенность относительно того, когда будет запущен код выхода из области. Модули, такие как Hook::Scope и Sub::ScopeFinalizer также выглядят хорошо, хотя я никогда не использовал их.

Мне нравится Попробуйте :: Tiny за его чистый интерфейс и простоту, и это поможет вам правильно обрабатывать исключения:

#!/usr/bin/perl

use strict; use warnings;
use Try::Tiny;

my $filename = 'file.test';

open my $fh, '>', $filename
    or die "Couldn't open '$filename': $!";

try {
    process($fh);
}
catch {
    warn $_;
}
finally {
    close $fh
        and warn "file closed properly\n";
};

sub process { die "cannot process\n" }
3 голосов
/ 23 марта 2010

Хорошая особенность лексических файловых дескрипторов в том, что они закрываются (и освобождаются), когда выходят из области видимости. Так что вы можете просто сделать что-то вроде этого:

{
    # bare block creates new scope
    open my $fh, "<", "filename" or die "Couldn't open filename: $!";
    eval { process($fh) };

    # handle exceptions here

    close $fh or die "Couldn't close filename: $!";
}

# $fh is now out of scope and goes away automagically.
...