Binding Variables to Objects
This chapter is dedicated to describing how the tie() function works in Perl. The tie() function enables you to create variables that are tied to specific methods called when a variable is written to or read from. Using the tie() function, you can eliminate the need for calling methods designed specifically for certain types of variables.
The tie() function is used to bind a variable to an object class. Here's the syntax:
The tie() function binds a variable to a class that then provides the methods for the variable. The $variable has to be set to the name of the variable to be tied. The $classname is the name of the class implementing objects to which you want the variable tied. The @list is the list of arguments that may be required for this variable's class methods.
The object returned by the tie() function is just like the new() function for an object. You can use the returned value from the tie() function to access other methods in the class you just tied the object to.
Once you tie a variable to a class, the class's behavior is reflected in the way you access the variable. Each of the methods for the type of object is called when the type of action for the variable is seen by Perl. Three types of objects can be tied to classes: scalars, arrays, and associative arrays.
It's often necessary to disassociate an object from a class. This is done with the use of the untie() function. Simply call the untie($object) function and you're done. The untie() function works whether you are tied to a scalar, array, or hash object. The next three sections illustrate how to use tie() on each of these objects.
A class implementing a scalar object that can be tied to must implement these four methods:
Think of these methods as events for the class. When a variable is first tied to a class, the TIESCALAR method is called. Every time the tied variable is read from, the value from the FETch method is returned. When the tied variable is assigned to, the STORE method is called. Finally, when the tied variable loses scope, the DESTROY method is called.
Listing 6.1 contains a script that uses the tie() function for the Knot class.
Listing 6.1. Using the tie() function.
Let's examine the code in Listing 6.1 to see where the methods for Knot.pm are invoked. In line 3, the address of the current directory is added to search for included modules. In line 4, the Knot.pm module is requested with the use statement. The Knot.pm file contains the module code for the class that allows variables to be tied.
In line 10, the variable $currentTime is tied to the class Knot. The TIESCALAR function in Knot.pm is called at this point. There are no additional arguments to be passed to the TIESCALAR function, so only two parameters, the variable number and the class name, are sent.
In line 15, the $currentTime variable is read from and the value of $currentTime is assigned to $x. Instead of treating $currentTime as a normal variable, Perl uses the FETch method of the tied class. The FETch method returns the current date in this example. You can write your own function. In line 17, the $currentTime variable is accessed again. This time, the FETch method is called again.
The program must not attempt to assign a value to the $currentTime variable. See line 26 in Listing 6.1. The Knot.pm module is implemented to allow only read-only variables; therefore, the FETch function will print an error message when the code at line 26 is executed.
Finally, the DESTROY method is called when the $currentTime variable is destroyed. The destruction is done automatically by Perl when the $currentTime variable goes out of scope. In this example, the DESTROY method simply prints an error message.
Here is the output from Listing 6.1.
x= Sat Jun 1 12:54:25 CDT 1996
Now let's look at the Knot.pm file in Listing 6.2.
Listing 6.2. The Knot.pm file.
Knot.pm defines the package in line 10 and imports the Carp and strict packages in lines 11 and 12, respectively. Line 74 terminates the module.
Lines 19 through 26 define the TIESCALAR function. The TIESCALAR function behaves a lot like the constructor of an object in Perl. It creates an associative array and returns a reference to this array after a call to the bless() function. (See Chapter 5, "Object-Oriented Programming in Perl," for more information on bless-ing objects.)
The FETch method starts at line 35. The FETch method is called every time the tied variable is read from. The only argument to the FETch method is a reference to the object itself. At line 37, the class type is confirmed, although it's not absolutely necessary to do this. Lines 39 through 41 return the current date and time for the value of the tied variable.
The STORE method is defined from line 50 through 56. In this case, we do not let values of the arguments that are passed in be assigned to anything because we want to make this value a read-only variable. You can easily modify this function to take some other action than what's shown in this example. The FETch method takes only two arguments: a reference to itself and a value that is being assigned. The confess() call is from within the Carp module.
The DESTROY method is called when the tied variable is destroyed. Normally, this function is empty. For this example, there is a print statement to show where the DESTROY function is called.
An array variable can be tied to a class in the same manner as a scalar can be tied to a class. The real difference is that the input parameters to the methods now need an index used to address a value in array. A class implementing an ordinary array must have these methods:
The FETch, DESTROY, and STORE methods have the same names as those for scalars. However, the name of the constructor is different-it's called TIEARRAY. Let's define a new array type called Cuboid, which has its first five indexes provide special functions. The first three indexes are written to as the height, width, and depth of a cuboid. The next two indexes contain the volume and surface area of the cuboid and are made read-only. The rest of the array can be made into a bounded array to allow a user to store his or her own values. As soon as a value is stored in the Cuboid array, the values of items at index 3 and 4 are recalculated to provide the latest volume and surface area of a cuboid.
Listing 6.3 illustrates how to use this array.
Listing 6.3. Using the Cuboid.pm module.
Here is the output of this code.
array will be 8 elements long
Now let's examine the Cuboid.pm module, which is presented in Listing 6.4.
Listing 6.4. The Cuboid.pm module.
1 # ------------------------------------------------------------
The Cuboid package is started at line 2 and ends at line 83 with a required statement. The package uses Carp and strict packages at lines 3 and 4, respectively. Lines 9 through 14 describe the structure of this object. The size of the sacrosanct indexes is set at line 17.
The TIEARRAY constructor starts at line 19. Note how the constructor takes two parameters: one for the class and one for the maximum size the array can take. Line 27 contains some error-checking routines using the confess() function in the Carp module. The maximum size is adjusted for the sacrosanct indexes, and an appropriate message is printed out at line 30.
A reference to the newly created object is returned in lines 31 through 34. Note how the array is initialized and the member MAXSIZE set at line 33.
The FETch method for the array behaves in the same manner as for a scalar. The FETch method is called every time an individual element in the tied array is read from. The FETch method takes two arguments: a reference to itself and the index whose value is being fetched. Look at line 43 where these two values are assigned to $self and $ndx. Bounds are checked at line 44, and we bail out in case of an error at line 45. If the bounds are correct, the value is returned at the request index via code at line 47.
The STORE method starts at line 50 and takes three arguments: a reference to itself, the index at which to store, and the value to store. In the STORE method, the extracted values are printed at lines 58 and 59. Bounds checking is done at line 60 with a bailout at line 61 in case of errors. Lines 63 through 65 do not permit storing values at index 3 or 4.
At line 66 the input value is assigned. At this point, you could make the code faster by checking whether only indexes 0 to 1 are modified, but you'll need to do that on your own. The area and volume results are stored at index 3 and 4. The assigned value is returned in line 76.
The DESTROY method is just a dummy function that doesn't do much. You really don't need to have a DESTROY method. The one shown here is for illustration only.
An associative array is more complicated to implement than either a scalar or an array because of the extra functions that have to be added to it. A class that can be tied to an associative array should have the following methods:
The next example is a simpler version of the one presented by Tom Christiansen in his perltie.html file, which is available on www.perl.com. The example presented by Tom is a bit dangerous to use because you can wipe out your .profile, .history, .elm, .term, and other "dot" files. I wrote this example to map the text versions of the perltie.html file into a hashed array to generate a table of contents for this book based on the first line of each heading. The example presented here simply lists the first line of each heading. There is no reason why you cannot print other information in the file, however.
Listing 6.5 shows the script that uses the tie() function on a hash. The module used for mirroring the contents of each chapter file is called Mirror.pm. It's used in line 4 of this code.
Listing 6.5. Using the tie() function on a hash.
Here is sample input and output from the code in Listing 6.6.
What you see above is the first line of every file listed in the hash instead of the filename! Had we not tied our own functions to the %chapters hash, we would be seeing the names of the file, such as ch29.txt, ch12.txt, and so forth. Instead of these names, when each element in the %chapters hash is accessed, our own function is called and prints out the first line in every file.
Of course, in your code, you would probably be using different functions to implement your own strategy and functions with a hash. For example, each access to a hash is tied to a record in a file. The function behind the access to each element in the hash would somehow take the record and format it to suit your needs. The example shown here in Listing 6.6 is simple enough to modify to fit into your own particular application.
Let's now look at how the code for Mirror.pm is written. By examining this code, you will be able to see how each function is defined for each type of action that you can tie a function to.
Listing 6.6. The Mirror.pm file.
1 # This file is hereby put in the public domain. Copy freely.
The TIEHASH function definition begins at line 10. The constructor takes two values as arguments: the first is the name of the class, and the second is an optional directory to work in. If the second parameter is not specified, the current working directory is used. The $node hash is used to store two parameters: HOME for the working directory and LIST for the list of items in this hash.
At line 21, the required information is collected for the hash and is stored away in LIST at line 23. The debug statement at line 24 is a very valuable tool while debugging. At line 27, the class is blessed and the reference to the $node is returned.
The FETch method is called whenever an element in the tied hash is being read. It takes two arguments: a reference to itself and the key whose value is being asked for. See lines 37 and 38, where the two parameters are extracted. It would be prudent to add some error correction here lest we look at the wrong path-this is done at line 41. At line 45 the returned value as cached in the init stages is returned. Had a new item to the hash been added with an undefined value, the code at line 45 would assign it a value.
The assignment-handling function is the STORE function. This function is called whenever an element in the hash is written to. It takes three arguments: a reference to itself, the index at which to store, and the value to store. The three arguments are extracted at lines 60 through 62. The third argument may be null, in which case the head of the file in the HOME directory is used. (Look at lines 66 through 72.)
The DELETE function is called
when an item is deleted from the hash. The function is defined
at line 81. There are two arguments to this function: a reference
to the object and the index to remove. The delete()
function is called to remove the indexed item from the LIST
The CLEAR function is called when the whole hash is removed, possibly by assigning an empty list to it. (The tied array has no such callback!) There is only one argument to this function, and that is a reference to itself. The CLEAR function is set to call the DELETE function in this example (see line 95). This call saves some code, but we could have just as easily used the delete() function in the same manner as DELETE.
The EXISTS function is called to check whether an item exists in a hash (see line 103). There are two arguments to this function: a reference to the object and the index to remove. It simply re-creates the key and uses this key to return a value from within the LIST hash.
The FIRSTKEY and NEXTKEY methods are called when the each() and keys() methods are called. The FIRSTKEY method is called when you start to iterate a list. The NEXTKEY method gets called during a keys() or each() iteration. The first argument to NEXTKEY is a reference to the object itself. The second argument is the last that was accessed.
For the file in Listing 6.6, you should now be able to derive your own classes for mapping hashes to functions. The tie() function, when used with hashes, provides more flexibility in defining methods than what the tie() function for arrays provides. However, using the hash is more complex than the array function because you have to define more methods with the hashing method.
There is supposedly some relief, though, with the use of the TieHash module provided with the Perl distribution. The TieHash module has predefined methods for you to pick and choose whatever functions you want to implement, and the rest are defaulted. There is a man page for the module in the Perl distribution, but it did not provide much information on how to actually use the module. Perhaps we'll see more documentation on this tool in later releases.
This chapter has provided only some basic information on the use of the tie() function. There is an excellent document, called perltie.html, by Tom Christiansen that is available at most of the Perl archive sites. The perltie.html document has more detailed information on how to use the tie() functions. The tie() function is also used in modules distributed with Perl. Two interesting modules to look at are the Config and DBM file modules, which show interesting uses of the tie() function. The DBM file modules provide detailed examples of mapping records to disk with the use of tie() functions on hashes.
This chapter has provided the basic information on how to use the tie() function to provide an association between a Perl variable and executable functions. Scalars, arrays, and hashes can be associated with methods having special names such as FETch, STORE, and so on. By designing classes that provide methods these names, you can provide extra functionality to map scalar, array, or hash objects to other objects, processes, or disk files.
With any suggestions or questions please feel free to contact us