Std::Class DEMOLISH doesn’t forward exceptions

Introduction

Std::Class is one of the CPAN modules for implementing object classes in Perl. According to the book Perl Best Practices by Damian Conway it is the preferred method for implementing objects in Perl.

The module creates a DEMOLISH method in every instantiated object. This method is implicitly called every time before the object gets destroyed, so developers can put their own clean-up code in there for e.g. closing file handles.

The purpose of this post is to warn of a discovered deficiency in regard with using exceptions in DEMOLISH code as well as to provide a workaround for the problem.

Test case

Firstly, we’ll create the class Test and store it in the file Test.pm:

package Test ;

use base qw( Exporter ) ;
use strict ;
use Carp ;
use Class::Std ;
use English ;

{
  sub exec_proc {
    croak 'My Exception' ;
  }
  sub DEMOLISH {
    my $a = 0 ;
  }
}
1;

Secondly, we’ll create a Test program, which creates a Test object, then calls the method exec_proc and finally checks for raised exceptions. The sole purpose of the method exec_proc is to croak an exception:

#!/u00/oracle/orabase/local/perl/bin/perl -w
use strict;
use English ;
use Test ;

my $subref =sub  {
  my $p_obj = Test->new( { } ) ;
  $p_obj->exec_proc() ;
} ;

eval { $subref->() ;} ;
if ($EVAL_ERROR) {
  print $EVAL_ERROR ;
} else {
  print "no exception caught" ;
}

No surprise there, the exception gets caught in the main procedure:

My Exception at ./Test.pl line 10.

Now, let’s enclose the DEMOLISH code in an eval block:

package Test ;

use base qw( Exporter ) ;
use strict ;
use Carp ;
use Class::Std ;
use English ;

{
  sub exec_proc {
    croak 'My Exception' ;
  }
  sub DEMOLISH {
    eval {
      my $a = 0 ;
    };
  }
}
1;
no exception caught

Strangely, we didn’t getting any exceptions this time. I reported a bug for this behavior.

Workaround

Apparently, the value of $EVAL_ERROR had been changed within the DEMOLISH method without being restored before returning the call. Localizing $EVAL_ERROR resolves the problem:

package Test ;

use base qw( Exporter ) ;

use strict ;
use Carp ;
use Class::Std ;
use English ;

{
  sub exec_proc {
    croak 'My Exception' ;
  }
  sub DEMOLISH {
    local $EVAL_ERROR ;
    eval {
      my $a = 0 ;
    };
  }
}
1;
My Exception at ./Test.pl line 10.
Thanks for sharing

Nenad Noveljic

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.