Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

grantm (164)

grantm
  (email not shown publicly)
http://www.mclean.net.nz/

Just a simple [cpan.org] guy, hacking Perl for fun and profit since way back in the last millenium. You may find me hanging around in the monestary [perlmonks.org].

What am I working on right now? Probably the Sprog project [sourceforge.net].

GnuPG key Fingerprint:
6CA8 2022 5006 70E9 2D66
AE3F 1AF1 A20A 4CC0 0851

Journal of grantm (164)

Saturday January 31, 2009
02:42 AM

Remember Your Responsibilities

[ #38375 ]

A colleague encountered an odd problem the other day. It took us a while to realise what was going on, you'll probably work it out much quicker ...

Here's a simplified version of what we were dealing with:

    use strict;
    use warnings;

    eval {
        my $obj = SomeClass->new();
        die "Code Red!\n";
    };

    if($@) {
        print "Exception caught: $@\n";
    }
    else {
        print "No exception occurred\n"
    }

We both expected it to output:

    Exception caught: Code Red!

but instead we got:

    No exception occurred

You probably worked it out straightaway but for the record, here's what was happening ...

The die statement did generate an exception and the message was stored in $@. Of course the code in the eval block stopped executing immediately but when control flow exited the block, any lexcial variables declared in the block went out of scope. In this case there was only one: $obj. What we didn't realise is that $obj's class defined a destructor method ('DESTROY'). This method is called when the object goes out of scope and apparently in our case the method wrapped something in an eval. Whatever that something was, it completed without generating an exception, so $@ ended up empty - indicating no error and overwriting the exception message that our code put there.

So, if you write a destructor method and it needs to use eval, please remember to use local($@) to restore the previous value of $@ when your routine completes.

Cross-posted from my shiny new blog

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • The problem of needing to localise special variables is discussed in http://perldoc.perl.org/perlobj.html#Destructors [perl.org].

    There is a P::C policy ErrorHandling::RequireCheckingReturnValueOfEval [cpan.org] that catches this programming mistake. Its documentation also offers an alternative approach where you don't need to mess with other people's destructors to make it work.

    package SomeClass;
    sub new {
        return bless \my $foo => __PACKAGE__;
    }

    sub DESTROY {
        eval {'resetting $@'};
    }

    package main;
    use

    • Did you mean to say

      if (
          eval {
              my $obj = SomeClass->new();
              die "Code Red!\n";
              1;
          }
      ) {
          print "No exception occurred\n";
      } else {
          print "Exception caught: $@\n";
      }

  • > use local($@) to restore the previous value of $@ when your routine completes. How? (Searched web but didn't find an answer)
  • Thanks. (This shows the danger of just mixing in code with plain English when you're explaining things; sometimes people don't recognize that it *is* code (even though it's obvious to others))
  • Following the eval block with an "or" seems to be more reliable than using "if ($@)" to check for an exception in an eval block, because an eval block (or even an eval EXPR) returns undef if there is a syntax error or runtime error or a die statement is executed (according to perldoc -f eval).


            eval {
                    my $obj = SomeClass->new();
                    die "Code Red!\n";