telcontar has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

My knowledge of Perl is admittedly rather basic. I have been truly bewildered by the behaviour of the following piece of code, specifically that "croak" within sub do_something() does not seem to call "die", and that $@ is not set:
#!/usr/bin/perl package Testing; use HTML::TreeBuilder; sub new { my $class = $_[0]; $class = ref($class) || $class; my $self = { tree => HTML::TreeBuilder->new() }; bless $self, $class } sub DESTROY { my $self = shift; eval { $self->{tree} = $self->{tree}->delete(); }; } sub do_something { Carp::croak "DIE! DIE! DIE!" } package main; eval { my $page = Testing->new(); $page->do_something(); }; if ($@) { print STDERR "Error: " . $@ if $@; exit 1; } exit 0;
A bit of testing has shown that when eval is used in the destructor of package "Testing", the eval in package "main" does not work, e.g. a croak thrown in a sub inside that eval does not seem to call die(). But why?
The eval in "DESTROY" is merely meant to catch (and ignore) any errors that might arise in that block.

I would be grateful for any pearls of wisdom you may offer.

Update: I've figured out that the destructor mangles $@... I've localized $@ and that fixed my problem.
-- tel

Replies are listed 'Best First'.
Re: croak() does not "die" when DESTROY is used
by codeacrobat (Chaplain) on Aug 07, 2007 at 06:40 UTC
    The eval in Testing::DESTROY is called before you print the error. So the script has already forgotten the previous error. You should localize $@ before you run eval blocks.
    This should do the fix.
    sub DESTROY { my $self = shift; local $@; eval { $self->{tree} = $self->{tree}->delete(); }; }
Re: croak() does not "die" when DESTROY is used
by ysth (Canon) on Aug 07, 2007 at 06:27 UTC
    I've figured out that the destructor mangles $@
    Right. eval {} does two things: first, it catches exceptions and places them in $@, and second (and often overlooked) it clears $@ if there were no exceptions. So the do_something method was setting $@ and bailing out of the main eval {}, but as part of that bail out, $page was destroyed, and the destructor's eval {} was clearing the just-set $@.
Re: croak() does not "die" when DESTROY is used
by ikegami (Patriarch) on Aug 07, 2007 at 14:30 UTC

    Check out Object::Destroyer as an alternative to writing your own wrapper for the tree.

    use HTML::TreeBuilder qw( ); use Object::Destroyer qw( ); my $tree = HTML::TreeBuilder->new(); $tree = Object::Destroyer->new($tree, 'delete');