[ABE.pm] My first enclosure doesn't work. :-(

Ricardo SIGNES perl.abe at rjbs.manxome.org
Fri Nov 9 07:45:21 PST 2007


* "Faber J. Fedor" <faber at linuxnj.com> [2007-11-07T13:16:20]
> Here's an example of what I had in mind:
> 
> http://www.netalive.org/tinkering/serious-perl/#import_extend

This tutorial either has been or soon will be added to the Perl 5 Tutorial Hall
of Shame.

  http://www.perlfoundation.org/perl5/index.cgi?hall_of_shame

First of all, it recommends using fields.pm, which is basically dead.  That's
not the real issue, though.  It does importing by using stringy eval, which is
Totally Insane.  How insane is that?  Well, I'd say it's way, way worse than
Exporter.pm, and my feelings about Exporter are well-known.

The code as shown is:

  sub import {
    my($class, @fields) = @_;
    return unless @fields;
    my $caller = caller();

    # Build the code we're going to eval for the caller
    # Do the fields call for the calling package
    my $eval = "package $caller;\n" .
               "use fields qw( " . join(' ', @fields) . ");\n";

    # Generate convenient accessor methods
    foreach my $field (@fields) {
      $eval .= "sub $field : lvalue { \$_[0]->{$field} }\n";
    }

    # Eval the code we prepared
    eval $eval;

    # $@ holds possible eval errors
    $@ and die "Error setting members for $caller: $@";
  }

It should instead be written:

  sub import {
    my($class, @fields) = @_;
    return unless @fields;
    my $caller = caller();

    # This can't be done without a string eval, and is one of the reasons that
    # the fields pragma ("use fields") is a bad idea.... but the use of join
    # was gratuitous, and I've fixed it.
    my $eval = "package $caller;"
             . "use fields qw(@fields)";

    # Generate convenient accessor methods
    foreach my $field (@fields) {
      # We need to turn off strict references so that we can alter a subroutine
      # based on its name as a string.  This is one of the few cases where
      # turning off strict makes perfect sense.
      no strict 'refs';
      *{ $caller . '::$field' } = sub : lvalue { $_[0]->{ $field } };
    }
  }

This code is significantly shorter, less prone to weird bugs, and can fail
nicely at compile time if there's a typo.  My gut also tells me that it would
be a tiny bit faster, but that's not a tested belief.

-- 
rjbs


More information about the ABE-pm mailing list