2

我有一个对象,我做了很多事情,然后通过调用 TidyUp() 方法来完成,该方法显示一些关于发生了什么的统计信息。

作为内部测试的一部分,我想检测这些对象何时被创建,但在程序退出之前没有整理,在这种情况下会死掉。

因此,我为在 TidyUP() 中设置的对象添加了一个标志,并为如果未设置此标志而死的类添加了一个 DESTROY 子例程。

这一切都很好,但它有一个不幸的副作用。如果程序中出现其他问题,导致 die 被调用,那么全局销毁会触发我的 DESTROY,它会注意到对象没有被整理并死亡。问题是我此时丢失了原始的 die 消息。

所以我想检测破坏是由于骰子还是正常程序退出而发生的,只有在后一种情况下才添加我的新骰子。

我看过Detecting global destroy in Perl但我认为这没有帮助,因为我想检测是什么触发了全局破坏。

4

3 回答 3

0

您可以挂钩到$SIG{__DIE__}全局异常处理程序:

#!/usr/bin/perl
use Modern::Perl;

package Foo;
my $DIED = 0;
BEGIN { $SIG{__DIE__} = sub { $DIED = 1 }; }

sub new { bless [0] }
sub DESTROY { die 'untidy' unless shift->[0] or $DIED }
sub tidy_up { shift->[0] = 1 }

package main;
my $foo = new Foo;
die if @ARGV; # line 13
$foo->tidy_up();
say 'success';

$ perl test.pl
success

$ perl test.pl die
Died at test.pl line 13.

由于该异常处理程序是全局安装的,因此请确保您没有覆盖现有的错误处理程序。Signals::XSIG 对此有所帮助。

于 2013-01-10T21:39:58.367 回答
0

通过解决这个问题,我看到方法中的调用堆栈DESTROY根据程序的退出方式略有不同。也许这会有所帮助:

package Foo;
sub new { bless [], __PACKAGE__ }
sub DESTROY {
    my ($n,$pkg,$file,$line);
    while (my @c=caller($n++)) {
        ($pkg,$file,$line) = @c;
    }
    print STDERR "DESTROY called via $file line $line\n";
}
my $foo = Foo->new;
if ($ARGV[0] eq 'die') { die }       # line 11
if ($ARGV[0] eq 'exit') { exit }     # line 12
# else passive exit

$ perl 14255585.pl die
Died at 14255585.pl line 11.
DESTROY called via 14255585.pl line 11

$ perl 14255585.pl exit
DESTROY called via 14255585.pl line 12

$ perl 14255585.pl foo
DESTROY called via 14255585.pl line 0

如果程序中的退出点列表很小且定义明确,则可以枚举它们并在程序结束时处理它们。否则,您可以进行一些即时静态代码分析,以查看可能的死亡原因是什么。

于 2013-01-10T16:01:28.657 回答
0

您可以在检查对象是否已整理之前设置一个全局标志。然后你知道你的程序在哪个阶段运行。您还可以尝试在程序终止时使用$SIG{__DIE__}. 检查$?是不安全的,因为它可以通过其他方式设置。检查您是否处于全球破坏中也应该有效。但最干净的解决方案是将所有需要整理的对象存储在一个额外的数组中。然后,您可以随时循环遍历这些对象,而无需使用DESTROY.

于 2013-01-10T14:17:15.750 回答