Item 54: Know how and when to use eval, require, and do.

' 'eval', 'require', and 'do'."-->

Item 54: Know how and when to use eval , require , and do .

One of the advantages that Perl shares with certain other interpreted languages is the ability to compile and execute code at run time. The basic mechanism for run-time compilation is the string form of the eval operator, which takes a string argument containing source code. For example:

 $varname = 'some_var';  $val = eval " $$varname "; 

$val gets whatever $some_var contains.

The contents of the string are compiled and then run in the context of the callerin the current package, with package and local variables (even my variables) available to it. The string is compiled each time the eval is executed. The result of eval is the value of the last expression evaluated inside the eval , similar to the way subroutines work. Should the code fail to compile, or suffer a run-time exception (as from die , divide-by-zero, etc.), execution of the eval -ed code ends and an error message is returned to the calling context in the special variable $@ :

Handle exceptions (inefficiently) with string eval .

 eval q{    open F1, $fname1 or die "$!";    open F2, $fname2 or die "$!";    # some other stuff ...  }; 

Single-quoting with q{} .

Try to open some files.

Note closing semicolon!

 if ($@ ne '') {    warn "error in eval: $@\n";    # clean up ...  } 

Exceptions come here.

The exception-handling abilities of eval are very handy. However, if all you need to do is add exception handling to an unchanging hunk of code, you should not be using the string form of eval . You should be using the block form.

Exception handling with eval

The block form of eval takes a block as its argument. The block form is used solely for exception handling, because the block is compiled only once, at the same time as the surrounding code:

Handle exceptions with block eval (preferred).

 eval {    open F1, $fname1 or die "$!";    open F2, $fname2 or die "$!";    # some other stuff ...  }; 

A block instead of a string.

Now, compiled with the surrounding code rather than when eval is encountered at run time.

 if ($@ ne '') {    warn "error in eval: $@\n";    # clean up ...  } 

Exceptions come heresame as with string form.

The block form of eval turns out to be more useful than the string form. Occasions to use the string form are rare (but see the example later in this Item). Although both forms of eval will catch exceptions, neither will catch signals, panics, or other types of "really fatal" errors. They also cannot "bring you back" from an exec or something similar. You can, however, benefit by adding signal handlers to eval blocks:

Use eval with signal handlers.

 eval {    local $SIG{INT} = sub {      die "caught an interrupt"    };    my $foo = <>;  }; 

Install a signal handler to catch control-C (or whatever).

Wait for some input.

 if ($@) {    print "error in eval: $@\n";  } 

Control goes here if the user hits control-C while the program waits for input above.

Incorporating source files at run time with require

Although the string form of eval is rarely used, it forms the basis for a very important "file" form of run-time compilation, require .

The require directive takes a numeric or string argument. The numeric form of require causes a fatal error if the current version of Perl is not equal to or greater than the numeric argument:

 require 5; 

Fatal error unless running at least version 5.000.

 require 5.004; 

Fatal error unless running at least version 5.004.

The string version, which is the one more relevant to the current discussion, reads in Perl source code at run time and executes it. In other words, it eval s the contents of a file:

Loading and executing source code at run time with require

File foo.pl

 print "loading foo!\n"; 

Will be executed when require -d.

 sub bar { print "sub bar\n" } 

Define a subroutine.

 1; 

Indicate successful loading.

Main program

 #!/usr/local/bin/perl 

Wherever Perl is.

 require "foo.pl"; 

Prints loading foo!

 &bar(); 

Prints sub bar

Unlike eval , require uses the last expression evaluated in the included source file to determine whether the inclusion was successful. If the value is false, the load is deemed unsuccessful and require produces a fatal exception. This is why Perl module and library source files often end in " 1; " on a line by itself.

In the good old days of Perl 4, require was the primary mechanism used to support Perl libraries. Library source files would typically define subroutines and (possibly) run some initialization code, much as in the example above. The use directive has largely supplanted require , although use is built on require (see Item 42).

Now, what require does is actually a little more sophisticated than reading source code and eval -ing it. (See do , discussed later, for the bare bones version.) First, require only loads a file once. Attempts to require a file more than once are ignored. Second, require searches the module include path for the specified filename. (The include path would ordinarily include the current directory.) See Item 43 for more about the module include path . Finally, if the argument to require is a bareword (an unquoted identifier), require automatically adds the extension .pm ("Perl Module") to the argument and searches for a file by that name . This is part of require 's support for the use directive.

Doing things with do

We have seen how eval has two somewhat different meanings (string and block form) and how require also does (Perl version and source file inclusion). We are about to discuss Perl's do operator, and you might wonder whether it too has more than one meaning.

Of course it does.

The file form of do is similar to require , but has fewer frills. It returns the value of the last statement evaluated in the included file, and it makes no difference whether this value is true or false. Neither does the file form of do presume a .pm suffix in the case of a bareword argument. The file form of do does, however, use the module include path. The file form of do can be useful at times. It is a handy way to load a " configuration file" of data, if the data can be written as Perl source:

Loading configuration files with require

File config.dat

 $ROWS = 25;  $COLS = 80; 

Some configuration values.

Main program

 #!/usr/local/bin/perl  die "where is config.dat? "    unless -e "config.dat"; 

See if our file is there.

 do "config.dat"; 

Read in some data.

It is also useful in combination with Data::Dumper (see Item 37) and other modules that generate Perl code.

The other form of do , the block form, has nothing to do with either files or eval , but we might as well cover it here. The block form returns the value of the last statement evaluated in its argument block:

 $max = do {    if ($a > $b) {      $a    } else {      $b    }  }; 

Returns the greater of $a and $b .

The block form is hacked so that if it is used as the expression argument to a statement modifier, it is always evaluated once before the modifier's condition is tested . This allows you to write do { } while loops in Perl:

 do { $i *= 2 } while $i <= 1024; 

Multiply $i by 2 at least once.

Creative uses for string eval

The only occasions in which the string form of eval is really worthwhile is when there is a need to read or generate, then execute, Perl code on the fly. You might, for example, allow a user to type in a function, then compile it so that it could be plotted or analyzed for roots, minima, maxima, or whatever. Hopefully this will be a trusted user who will stick to typing in mathematical functions; otherwise you should check out the Safe module.

You can also use string eval to generate boilerplate functions automatically. Here is a slightly contrived example along those lines. The following code automatically generates "get" and "set" functions for Perl objects:

Generating class boilerplate with string eval

The code below allows you to declare a Perl class and its member variables with a class function, using syntax like:

 class MyClass qw(Member1 Member2 Member3); 

The class function automatically defines a constructor for MyClass , as well as methods to set and get the values of each member, for example:

 $myObj = new MyClass;    $myObj->Member1($some_val);    $val = $myObj->Member2(); 
 package UNIVERSAL;  sub AUTOLOAD {    my ($pkg, $func) =      ($AUTOLOAD =~ /^(.*)::(.*)$/);    return if ($func eq 'DESTROY');    if ($func ne 'class') {      die "No such function $AUTOLOAD";    } 

To make the nice declaration syntax below work, we have to define a UNIVERSAL AUTOLOAD function (see Item 50 ). It will catch calls to functions named class , in any package.

 my $class = shift;    my @members = @_; 

When called with indirect object syntax (see Item 50 ), $class and the package name (from $AUTOLOAD ) will be the same, so we just ignore $class below.

 eval qq{      package $pkg;      sub new {        my $self = { };        bless $self, '$pkg';      }    }; 

Double-quote with qq{} .

Must set the proper package.

This eval creates a constructor for the class.

Note backslashes in front of $self , in honor of qq{} .

 foreach $member (@members) {      eval qq{        package $pkg;        sub $member {          my $self = shift;          if (\@_) {            $self->{$member} = shift;          } else {            $self->{$member};          }        }      }    }  } 

Now, for each of the members listed:

Set the package again.

This eval creates a function that returns the value of the member if no arguments are supplied, or that sets the value of the member with the first argument.

 package main; 

Get back to main .

 class Student qw(firstname lastname id); 

Here's our declaration syntax at workthis will create a constructor for class Student , as well as get/set functions for it.

 $student = new Student;  $student->firstname('Joseph');  $student->lastname('Hall');  $student->id('7777'); 

Let's use the constructor.

Now set first name, last name, and id.

 print "Name = ", $student->firstname(),    " ", $student->lastname(), "\n";  print "Id = ", $student->id(), "\n"; 

Name = Joseph Hall

Id = 7777

It might seem that this sort of application absolutely requires the use of string eval , but it doesn't. You can achieve the same effect by using closures (see Item 29), along with assignments to typeglobs to give them globally visible names . Closures are a more difficult mechanism for most programmers to understand, though, and in this case eval is probably the best way to go.

For a real module providing this kind of functionality, check out Class::Template .



Effective Perl Programming. Writing Better Programs with Perl
Effective Perl Programming: Writing Better Programs with Perl
ISBN: 0201419750
EAN: 2147483647
Year: 1996
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net