Previous Page Next Page

Chapter 15. Those Magic Ties and DBM Stuff

15.1. Tying Variables to a Class

Normally when you perform some operation on a variable, such as assigning, changing, or printing the value of the variable, Perl performs the necessary operations on that variable internally. For example, you don't need a constructor method just to create a variable and assign a value to it, and you don't have to create access methods to manipulate the variable. The assignment statement $x=5; doesn't require any tricky semantics. Perl creates the memory location for $x and puts the value 5 in that location.

It is now possible to bind an ordinary variable to a class and provide methods for that variable so that, as if by magic, the variable is transformed when an assignment is made or a value is retrieved from it. A scalar, array, or hash, then, can be given a new implementation. Unlike objects, where you must use a reference to the object, tied variables, once created, are treated like any other variable. All of the details are hidden from the user. You will use the same syntax to assign values to the variable and to access the variable as you did before tying.[1] The magic goes on behind the scenes. Perl creates an object to represent the variable and uses predefined method names to construct, set, get, and destroy the object that has been tied to the variable. The programmer who creates the class uses the predefined method names, such as FETCH and STORE, to include the statements necessary to manipulate the object. The user ties the variable and, from that point on, uses it the same way as he would any other variable in his program.

[1] DBM databases use the tie mechanism to automatically perform database operations on data.

15.1.1. The tie Function

The tie function binds a variable to a package or class and returns a reference to an object. All the details are handled internally. The tie function is most commonly used with associative arrays to bind key/value pairs to a database; for example, the DBM modules provided with the Perl distribution use tied variables. The untie function will disassociate a variable from the class to which it was tied. The format for tie follows.

Format

$object =  tie variable, class, list;
untie variable;

tie variable, class, list;
$object = tied variable;


The tie function returns a reference to the object that was previously bound with the tie function or undefined if the variable is not tied to a package.

15.1.2. Predefined Methods

Tying variables allows you to define the behavior of a variable by constructing a class that has special methods to create and access the variable. The methods will be called automatically when the variable is used. A variable can't be tied to any class but must be tied to a class that has predefined method names. The behavior of the variable is determined by methods in the class that will be called automatically when the variable is used. The constructors and methods used to tie (constructor) and manipulate (access methods) the variable have predefined names. The methods will be called automatically when the tied variables are fetched, stored, destroyed, and so on. All of the details are handled internally. The constructor can bless and return a pointer to any type of object. For example, the reference may point to a blessed scalar, array, or hash. But the access methods must return a scalar value if TIESCALAR is used, an array value if TIEARRAY is used, and a hash value if TIEHASH is used.

15.1.3. Tying a Scalar

In order to use a tied scalar, the class must define a set of methods that have predefined names. The constructor TIESCALAR is called when the variable is tied, and it creates the underlying object that will be manipulated by the access methods STORE and FETCH. Any time the user makes an assignment to the tied scalar, the STORE method is called, and whenever he attempts to display the tied scalar, the FETCH method is called. The DESTROY method is not required but if it is defined, will be called when the tied scalar is untied or goes out of scope. Methods provided for a tied scalar are

TIESCALAR $classname, LIST
STORE $self, $value
FETCH $self
DESTROY $self

There is also a base module in the standard Perl library for tying scalars that provides some skeletal methods for scalar-tying classes. See the perltie man page for a list of the functions required in tying scalar to a package. The basic Tie::Scalar package provides a new method, as well as methods TIESCALAR, FETCH, and STORE. For documentation of this module, type perldoc Tie::Scalar.

Example 15.1 demonstrates tying a scalar and how to access the tied scalar.

Example 15.1.

    # File is Square.pm
    # It will square a number, initially set to 5
1   package Square;
2   sub TIESCALAR{
3       my $class = shift;
4       my $data = shift;
5       bless(\$data,$class);   # Blessing a scalar
    }
6   sub FETCH{
7       my $self = shift;
8       $$self **= 2;
    }
9   sub STORE{
10      my $self = shift;
11      $$self = shift;
    }
    1;
---------------------------------------------------------------------

    # User program
12  use Square;
13  $object=tie $squared, 'Square', 5;  # Call constructor TIESCALAR
14  print "object is $object.\n";
15  print $squared,"\n";      # Call FETCH three times
16  print $squared,"\n";
    print $squared,"\n";
    print "----------------------------\n";

17  $squared=3;            # Call STORE

18  print $squared,"\n";   # Call FETCH
    print $squared,"\n";
    print $squared,"\n";

19  untie $squared;     # Break the tie that binds the
                        # scalar to the object
(Output)
14  object is Square=SCALAR(0x1a72da8).
15  25
16  625
    390625
    ----------------------------
18  9
    81
    6561

					  

Explanation

  1. The package/class Square is declared. The file is Square.pm.

  2. TIESCALAR is the constructor for the class. It creates an association between the tied scalar and an object. Look at line 13 of this example. This is where the constructor is called. The variable tied to the object is $squared.

  3. The first argument to the constructor is the name of the class. It is shifted from the @_ array and assigned to $class.

  4. Look again at line 13. The tied variable, $squared, is followed by the name of the class and then the number 5. The class name is passed first, followed by 5. In the constructor, the number 5 is shifted from the @_ array and assigned to $data.

  5. A reference to the scalar is created and passed to the bless function, which creates the object.

  6. The FETCH method is defined. This access method will retrieve data from the object.

  7. The first argument to the FETCH method is a reference to the object.

  8. The pointer is dereferenced and its value squared.

  9. The STORE method is defined. It will be used to assign a value to the object.

  10. The first argument to the STORE method is a reference to the object.

  11. The value to be assigned is shifted off the @_ array and assigned to $$self.

  12. This is the user/driver program. The Square module is loaded into the program.

  13. The tie function automatically calls the constructor TIESCALAR. The tie function ties the variable, $squared, to the class and returns a reference to the newly created object.

  14. The reference, $object, points to a scalar variable found in the Square class.

  15. The FETCH method is automatically called and the current value of $squared is printed.

  16. The FETCH method is called again. Each time the method is called, the current value of the tied variable is squared again and returned.

  17. The STORE method is automatically called. A new value, 3, is assigned to the tied variable.

  18. The FETCH method is automatically called and displays the squared value.

  19. The untie method disassociates the object with the tied variable. If a DESTROY method had been defined, it would have been called at this point.

15.1.4. Tying an Array

In order to use a tied array, the class must define a set of methods that have predefined names. The constructor TIEARRAY is called when the variable is tied, and it creates the underlying object that will be manipulated by the access methods STORE and FETCH. Any time the user makes an assignment to the tied array, the STORE method is called, and whenever he attempts to display the tied array, the FETCH method is called. The DESTROY method is not required but, if it is defined, will be called when the tied scalar is untied or goes out of scope. There is also a set of optional methods that can be used with tied arrays. STORESIZE sets the total number of items in the array, FETCHSIZE is the same as using scalar(@array) or $#array + 1 to get the size of the array, and CLEAR is used when the array is to be emptied of all its elements. There are also a number of methods, such as POP and PUSH, that emulate their like-named Perl functions in manipulating the array. Methods provided for an array are

TIEARRAY $classname, LIST
STORE $self, $subscript, $value
FETCH $self, $subscript, $value
DESTROY $self
STORESIZE  $self, $arraysize
FETCHSIZE  $self
EXTEND $self, $arraysize
EXISTS $subscript
DELETE $self, $subscript
CLEAR $self
PUSH $self, LIST
UNSHIFT $self, LIST
POP $self
SHIFT $self
SPLICE $self, OFFSET, LENGTH, LIST

There is also a base class called Tie::Array in the standard Perl library that contains a number of predefined methods from the preceding list, making the implementation of tied arrays much easier. To see documentation for this module, type perldoc Tie::Array.

Example 15.2.

1   package Temp;
2   sub TIEARRAY {
3       my $class = shift;  # Shifting the @_ array
4       my $obj = [ ];
5       bless ($obj, $class);
    }
    # Access methods
6   sub FETCH {
7       my $self=shift;
8       my $indx = shift;
9       return $self->[$indx];
    }

10  sub STORE {
11      my $self = shift;
12      my $indx= shift;
13      my $F = shift;    # The Fahrenheit temperature
14      $self->[$indx]=($F - 32) / 1.8;   # Magic works here!

    }
    1;
---------------------------------------------------------------------
    #!/bin/perl
    # The user/driver program
15  use Temp;
16  tie @list, "Temp";
17  print "Beginning Fahrenheit: ";
    chomp($bf = <STDIN>);
    print "Ending temp: ";
    chomp($ef = <STDIN>);
    print "Increment value: ";
    chomp($ic = <STDIN>);
    print "\n";
    print "\tConversion Table\n";
    print "\t----------------\n";

18  for($i=$bf;$i<=$ef;$i+=$ic){
19      $list[$i]=$i;
20      printf"\t$i F. = %.2f C.\n", $list[$i]; }


(Output)
17  Beginning Fahrenheit: 32
    Ending temp: 100
    Increment value: 5

    Conversion Table
    ----------------
20  2 F. = 0.00 C.
    37 F. = 2.78 C.
    42 F. = 5.56 C.
    47 F. = 8.33 C.
    52 F. = 11.11 C.
    57 F. = 13.89 C.
    62 F. = 16.67 C.
    67 F. = 19.44 C.
    72 F. = 22.22 C.
    77 F. = 25.00 C.
    82 F. = 27.78 C.
    87 F. = 30.56 C.
    92 F. = 33.33 C.
    97 F. = 36.11 C.

					  

Explanation

  1. This is the package/class declaration. The file is Temp.pm.

  2. TIEARRAY is the constructor for the tied array. It creates the underlying object.

  3. The first argument passed into the constructor is the name of the class, Temp.

  4. A reference to an anonymous array is created.

  5. The object is blessed into the class.

  6. The access method FETCH will retrieve elements from the tied array.

  7. The first argument shifted off and assigned to $self is a reference to the object.

  8. The next argument is the value of the subscript in the tied array. It is shifted off and assigned to $indx.

  9. The value of the array element is returned when the user/driver program attempts to display an element of the tied array.

  10. The access method STORE will assign values to the tied array.

  11. The first argument shifted off and assigned to $self is a reference to the object.

  12. The next argument is the value of the subscript in the tied array. It is shifted off and assigned to $indx.

  13. The value of the Fahrenheit temperature is shifted from the argument list. See line 19 where the assignment is being made in the user/driver program. The value being assigned is what will be stored in $F in the STORE method.

  14. The calculation to convert from Fahrenheit to Celsius is made on the incoming tied array element. The user/driver program never sees this calculation, as though it were done by magic.

  15. This is the user's program. The Temp.pm module is loaded into the program.

  16. The tie function ties the array to the class Temp and returns an underlying reference to an object that is tied to the array.

  17. The user is asked for input. He will provide a beginning Fahrenheit temperature, an ending Fahrenheit temperature, and an increment value.

  18. A for loop is entered to iterate through the list of temperatures. It charts the Fahrenheit temperature and the corresponding Celsius temperature after the conversion.

  19. The magic happens here. When the assignment is made, the STORE method is automatically called, where the formula converts the Fahrenheit temperature to Celsius and assigns it to the array. A reference to the tied object and the array index are passed to STORE.

  20. When printf is used, the FETCH method will automatically be called and display an element of the tied array. A reference to the tied object and the array index are passed to FETCH.

 

15.1.5. Tying a Hash

In order to use a tied hash, the class must define a set of methods that have predefined names. The constructor TIEHASH is called when the variable is tied, and it creates the underlying object that will be manipulated by the access methods STORE and FETCH. Any time the user makes an assignment to the tied hash, the STORE method is called, and whenever he attempts to display the tied hash, the FETCH method is called. The DESTROY method is not required but, if it is defined, will be called when the tied hash is untied or goes out of scope. There is also a set of optional methods that can be used with tied hashes. DELETE removes a key/value pair, EXISTS checks for the existence of a key, and CLEAR empties the entire hash. If you use Perl's built-in keys, values, or each methods, the FIRSTKEY and NEXTKEY methods are called to iterate over the hash. Methods provided for an associative array are

TIEHASH $classname, LIST
FETCH $self, $key
STORE $self, $key
DELETE $self, $key
EXISTS $self, $key
FIRSTKEY $self
NEXTKEY $self, $lastkey
DESTROY $self
CLEAR $self

There is also a base class module called Tie::Hash in the standard Perl library that contains a number of predefined methods from the preceding list, making the implementation of tied arrays much easier. To see documentation for this module, type perldoc Tie::Hash.

Example 15.3.

 (The Script)
    #!/bin/perl
    # Example using tie with a hash

1   package House;
2   sub TIEHASH {             # Constructor method
3       my $class = shift;    # Shifting the @_ array
        my $price = shift;
        my $color = shift;
        my $rooms = shift;
4       print "I'm the constructor in class $class.\n";
5       my $house =  {  Color=>$color,    # Data for the tied hash
6                       Price=>$price,
                        Rooms=>$rooms,

                     };
7       bless $house, $class;
    }
8   sub FETCH {               # Access methods
        my $self=shift;
        my $key=shift;
9       print "Fetching a value.\n";
10      return $self->{$key};
    }
11  sub STORE {
        my $self = shift;
        my $key = shift;
        my $value = shift;
        print "Storing a value.\n";
12      $self->{$key}=$value;
    }
    1;

---------------------------------------------------------------------

    # User/driver program
13  use House;
    # The arguments following the package name are
    # are passed as a list to the tied hash
    # Usage: tie hash, package, argument list
    # The hash %home is tied to the package House.
14  tie %home, "House", 155000, "Yellow", 9;
                    # Calls the TIEHASH constructor
15   print qq/The original color of the house: $home{"Color"}\n/;
                    # Calls FETCH method
16  print qq/The number of rooms in the house: $home{"Rooms"}\n/;
17  print qq/The price of the house is: $home{"Price"}\n/;
18  $home{"Color"}="beige with white trim";   # Calls STORE method
19  print "The house has been painted. It is now $home{Color}.\n";
20  untie(%home);     # Removes the object
(Output)
4   I'm the constructor in class House.
9   Fetching a value.
15  The original color of the house: Yellow
9   Fetching a value.
16  The number of rooms in the house: 9
9   Fetching a value.
17  The price of the house is: 155000
    Storing a value.
    Fetching a value.
    The house has been painted. It is now beige with white trim.

					  

Explanation

  1. The package/class House is declared. The file is House.pm.

  2. The constructor TIEHASH will tie a hash to an object.

  3. The first argument is the name of the class, $class. The rest of the arguments shifted, and all will be assigned as values to the keys of an anonymous hash, the object.

  4. The printout shows that the constructor class is called House.

  5. A reference to an anonymous hash is created and with key/value pairs assigned. They will become the properties of the object.

  6. The values assigned to the keys of the anonymous hash were passed to the constructor TIEHASH. (See line 13 in the user program.)

  7. The bless function returns a reference to the object that is created.

  8. The FETCH method is an access method that will retrieve a value from the hash object.

  9. Each time the user uses the print function to display a value from the hash, the FETCH method is automatically called and this line will be printed.

  10. A value from the hash is returned.

  11. The STORE method is an access method that will be called automatically when the user attempts to assign a value to one of the keys in the hash.

  12. The value to be assigned to the hash came from the argument list passed to the STORE method.

  13. This is the user/driver program. The House module is loaded into the program.

  14. The tie function calls the TIEHASH constructor in House.pm. The hash %home is tied to an object in the House class. The name of the class, House, and three additional arguments are passed.

  15. This line causes the FETCH access method to be called, which will display the value for the hash key Color.

  16. This line causes the FETCH access method to be called, which will display the value for the hash key Rooms.

  17. This line causes the FETCH access method to be called, which will display the value for the hash key Price.

  18. This line causes the STORE access method to be called automatically when a value is assigned to one of the hash keys, in this case the key Color.

  19. The print function causes the FETCH method to be called automatically to display a value for a specified hash key, Color.

  20. The untie function disassociates the hash from the object to which it was tied.

 

Example 15.4.

    # File is House.pm
1   package House;
2   sub TIEHASH  {
        my $class = shift;
        print "I'm the constructor in package $class\n";
        my $houseref = {};
        bless $houseref, $class;
    }
3   sub FETCH {
        my $self=shift;
        my $key=shift;
        return $self->{$key};
    }
4   sub STORE {
        my $self = shift;
        my $key = shift;
        my $value = shift;
        $self->{$key}=$value;
    }

5   sub FIRSTKEY {
        my $self = shift;
6       my $tmp = scalar keys %{$self};
7       return each  %{$self};
    }
8   sub NEXTKEY {
        $self=shift;
        each %{$self};
    }
    1;

 --------------------------------------------------------------------

     #!/usr/bin/perl
     # File is mainfile
9   use House;
10  tie %home, "House";
    $home{"Price"} = 55000;  # Assign and Store the data
    $home{"Rooms"} = 11;
    # Fetch the data
    print "The number of rooms in the house: $home{Rooms}\n";
    print "The price of the house is: $home{Price}\n";
11  foreach $key (keys(%home)){
12      print "Key is $key\n";
    }
13  while(  ($key, $value) = each(%home)){
            # Calls to FIRSTKEY and NEXTKEY
14      print "Key=$key, Value=$value\n";
    }
15  untie(%home);

(Output)
I'm the constructor in package House
The number of rooms in the house: 11
The price of the house is: 55000
Key is Rooms
Key is Price
Key=Rooms, Value=11
Key=Price, Value=55000

					  

Explanation

  1. The package/class House is declared. The file is House.pm.

  2. The constructor TIEHASH will tie a hash to an object.

  3. The FETCH method is an access method that will retrieve a value from the hash object.

  4. The STORE method is an access method that will assign a value to the hash object.

  5. The FIRSTKEY method is called automatically if the user program calls one of Perl's built-in hash functions: keys, value, or each.

  6. By calling keys in a scalar context, Perl resets the internal state of the hash in order to guarantee that the next time each is called, it will be given the first key.

  7. The each function returns the first key/value pair.

  8. The NEXTKEY method knows what the previous key was (PREVKEY) and starts on the next one as the hash is being iterated through a loop in the user program.

  9. This is the user/driver program. The House module is loaded into the program.

  10. The tie function calls the TIEHASH constructor in House.pm. The hash %home is tied to an object in the House class.

  11. The while loop is used to iterate through the hash with the keys function. The first time in the loop, the FIRSTKEY method is automatically called.

  12. The value for each key is printed. This value is returned from the access methods FIRSTKEY and NEXTKEY.

  13. The while loop is used to iterate through the hash with the each function. The first time in the loop, the FIRSTKEY method is automatically called.

  14. Each key and value is printed. These values are returned from the access methods FIRSTKEY and NEXTKEY.

 

Previous Page Next Page