在Perl中,最优雅的捕获信号的方法是什么?

3

我在Perl脚本中有两行代码非常接近,可能会引发__WARN__。如果第一行引发了警告,则我希望仅从函数中返回,而不尝试继续执行。

我知道如何在这两行之前设置处理程序,以便可以报告错误等:

local $SIG{__WARN__} = sub {
  my $e = shift;
  # log the error etc.
  return;
};
# possibly warning-resulting line 1
# possibly warning-resulting line 2

但是,这对于两行都发生的情况来说都是适用的。我宁愿它只捕捉到第一个实例并从函数中返回。但是在处理程序中的返回值只返回处理程序,而不是外部函数。
在处理信号时是否有一种方法可以从函数中返回?

1
你期望哪种警告?如果你从一开始就知道警告的原因,为什么不在执行第1和第2行之前在你的子程序中考虑它呢? - Dallaylaen
4个回答

3

将这两行代码分别封装在不同的函数中,让第一个函数返回一个状态指示调用函数应该返回。这样,可以使用单独的函数来处理警告,可以使用相同的函数来进行日志记录。

sub wrap_line_1
{
    local $SIG{__WARN__} = ...;
    ...do line 1...
    return ($warning_fired ? 1 : 0);
}

sub wrap_line_2
{
    local $SIG{__WARN__} = ...;
    ...do line 2...
    return;
}


...calling code...
wrap_line_1() and return;
wrap_line_2();
...

1

只需抛出异常并在调用者中捕获和返回。

使处理程序返回一个值并检查它以在eval后的if语句中返回。

实际上,您甚至不必从处理程序返回值,因为它不决定是否返回 - 您始终在捕获时返回。

具体来说:

# We're in the caller now
eval{
    local $SIG{__WARN__} = sub {
        my $e = shift;
        # log the error etc.
        die MyWarnException->new("This is an exception.");
    };

    # Offending statements go here
    do_things();

};
if( $@ && $@->isa('MyWarnException')){
    return;
}

那么,你的意思是让处理程序设置一个全局标志,然后在随后的代码中检查该标志是否已设置? - John Bachir
实际上,我太匆忙地纠正了自己:你确实需要抛出一个异常。让我编辑答案以展示我的意思。 - trutheality

1

您可以在处理程序中使用die(),并在下面的eval中捕获它:

use 5.12.0;

local $SIG{__WARN__} = sub {
  my $e = shift;
  # log the error etc.
  die "$e";
};

func(undef);
func('honk');

sub func {
    my $foo = shift;

    eval {
       my $bar = "a $foo";
       say "$bar";
    };
    if ($@) {
       die $@ unless $@ =~ /^Use of uninitialized value/;
    }
    return;
}

你可能想直接检查警告是否会发生,然后简单地返回:

sub func {
  my $foo = shift;
  return unless defined($foo);
  ...; # use $foo fine
}

1

不,你不能强制调用者返回。(嗯,我相信你可以通过正确的黑魔法XS咒语来实现。但是不要这样做!)

这似乎是一个错误的设计;警告不应该是致命错误,并且肯定不应该像这样使用。


2
你甚至可以不用黑魔法,只需使用use warnings FATAL => 'all'。这会使任何警告都抛出异常,以便可以相应地处理它们。 - bvr
当然,虽然我非常犹豫地认为这是一个“返回”。提供有关此程序结构的更多信息将会很有帮助。 - Eevee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接