Полезная информация

Advanced Perl Programming

Advanced Perl ProgrammingSearch this book
Previous: 8.4 ResourcesChapter 9Next: 9.2 Tying Arrays
 

9. Tie

Contents:
Tying Scalars
Tying Arrays
Tying Hashes
Tying Filehandles
Example: Monitoring Variables
Comparisons with Other Languages

Give me a wild tie brother,
One with a cosmic urge,
A tie that will swear and rip and tear,
When it sees my old blue serge.

- Stoddard King, The Ties That Bind

Normally, when you read or update a scalar, array, hash, or filehandle, Perl performs the appropriate operation on the corresponding internal data structure. Alternatively, you can use the tie keyword to bind the value (or variable) to a user-defined object, so that when you read from or write to that variable, Perl simply calls a specific method of the object it is tied to. In other words, while it provides the implementation for a "normal" variable, Perl expects a user-defined module to do so for a tied variable. Once a variable is tied, even accesses from the C API of the Perl library are delegated to the corresponding tied object.

This approach may seem like syntactic sugar, but as you'll see from the examples in this chapter, it is the syntax that gives it its power: an ordinary variable can be made to invoke a user-defined function whenever a variable is manipulated, without the user's code changing or being necessarily aware of the subterfuge. The most common use of this technique is to tie a hash variable to a module that can manipulate DBM files, which are typically disk-based hash tables (they can also be BTrees). This technique allows you to make a hash value persistent and is capable of storing much more information than can fit into available memory, while giving the impression that you are manipulating an ordinary associative array.

In the following pages, we will study how tie works with the various data types, and look at a few useful examples of this feature.

9.1 Tying Scalars

At the most basic level, there are only four things you can do with a scalar. You can create it, get or set its value, and destroy it (by ending the scope or undef'ing it). tie allows you to supply a subroutine to be called back for each of these operations.

The syntax of tie is as follows:

tie variable, classname, list;

The first parameter should be one of the four supported types described above. The second parameter is the name of a user-defined class. You are expected to have invoked use classname or require classname before calling tie.

When this statement is executed, Perl checks the type of the variable (the first parameter). It then calls the method classname->TIESCALAR(list), or TIEARRAY, TIEHASH, or TIEHANDLE, depending on whether the tied variable is a scalar, array, hash, or filehandle. It is a run-time error if this method is not present in the class. TIESCALAR() is expected to return an object, which is then internally associated with (or tied to) the given variable. Now, when you read and write to the variable, Perl internally calls object->FETCH() and object->STORE(new value), respectively. Finally, when the tied variable goes out of scope, Perl calls object->DESTROY(). Simple, isn't it?

The names FETCH, STORE, and TIESCALAR are similar to AUTOLOAD and DESTROY in that they have a special significance for Perl only under appropriate circumstances. That is, a module can have a method called FETCH, which can be used normally like any other user-defined subroutine. But if you use tie, this method assumes a special meaning.

Perl does not care about the exact data structure used for the object (whether you used a hash or ObjectTemplate). Table 9.1 shows a variable "$temperature" tied to an automatic temperature control system, which is represented by a Perl module called AC.pm.[1] An attempt to read $temperature's value is translated to a call to the temperature sensor, and an attempt to set its value translates to a command to the heating system to do what is needed.

[1] Air conditioning, not alternating current!


Table 9.1: Flow of Control in a Tie Scalar Operation

When you say:

Perl translates it to the method call:

The method looks like this:

tie $temperature,
    'AC';
$obj = AC->TIESCALAR()

Perl now "ties"

$temperature and $obj

package AC;
sub TIESCALAR {
  ...
  $ac = bless {...},
        $pkg;
  return $ac;
}
$x = $temperature;
$x = $obj->FETCH();
sub FETCH {
  $ac->get_temp();
}
$temperature = 20;
$obj->STORE(20);
sub STORE {
 ($obj, $t) = @_;
 $ac->set_temp($t);
}
untie $temperature;
#or
undef $temperature;

or when $temperature goes out of scope

$obj->DESTROY()
sub DESTROY {
}

As you can see, the AC module is an ordinary class with a constructor and three object methods (whose names happen to be special). Perl interacts with this module behind the scenes, providing the user with a much simpler interaction model. You can get the tied object as the return value of tie or invoke the tied function to get to it at any other time. Therefore the statement

$temperature = 20;

is identical to

(tied $temperature)->STORE(20);

The untie function restores the original value of the variable and also calls the object's DESTROY method.

Perl does not constrain the object's module in any way other than to expect it to provide the methods we saw earlier. It can store whatever data it wants, can have other methods, and is perfectly usable even in a non-tie context.

9.1.1 Example: Stopwatch

Let us look at a simple example of a stopwatch using a tied scalar. When you store any value into it, it notes the current time (that is, it ignores the value). When you retrieve a value from it, it returns the amount of time elapsed since the last time a store was attempted on it. This is how it is used:

use Stopwatch;
tie $s, 'Stopwatch';

# $s is scalar transparently tied to a Stopwatch object.
$s = 0;             # Writing to it forces a reset
sleep(10);          # Sleep 10 seconds
print "$s\n";       # Should print 10

The example might sometimes print 9 because of sleep's resolution.

Example 9.1 shows how Stopwatch is implemented.

Example 9.1: Stopwatch Implemented Using tie

package Stopwatch;

sub TIESCALAR {
   my ($pkg) = @_;
   my $obj = time();  # $obj stores the time at last reset.
   return (bless \$obj, $pkg);
}

sub FETCH {
   my ($r_obj) = @_;
   # Return the time elapsed since it was last reset 
   return (time() - $$r_obj); 
}

sub STORE {
   my ($r_obj, $val) = @_;
   # Ignore the value. Any write to it is seen as a reset
   return ($$r_obj = time());
}

1;

TIESCALAR notes the current time and returns a reference to a blessed scalar (with the current time in it). As was mentioned earlier, you are under no obligation to provide a blessed scalar reference; Perl does not care whether the object is a scalar or an array or a complex data structure. The only requirement is that it be blessed into a module that supports the FETCH and STORE methods. In this case, FETCH computes the interval between the current time (as reported by time) and the last reset time.

Incidentally, the time calculations in this module work at the granularity of only a second. If you want a finer granularity, you can use the Time::HiRes module available from CPAN, which gives microsecond resolution on Unix systems (gives access to the usleep and ualarm system calls). On Microsoft Windows systems, you can use the Win32::Timer call for millisecond-level timing.

9.1.1.1 tie works with anonymous values

The first argument to tie must boil down to a scalar, array, hash, or filehandle value ; it does not have to be a variable. The following code shows two valid examples of scalar ties:

$r = \$s; 
tie $$r, 'Stopwatch';  # Indirect tie to $s

@foo = (1, 2);
tie $foo[1], 'Stopwatch';

As you can see, this facility works with the underlying values and is not associated with a variable name (unlike the trace facility in TCL).


Previous: 8.4 ResourcesAdvanced Perl ProgrammingNext: 9.2 Tying Arrays
8.4 ResourcesBook Index9.2 Tying Arrays