home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Perl CookbookPerl CookbookSearch this book

13.15. Creating Magic Variables with tie

13.15.3. Discussion

Anyone who's ever used a DBM file under Perl has already used tied objects. Perhaps the most excellent way of using objects is such that the user need never notice them. With tie, you can bind a variable or handle to a class, after which all access to the tied variable or handle is transparently intercepted by specially named object methods (see Table 13-2).

The most important tie methods are FETCH to intercept read access, STORE to intercept write access, and the constructor, which is one of TIESCALAR, TIEARRAY, TIEHASH, or TIEHANDLE.

Table 13-2. How tied variables are interpreted

User code

Executed code

tie $s, "SomeClass"

SomeClass->TIESCALAR( )

$p = $s

$p = $obj->FETCH( )

$s = 10

$obj->STORE(10)

Where did that $obj come from? The tie triggers an invocation of the class's TIESCALAR constructor method. Perl squirrels away the object returned and surreptitiously uses it for later access.

Here's a simple example of a tie class that implements a value ring. Every time the variable is read from, the next value on the ring is displayed. When it's written to, a new value is pushed on the ring. Here's an example:

#!/usr/bin/perl
# demo_valuering - show tie class
use ValueRing;
tie $color, "ValueRing", qw(red blue);
print "$color $color $color $color $color $color\n";
red blue red blue red blue

$color = "green";
print "$color $color $color $color $color $color\n";
green red blue green red blue

The simple implementation is shown in Example 13-3.

Example 13-3. ValueRing

  package ValueRing;
  # this is the constructor for scalar ties
  sub TIESCALAR {
      my ($class, @values) = @_;
      bless  \@values, $class;
      return \@values;
  }
  # this intercepts read accesses
  sub FETCH {
      my $self = shift;
      push(@$self, shift(@$self));
      return $self->[-1];
  }
  # this intercepts write accesses
  sub STORE {
      my ($self, $value) = @_;
      unshift @$self, $value;
      return $value;
  }
  1;

This example might not be compelling, but it illustrates how easy it is to write ties of arbitrary complexity. To the user, $color is just a plain old variable, not an object. All the magic is hidden beneath the tie. You don't have to use a scalar reference just because you're tying a scalar. Here we've used an array reference, but you can use anything you'd like. Usually a hash reference will be used no matter what's being tied because hashes provide the most flexible object representation.

For arrays and hashes, more elaborate operations are possible. Because so many object methods are needed to fully support tied variables (except perhaps for scalars), most users choose to inherit from standard modules that provide base class definitions of customary methods for operations on that variable type. They then selectively override only those whose behaviors they wish to alter.

These four modules are Tie::Scalar, Tie::Array, Tie::Hash, and Tie::Handle. Each module provides two different classes: a bare-bones class by the name of the module itself, as well as a more fleshed out class named Tie::StdTYPE, where TYPE is one of the four types.

Following are numerous examples of interesting uses of ties.

13.15.4. Tie Example: Outlaw $_

This curious tie class is used to outlaw unlocalized uses of the implicit variable, $_. Instead of pulling it in with use, which implicitly invokes the class's import( ) method, this one should be loaded with no to call invoke the seldom-used unimport( ) method. The user says:

no UnderScore;

Then, all uses of the unlocalized global $_ will raise an exception.

Here's a little test suite for the module:

#!/usr/bin/perl
#nounder_demo - show how to ban $_ from your program
no UnderScore;
@tests = (
    "Assignment"  => sub { $_ = "Bad" },
    "Reading"     => sub { print },
    "Matching"    => sub { $x = /badness/ },
    "Chop"        => sub { chop },
    "Filetest"    => sub { -x },
    "Nesting"     => sub { for (1..3) { print } },
);

while ( ($name, $code) = splice(@tests, 0, 2) ) {
    print "Testing $name: ";
    eval { &$code };
    print $@ ? "detected" : "missed!";
    print "\n";
}

The result is the following:

Testing Assignment: detected
Testing Reading: detected
Testing Matching: detected
Testing Chop: detected
Testing Filetest: detected
Testing Nesting: 123missed!

The reason the last one was missed is that it was properly localized by the for loop, so it was considered safe.

The UnderScore module itself is shown in Example 13-4. Notice how small it is. The module itself does the tie in its initialization code.

Example 13-4. UnderScore

  package UnderScore;
  use Carp;
  sub TIESCALAR {
      my $class = shift;
      my $dummy;
      return bless \$dummy => $class;
  }
  sub FETCH { croak "Read access to \$_ forbidden"  }
  sub STORE { croak "Write access to \$_ forbidden" }
  sub unimport { tie($_, _ _PACKAGE_ _) }
  sub import { untie $_ }
  tie($_, _ _PACKAGE_ _) unless tied $_;
  1;

You can't usefully mix calls to use and no for this class in your program, because they all happen at compile time, not runtime. To renege and let yourself use $_ again, local ize it.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.