diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:43:11 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 19:43:11 +0000 |
commit | fc22b3d6507c6745911b9dfcc68f1e665ae13dbc (patch) | |
tree | ce1e3bce06471410239a6f41282e328770aa404a /upstream/debian-unstable/man1/perltie.1 | |
parent | Initial commit. (diff) | |
download | manpages-l10n-fc22b3d6507c6745911b9dfcc68f1e665ae13dbc.tar.xz manpages-l10n-fc22b3d6507c6745911b9dfcc68f1e665ae13dbc.zip |
Adding upstream version 4.22.0.upstream/4.22.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'upstream/debian-unstable/man1/perltie.1')
-rw-r--r-- | upstream/debian-unstable/man1/perltie.1 | 1361 |
1 files changed, 1361 insertions, 0 deletions
diff --git a/upstream/debian-unstable/man1/perltie.1 b/upstream/debian-unstable/man1/perltie.1 new file mode 100644 index 00000000..b0af3168 --- /dev/null +++ b/upstream/debian-unstable/man1/perltie.1 @@ -0,0 +1,1361 @@ +.\" -*- mode: troff; coding: utf-8 -*- +.\" Automatically generated by Pod::Man 5.01 (Pod::Simple 3.43) +.\" +.\" Standard preamble: +.\" ======================================================================== +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" \*(C` and \*(C' are quotes in nroff, nothing in troff, for use with C<>. +.ie n \{\ +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds C` +. ds C' +'br\} +.\" +.\" Escape single quotes in literal strings from groff's Unicode transform. +.ie \n(.g .ds Aq \(aq +.el .ds Aq ' +.\" +.\" If the F register is >0, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.\" +.\" Avoid warning from groff about undefined register 'F'. +.de IX +.. +.nr rF 0 +.if \n(.g .if rF .nr rF 1 +.if (\n(rF:(\n(.g==0)) \{\ +. if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. if !\nF==2 \{\ +. nr % 0 +. nr F 2 +. \} +. \} +.\} +.rr rF +.\" ======================================================================== +.\" +.IX Title "PERLTIE 1" +.TH PERLTIE 1 2024-01-12 "perl v5.38.2" "Perl Programmers Reference Guide" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.if n .ad l +.nh +.SH NAME +perltie \- how to hide an object class in a simple variable +.IX Xref "tie" +.SH SYNOPSIS +.IX Header "SYNOPSIS" +.Vb 1 +\& tie VARIABLE, CLASSNAME, LIST +\& +\& $object = tied VARIABLE +\& +\& untie VARIABLE +.Ve +.SH DESCRIPTION +.IX Header "DESCRIPTION" +Prior to release 5.0 of Perl, a programmer could use \fBdbmopen()\fR +to connect an on-disk database in the standard Unix \fBdbm\fR\|(3x) +format magically to a \f(CW%HASH\fR in their program. However, their Perl was either +built with one particular dbm library or another, but not both, and +you couldn't extend this mechanism to other packages or types of variables. +.PP +Now you can. +.PP +The \fBtie()\fR function binds a variable to a class (package) that will provide +the implementation for access methods for that variable. Once this magic +has been performed, accessing a tied variable automatically triggers +method calls in the proper class. The complexity of the class is +hidden behind magic methods calls. The method names are in ALL CAPS, +which is a convention that Perl uses to indicate that they're called +implicitly rather than explicitly\-\-just like the \fBBEGIN()\fR and \fBEND()\fR +functions. +.PP +In the \fBtie()\fR call, \f(CW\*(C`VARIABLE\*(C'\fR is the name of the variable to be +enchanted. \f(CW\*(C`CLASSNAME\*(C'\fR is the name of a class implementing objects of +the correct type. Any additional arguments in the \f(CW\*(C`LIST\*(C'\fR are passed to +the appropriate constructor method for that class\-\-meaning \fBTIESCALAR()\fR, +\&\fBTIEARRAY()\fR, \fBTIEHASH()\fR, or \fBTIEHANDLE()\fR. (Typically these are arguments +such as might be passed to the \fBdbminit()\fR function of C.) The object +returned by the "new" method is also returned by the \fBtie()\fR function, +which would be useful if you wanted to access other methods in +\&\f(CW\*(C`CLASSNAME\*(C'\fR. (You don't actually have to return a reference to a right +"type" (e.g., HASH or \f(CW\*(C`CLASSNAME\*(C'\fR) so long as it's a properly blessed +object.) You can also retrieve a reference to the underlying object +using the \fBtied()\fR function. +.PP +Unlike \fBdbmopen()\fR, the \fBtie()\fR function will not \f(CW\*(C`use\*(C'\fR or \f(CW\*(C`require\*(C'\fR a module +for you\-\-you need to do that explicitly yourself. +.SS "Tying Scalars" +.IX Xref "scalar, tying" +.IX Subsection "Tying Scalars" +A class implementing a tied scalar should define the following methods: +TIESCALAR, FETCH, STORE, and possibly UNTIE and/or DESTROY. +.PP +Let's look at each in turn, using as an example a tie class for +scalars that allows the user to do something like: +.PP +.Vb 2 +\& tie $his_speed, \*(AqNice\*(Aq, getppid(); +\& tie $my_speed, \*(AqNice\*(Aq, $$; +.Ve +.PP +And now whenever either of those variables is accessed, its current +system priority is retrieved and returned. If those variables are set, +then the process's priority is changed! +.PP +We'll use Jarkko Hietaniemi <\fIjhi@iki.fi\fR>'s BSD::Resource class (not +included) to access the PRIO_PROCESS, PRIO_MIN, and PRIO_MAX constants +from your system, as well as the \fBgetpriority()\fR and \fBsetpriority()\fR system +calls. Here's the preamble of the class. +.PP +.Vb 5 +\& package Nice; +\& use Carp; +\& use BSD::Resource; +\& use strict; +\& $Nice::DEBUG = 0 unless defined $Nice::DEBUG; +.Ve +.IP "TIESCALAR classname, LIST" 4 +.IX Xref "TIESCALAR" +.IX Item "TIESCALAR classname, LIST" +This is the constructor for the class. That means it is +expected to return a blessed reference to a new scalar +(probably anonymous) that it's creating. For example: +.Sp +.Vb 3 +\& sub TIESCALAR { +\& my $class = shift; +\& my $pid = shift || $$; # 0 means me +\& +\& if ($pid !~ /^\ed+$/) { +\& carp "Nice::Tie::Scalar got non\-numeric pid $pid" if $^W; +\& return undef; +\& } +\& +\& unless (kill 0, $pid) { # EPERM or ERSCH, no doubt +\& carp "Nice::Tie::Scalar got bad pid $pid: $!" if $^W; +\& return undef; +\& } +\& +\& return bless \e$pid, $class; +\& } +.Ve +.Sp +This tie class has chosen to return an error rather than raising an +exception if its constructor should fail. While this is how \fBdbmopen()\fR works, +other classes may well not wish to be so forgiving. It checks the global +variable \f(CW$^W\fR to see whether to emit a bit of noise anyway. +.IP "FETCH this" 4 +.IX Xref "FETCH" +.IX Item "FETCH this" +This method will be triggered every time the tied variable is accessed +(read). It takes no arguments beyond its self reference, which is the +object representing the scalar we're dealing with. Because in this case +we're using just a SCALAR ref for the tied scalar object, a simple $$self +allows the method to get at the real value stored there. In our example +below, that real value is the process ID to which we've tied our variable. +.Sp +.Vb 10 +\& sub FETCH { +\& my $self = shift; +\& confess "wrong type" unless ref $self; +\& croak "usage error" if @_; +\& my $nicety; +\& local($!) = 0; +\& $nicety = getpriority(PRIO_PROCESS, $$self); +\& if ($!) { croak "getpriority failed: $!" } +\& return $nicety; +\& } +.Ve +.Sp +This time we've decided to blow up (raise an exception) if the renice +fails\-\-there's no place for us to return an error otherwise, and it's +probably the right thing to do. +.IP "STORE this, value" 4 +.IX Xref "STORE" +.IX Item "STORE this, value" +This method will be triggered every time the tied variable is set +(assigned). Beyond its self reference, it also expects one (and only one) +argument: the new value the user is trying to assign. Don't worry about +returning a value from STORE; the semantic of assignment returning the +assigned value is implemented with FETCH. +.Sp +.Vb 5 +\& sub STORE { +\& my $self = shift; +\& confess "wrong type" unless ref $self; +\& my $new_nicety = shift; +\& croak "usage error" if @_; +\& +\& if ($new_nicety < PRIO_MIN) { +\& carp sprintf +\& "WARNING: priority %d less than minimum system priority %d", +\& $new_nicety, PRIO_MIN if $^W; +\& $new_nicety = PRIO_MIN; +\& } +\& +\& if ($new_nicety > PRIO_MAX) { +\& carp sprintf +\& "WARNING: priority %d greater than maximum system priority %d", +\& $new_nicety, PRIO_MAX if $^W; +\& $new_nicety = PRIO_MAX; +\& } +\& +\& unless (defined setpriority(PRIO_PROCESS, +\& $$self, +\& $new_nicety)) +\& { +\& confess "setpriority failed: $!"; +\& } +\& } +.Ve +.IP "UNTIE this" 4 +.IX Xref "UNTIE" +.IX Item "UNTIE this" +This method will be triggered when the \f(CW\*(C`untie\*(C'\fR occurs. This can be useful +if the class needs to know when no further calls will be made. (Except DESTROY +of course.) See "The \f(CW\*(C`untie\*(C'\fR Gotcha" below for more details. +.IP "DESTROY this" 4 +.IX Xref "DESTROY" +.IX Item "DESTROY this" +This method will be triggered when the tied variable needs to be destructed. +As with other object classes, such a method is seldom necessary, because Perl +deallocates its moribund object's memory for you automatically\-\-this isn't +C++, you know. We'll use a DESTROY method here for debugging purposes only. +.Sp +.Vb 5 +\& sub DESTROY { +\& my $self = shift; +\& confess "wrong type" unless ref $self; +\& carp "[ Nice::DESTROY pid $$self ]" if $Nice::DEBUG; +\& } +.Ve +.PP +That's about all there is to it. Actually, it's more than all there +is to it, because we've done a few nice things here for the sake +of completeness, robustness, and general aesthetics. Simpler +TIESCALAR classes are certainly possible. +.SS "Tying Arrays" +.IX Xref "array, tying" +.IX Subsection "Tying Arrays" +A class implementing a tied ordinary array should define the following +methods: TIEARRAY, FETCH, STORE, FETCHSIZE, STORESIZE, CLEAR +and perhaps UNTIE and/or DESTROY. +.PP +FETCHSIZE and STORESIZE are used to provide \f(CW$#array\fR and +equivalent \f(CWscalar(@array)\fR access. +.PP +The methods POP, PUSH, SHIFT, UNSHIFT, SPLICE, DELETE, and EXISTS are +required if the perl operator with the corresponding (but lowercase) name +is to operate on the tied array. The \fBTie::Array\fR class can be used as a +base class to implement the first five of these in terms of the basic +methods above. The default implementations of DELETE and EXISTS in +\&\fBTie::Array\fR simply \f(CW\*(C`croak\*(C'\fR. +.PP +In addition EXTEND will be called when perl would have pre-extended +allocation in a real array. +.PP +For this discussion, we'll implement an array whose elements are a fixed +size at creation. If you try to create an element larger than the fixed +size, you'll take an exception. For example: +.PP +.Vb 4 +\& use FixedElem_Array; +\& tie @array, \*(AqFixedElem_Array\*(Aq, 3; +\& $array[0] = \*(Aqcat\*(Aq; # ok. +\& $array[1] = \*(Aqdogs\*(Aq; # exception, length(\*(Aqdogs\*(Aq) > 3. +.Ve +.PP +The preamble code for the class is as follows: +.PP +.Vb 3 +\& package FixedElem_Array; +\& use Carp; +\& use strict; +.Ve +.IP "TIEARRAY classname, LIST" 4 +.IX Xref "TIEARRAY" +.IX Item "TIEARRAY classname, LIST" +This is the constructor for the class. That means it is expected to +return a blessed reference through which the new array (probably an +anonymous ARRAY ref) will be accessed. +.Sp +In our example, just to show you that you don't \fIreally\fR have to return an +ARRAY reference, we'll choose a HASH reference to represent our object. +A HASH works out well as a generic record type: the \f(CW\*(C`{ELEMSIZE}\*(C'\fR field will +store the maximum element size allowed, and the \f(CW\*(C`{ARRAY}\*(C'\fR field will hold the +true ARRAY ref. If someone outside the class tries to dereference the +object returned (doubtless thinking it an ARRAY ref), they'll blow up. +This just goes to show you that you should respect an object's privacy. +.Sp +.Vb 11 +\& sub TIEARRAY { +\& my $class = shift; +\& my $elemsize = shift; +\& if ( @_ || $elemsize =~ /\eD/ ) { +\& croak "usage: tie ARRAY, \*(Aq" . _\|_PACKAGE_\|_ . "\*(Aq, elem_size"; +\& } +\& return bless { +\& ELEMSIZE => $elemsize, +\& ARRAY => [], +\& }, $class; +\& } +.Ve +.IP "FETCH this, index" 4 +.IX Xref "FETCH" +.IX Item "FETCH this, index" +This method will be triggered every time an individual element the tied array +is accessed (read). It takes one argument beyond its self reference: the +index whose value we're trying to fetch. +.Sp +.Vb 5 +\& sub FETCH { +\& my $self = shift; +\& my $index = shift; +\& return $self\->{ARRAY}\->[$index]; +\& } +.Ve +.Sp +If a negative array index is used to read from an array, the index +will be translated to a positive one internally by calling FETCHSIZE +before being passed to FETCH. You may disable this feature by +assigning a true value to the variable \f(CW$NEGATIVE_INDICES\fR in the +tied array class. +.Sp +As you may have noticed, the name of the FETCH method (et al.) is the same +for all accesses, even though the constructors differ in names (TIESCALAR +vs TIEARRAY). While in theory you could have the same class servicing +several tied types, in practice this becomes cumbersome, and it's easiest +to keep them at simply one tie type per class. +.IP "STORE this, index, value" 4 +.IX Xref "STORE" +.IX Item "STORE this, index, value" +This method will be triggered every time an element in the tied array is set +(written). It takes two arguments beyond its self reference: the index at +which we're trying to store something and the value we're trying to put +there. +.Sp +In our example, \f(CW\*(C`undef\*(C'\fR is really \f(CW\*(C`$self\->{ELEMSIZE}\*(C'\fR number of +spaces so we have a little more work to do here: +.Sp +.Vb 11 +\& sub STORE { +\& my $self = shift; +\& my( $index, $value ) = @_; +\& if ( length $value > $self\->{ELEMSIZE} ) { +\& croak "length of $value is greater than $self\->{ELEMSIZE}"; +\& } +\& # fill in the blanks +\& $self\->STORESIZE( $index ) if $index > $self\->FETCHSIZE(); +\& # right justify to keep element size for smaller elements +\& $self\->{ARRAY}\->[$index] = sprintf "%$self\->{ELEMSIZE}s", $value; +\& } +.Ve +.Sp +Negative indexes are treated the same as with FETCH. +.IP "FETCHSIZE this" 4 +.IX Xref "FETCHSIZE" +.IX Item "FETCHSIZE this" +Returns the total number of items in the tied array associated with +object \fIthis\fR. (Equivalent to \f(CWscalar(@array)\fR). For example: +.Sp +.Vb 4 +\& sub FETCHSIZE { +\& my $self = shift; +\& return scalar $self\->{ARRAY}\->@*; +\& } +.Ve +.IP "STORESIZE this, count" 4 +.IX Xref "STORESIZE" +.IX Item "STORESIZE this, count" +Sets the total number of items in the tied array associated with +object \fIthis\fR to be \fIcount\fR. If this makes the array larger then +class's mapping of \f(CW\*(C`undef\*(C'\fR should be returned for new positions. +If the array becomes smaller then entries beyond count should be +deleted. +.Sp +In our example, 'undef' is really an element containing +\&\f(CW\*(C`$self\->{ELEMSIZE}\*(C'\fR number of spaces. Observe: +.Sp +.Vb 10 +\& sub STORESIZE { +\& my $self = shift; +\& my $count = shift; +\& if ( $count > $self\->FETCHSIZE() ) { +\& foreach ( $count \- $self\->FETCHSIZE() .. $count ) { +\& $self\->STORE( $_, \*(Aq\*(Aq ); +\& } +\& } elsif ( $count < $self\->FETCHSIZE() ) { +\& foreach ( 0 .. $self\->FETCHSIZE() \- $count \- 2 ) { +\& $self\->POP(); +\& } +\& } +\& } +.Ve +.IP "EXTEND this, count" 4 +.IX Xref "EXTEND" +.IX Item "EXTEND this, count" +Informative call that array is likely to grow to have \fIcount\fR entries. +Can be used to optimize allocation. This method need do nothing. +.Sp +In our example there is no reason to implement this method, so we leave +it as a no-op. This method is only relevant to tied array implementations +where there is the possibility of having the allocated size of the array +be larger than is visible to a perl programmer inspecting the size of the +array. Many tied array implementations will have no reason to implement it. +.Sp +.Vb 5 +\& sub EXTEND { +\& my $self = shift; +\& my $count = shift; +\& # nothing to see here, move along. +\& } +.Ve +.Sp +\&\fBNOTE:\fR It is generally an error to make this equivalent to STORESIZE. +Perl may from time to time call EXTEND without wanting to actually change +the array size directly. Any tied array should function correctly if this +method is a no-op, even if perhaps they might not be as efficient as they +would if this method was implemented. +.IP "EXISTS this, key" 4 +.IX Xref "EXISTS" +.IX Item "EXISTS this, key" +Verify that the element at index \fIkey\fR exists in the tied array \fIthis\fR. +.Sp +In our example, we will determine that if an element consists of +\&\f(CW\*(C`$self\->{ELEMSIZE}\*(C'\fR spaces only, it does not exist: +.Sp +.Vb 7 +\& sub EXISTS { +\& my $self = shift; +\& my $index = shift; +\& return 0 if ! defined $self\->{ARRAY}\->[$index] || +\& $self\->{ARRAY}\->[$index] eq \*(Aq \*(Aq x $self\->{ELEMSIZE}; +\& return 1; +\& } +.Ve +.IP "DELETE this, key" 4 +.IX Xref "DELETE" +.IX Item "DELETE this, key" +Delete the element at index \fIkey\fR from the tied array \fIthis\fR. +.Sp +In our example, a deleted item is \f(CW\*(C`$self\->{ELEMSIZE}\*(C'\fR spaces: +.Sp +.Vb 5 +\& sub DELETE { +\& my $self = shift; +\& my $index = shift; +\& return $self\->STORE( $index, \*(Aq\*(Aq ); +\& } +.Ve +.IP "CLEAR this" 4 +.IX Xref "CLEAR" +.IX Item "CLEAR this" +Clear (remove, delete, ...) all values from the tied array associated with +object \fIthis\fR. For example: +.Sp +.Vb 4 +\& sub CLEAR { +\& my $self = shift; +\& return $self\->{ARRAY} = []; +\& } +.Ve +.IP "PUSH this, LIST" 4 +.IX Xref "PUSH" +.IX Item "PUSH this, LIST" +Append elements of \fILIST\fR to the array. For example: +.Sp +.Vb 7 +\& sub PUSH { +\& my $self = shift; +\& my @list = @_; +\& my $last = $self\->FETCHSIZE(); +\& $self\->STORE( $last + $_, $list[$_] ) foreach 0 .. $#list; +\& return $self\->FETCHSIZE(); +\& } +.Ve +.IP "POP this" 4 +.IX Xref "POP" +.IX Item "POP this" +Remove last element of the array and return it. For example: +.Sp +.Vb 4 +\& sub POP { +\& my $self = shift; +\& return pop $self\->{ARRAY}\->@*; +\& } +.Ve +.IP "SHIFT this" 4 +.IX Xref "SHIFT" +.IX Item "SHIFT this" +Remove the first element of the array (shifting other elements down) +and return it. For example: +.Sp +.Vb 4 +\& sub SHIFT { +\& my $self = shift; +\& return shift $self\->{ARRAY}\->@*; +\& } +.Ve +.IP "UNSHIFT this, LIST" 4 +.IX Xref "UNSHIFT" +.IX Item "UNSHIFT this, LIST" +Insert LIST elements at the beginning of the array, moving existing elements +up to make room. For example: +.Sp +.Vb 9 +\& sub UNSHIFT { +\& my $self = shift; +\& my @list = @_; +\& my $size = scalar( @list ); +\& # make room for our list +\& $self\->{ARRAY}[ $size .. $self\->{ARRAY}\->$#* + $size ]\->@* +\& = $self\->{ARRAY}\->@* +\& $self\->STORE( $_, $list[$_] ) foreach 0 .. $#list; +\& } +.Ve +.IP "SPLICE this, offset, length, LIST" 4 +.IX Xref "SPLICE" +.IX Item "SPLICE this, offset, length, LIST" +Perform the equivalent of \f(CW\*(C`splice\*(C'\fR on the array. +.Sp +\&\fIoffset\fR is optional and defaults to zero, negative values count back +from the end of the array. +.Sp +\&\fIlength\fR is optional and defaults to rest of the array. +.Sp +\&\fILIST\fR may be empty. +.Sp +Returns a list of the original \fIlength\fR elements at \fIoffset\fR. +.Sp +In our example, we'll use a little shortcut if there is a \fILIST\fR: +.Sp +.Vb 11 +\& sub SPLICE { +\& my $self = shift; +\& my $offset = shift || 0; +\& my $length = shift || $self\->FETCHSIZE() \- $offset; +\& my @list = (); +\& if ( @_ ) { +\& tie @list, _\|_PACKAGE_\|_, $self\->{ELEMSIZE}; +\& @list = @_; +\& } +\& return splice $self\->{ARRAY}\->@*, $offset, $length, @list; +\& } +.Ve +.IP "UNTIE this" 4 +.IX Xref "UNTIE" +.IX Item "UNTIE this" +Will be called when \f(CW\*(C`untie\*(C'\fR happens. (See "The \f(CW\*(C`untie\*(C'\fR Gotcha" below.) +.IP "DESTROY this" 4 +.IX Xref "DESTROY" +.IX Item "DESTROY this" +This method will be triggered when the tied variable needs to be destructed. +As with the scalar tie class, this is almost never needed in a +language that does its own garbage collection, so this time we'll +just leave it out. +.SS "Tying Hashes" +.IX Xref "hash, tying" +.IX Subsection "Tying Hashes" +Hashes were the first Perl data type to be tied (see \fBdbmopen()\fR). A class +implementing a tied hash should define the following methods: TIEHASH is +the constructor. FETCH and STORE access the key and value pairs. EXISTS +reports whether a key is present in the hash, and DELETE deletes one. +CLEAR empties the hash by deleting all the key and value pairs. FIRSTKEY +and NEXTKEY implement the \fBkeys()\fR and \fBeach()\fR functions to iterate over all +the keys. SCALAR is triggered when the tied hash is evaluated in scalar +context, and in 5.28 onwards, by \f(CW\*(C`keys\*(C'\fR in boolean context. UNTIE is +called when \f(CW\*(C`untie\*(C'\fR happens, and DESTROY is called when the tied variable +is garbage collected. +.PP +If this seems like a lot, then feel free to inherit from merely the +standard Tie::StdHash module for most of your methods, redefining only the +interesting ones. See Tie::Hash for details. +.PP +Remember that Perl distinguishes between a key not existing in the hash, +and the key existing in the hash but having a corresponding value of +\&\f(CW\*(C`undef\*(C'\fR. The two possibilities can be tested with the \f(CWexists()\fR and +\&\f(CWdefined()\fR functions. +.PP +Here's an example of a somewhat interesting tied hash class: it gives you +a hash representing a particular user's dot files. You index into the hash +with the name of the file (minus the dot) and you get back that dot file's +contents. For example: +.PP +.Vb 8 +\& use DotFiles; +\& tie %dot, \*(AqDotFiles\*(Aq; +\& if ( $dot{profile} =~ /MANPATH/ || +\& $dot{login} =~ /MANPATH/ || +\& $dot{cshrc} =~ /MANPATH/ ) +\& { +\& print "you seem to set your MANPATH\en"; +\& } +.Ve +.PP +Or here's another sample of using our tied class: +.PP +.Vb 5 +\& tie %him, \*(AqDotFiles\*(Aq, \*(Aqdaemon\*(Aq; +\& foreach $f ( keys %him ) { +\& printf "daemon dot file %s is size %d\en", +\& $f, length $him{$f}; +\& } +.Ve +.PP +In our tied hash DotFiles example, we use a regular +hash for the object containing several important +fields, of which only the \f(CW\*(C`{LIST}\*(C'\fR field will be what the +user thinks of as the real hash. +.IP USER 5 +.IX Item "USER" +whose dot files this object represents +.IP HOME 5 +.IX Item "HOME" +where those dot files live +.IP CLOBBER 5 +.IX Item "CLOBBER" +whether we should try to change or remove those dot files +.IP LIST 5 +.IX Item "LIST" +the hash of dot file names and content mappings +.PP +Here's the start of \fIDotfiles.pm\fR: +.PP +.Vb 5 +\& package DotFiles; +\& use Carp; +\& sub whowasi { (caller(1))[3] . \*(Aq()\*(Aq } +\& my $DEBUG = 0; +\& sub debug { $DEBUG = @_ ? shift : 1 } +.Ve +.PP +For our example, we want to be able to emit debugging info to help in tracing +during development. We keep also one convenience function around +internally to help print out warnings; \fBwhowasi()\fR returns the function name +that calls it. +.PP +Here are the methods for the DotFiles tied hash. +.IP "TIEHASH classname, LIST" 4 +.IX Xref "TIEHASH" +.IX Item "TIEHASH classname, LIST" +This is the constructor for the class. That means it is expected to +return a blessed reference through which the new object (probably but not +necessarily an anonymous hash) will be accessed. +.Sp +Here's the constructor: +.Sp +.Vb 9 +\& sub TIEHASH { +\& my $class = shift; +\& my $user = shift || $>; +\& my $dotdir = shift || \*(Aq\*(Aq; +\& croak "usage: @{[&whowasi]} [USER [DOTDIR]]" if @_; +\& $user = getpwuid($user) if $user =~ /^\ed+$/; +\& my $dir = (getpwnam($user))[7] +\& || croak "@{[&whowasi]}: no user $user"; +\& $dir .= "/$dotdir" if $dotdir; +\& +\& my $node = { +\& USER => $user, +\& HOME => $dir, +\& LIST => {}, +\& CLOBBER => 0, +\& }; +\& +\& opendir(DIR, $dir) +\& || croak "@{[&whowasi]}: can\*(Aqt opendir $dir: $!"; +\& foreach $dot ( grep /^\e./ && \-f "$dir/$_", readdir(DIR)) { +\& $dot =~ s/^\e.//; +\& $node\->{LIST}{$dot} = undef; +\& } +\& closedir DIR; +\& return bless $node, $class; +\& } +.Ve +.Sp +It's probably worth mentioning that if you're going to filetest the +return values out of a readdir, you'd better prepend the directory +in question. Otherwise, because we didn't \fBchdir()\fR there, it would +have been testing the wrong file. +.IP "FETCH this, key" 4 +.IX Xref "FETCH" +.IX Item "FETCH this, key" +This method will be triggered every time an element in the tied hash is +accessed (read). It takes one argument beyond its self reference: the key +whose value we're trying to fetch. +.Sp +Here's the fetch for our DotFiles example. +.Sp +.Vb 6 +\& sub FETCH { +\& carp &whowasi if $DEBUG; +\& my $self = shift; +\& my $dot = shift; +\& my $dir = $self\->{HOME}; +\& my $file = "$dir/.$dot"; +\& +\& unless (exists $self\->{LIST}\->{$dot} || \-f $file) { +\& carp "@{[&whowasi]}: no $dot file" if $DEBUG; +\& return undef; +\& } +\& +\& if (defined $self\->{LIST}\->{$dot}) { +\& return $self\->{LIST}\->{$dot}; +\& } else { +\& return $self\->{LIST}\->{$dot} = \`cat $dir/.$dot\`; +\& } +\& } +.Ve +.Sp +It was easy to write by having it call the Unix \fBcat\fR\|(1) command, but it +would probably be more portable to open the file manually (and somewhat +more efficient). Of course, because dot files are a Unixy concept, we're +not that concerned. +.IP "STORE this, key, value" 4 +.IX Xref "STORE" +.IX Item "STORE this, key, value" +This method will be triggered every time an element in the tied hash is set +(written). It takes two arguments beyond its self reference: the index at +which we're trying to store something, and the value we're trying to put +there. +.Sp +Here in our DotFiles example, we'll be careful not to let +them try to overwrite the file unless they've called the \fBclobber()\fR +method on the original object reference returned by \fBtie()\fR. +.Sp +.Vb 7 +\& sub STORE { +\& carp &whowasi if $DEBUG; +\& my $self = shift; +\& my $dot = shift; +\& my $value = shift; +\& my $file = $self\->{HOME} . "/.$dot"; +\& my $user = $self\->{USER}; +\& +\& croak "@{[&whowasi]}: $file not clobberable" +\& unless $self\->{CLOBBER}; +\& +\& open(my $f, \*(Aq>\*(Aq, $file) || croak "can\*(Aqt open $file: $!"; +\& print $f $value; +\& close($f); +\& } +.Ve +.Sp +If they wanted to clobber something, they might say: +.Sp +.Vb 3 +\& $ob = tie %daemon_dots, \*(Aqdaemon\*(Aq; +\& $ob\->clobber(1); +\& $daemon_dots{signature} = "A true daemon\en"; +.Ve +.Sp +Another way to lay hands on a reference to the underlying object is to +use the \fBtied()\fR function, so they might alternately have set clobber +using: +.Sp +.Vb 2 +\& tie %daemon_dots, \*(Aqdaemon\*(Aq; +\& tied(%daemon_dots)\->clobber(1); +.Ve +.Sp +The clobber method is simply: +.Sp +.Vb 4 +\& sub clobber { +\& my $self = shift; +\& $self\->{CLOBBER} = @_ ? shift : 1; +\& } +.Ve +.IP "DELETE this, key" 4 +.IX Xref "DELETE" +.IX Item "DELETE this, key" +This method is triggered when we remove an element from the hash, +typically by using the \fBdelete()\fR function. Again, we'll +be careful to check whether they really want to clobber files. +.Sp +.Vb 2 +\& sub DELETE { +\& carp &whowasi if $DEBUG; +\& +\& my $self = shift; +\& my $dot = shift; +\& my $file = $self\->{HOME} . "/.$dot"; +\& croak "@{[&whowasi]}: won\*(Aqt remove file $file" +\& unless $self\->{CLOBBER}; +\& delete $self\->{LIST}\->{$dot}; +\& my $success = unlink($file); +\& carp "@{[&whowasi]}: can\*(Aqt unlink $file: $!" unless $success; +\& $success; +\& } +.Ve +.Sp +The value returned by DELETE becomes the return value of the call +to \fBdelete()\fR. If you want to emulate the normal behavior of \fBdelete()\fR, +you should return whatever FETCH would have returned for this key. +In this example, we have chosen instead to return a value which tells +the caller whether the file was successfully deleted. +.IP "CLEAR this" 4 +.IX Xref "CLEAR" +.IX Item "CLEAR this" +This method is triggered when the whole hash is to be cleared, usually by +assigning the empty list to it. +.Sp +In our example, that would remove all the user's dot files! It's such a +dangerous thing that they'll have to set CLOBBER to something higher than +1 to make it happen. +.Sp +.Vb 10 +\& sub CLEAR { +\& carp &whowasi if $DEBUG; +\& my $self = shift; +\& croak "@{[&whowasi]}: won\*(Aqt remove all dot files for $self\->{USER}" +\& unless $self\->{CLOBBER} > 1; +\& my $dot; +\& foreach $dot ( keys $self\->{LIST}\->%* ) { +\& $self\->DELETE($dot); +\& } +\& } +.Ve +.IP "EXISTS this, key" 4 +.IX Xref "EXISTS" +.IX Item "EXISTS this, key" +This method is triggered when the user uses the \fBexists()\fR function +on a particular hash. In our example, we'll look at the \f(CW\*(C`{LIST}\*(C'\fR +hash element for this: +.Sp +.Vb 6 +\& sub EXISTS { +\& carp &whowasi if $DEBUG; +\& my $self = shift; +\& my $dot = shift; +\& return exists $self\->{LIST}\->{$dot}; +\& } +.Ve +.IP "FIRSTKEY this" 4 +.IX Xref "FIRSTKEY" +.IX Item "FIRSTKEY this" +This method will be triggered when the user is going +to iterate through the hash, such as via a \fBkeys()\fR, \fBvalues()\fR, or \fBeach()\fR call. +.Sp +.Vb 6 +\& sub FIRSTKEY { +\& carp &whowasi if $DEBUG; +\& my $self = shift; +\& my $a = keys $self\->{LIST}\->%*; # reset each() iterator +\& each $self\->{LIST}\->%* +\& } +.Ve +.Sp +FIRSTKEY is always called in scalar context and it should just +return the first key. \fBvalues()\fR, and \fBeach()\fR in list context, +will call FETCH for the returned keys. +.IP "NEXTKEY this, lastkey" 4 +.IX Xref "NEXTKEY" +.IX Item "NEXTKEY this, lastkey" +This method gets triggered during a \fBkeys()\fR, \fBvalues()\fR, or \fBeach()\fR iteration. It has a +second argument which is the last key that had been accessed. This is +useful if you're caring about ordering or calling the iterator from more +than one sequence, or not really storing things in a hash anywhere. +.Sp +NEXTKEY is always called in scalar context and it should just +return the next key. \fBvalues()\fR, and \fBeach()\fR in list context, +will call FETCH for the returned keys. +.Sp +For our example, we're using a real hash so we'll do just the simple +thing, but we'll have to go through the LIST field indirectly. +.Sp +.Vb 5 +\& sub NEXTKEY { +\& carp &whowasi if $DEBUG; +\& my $self = shift; +\& return each $self\->{LIST}\->%* +\& } +.Ve +.Sp +If the object underlying your tied hash isn't a real hash and you don't have +\&\f(CW\*(C`each\*(C'\fR available, then you should return \f(CW\*(C`undef\*(C'\fR or the empty list once you've +reached the end of your list of keys. See \f(CW\*(C`each\*(Aqs own documentation\*(C'\fR +for more details. +.IP "SCALAR this" 4 +.IX Xref "SCALAR" +.IX Item "SCALAR this" +This is called when the hash is evaluated in scalar context, and in 5.28 +onwards, by \f(CW\*(C`keys\*(C'\fR in boolean context. In order to mimic the behaviour of +untied hashes, this method must return a value which when used as boolean, +indicates whether the tied hash is considered empty. If this method does +not exist, perl will make some educated guesses and return true when +the hash is inside an iteration. If this isn't the case, FIRSTKEY is +called, and the result will be a false value if FIRSTKEY returns the empty +list, true otherwise. +.Sp +However, you should \fBnot\fR blindly rely on perl always doing the right +thing. Particularly, perl will mistakenly return true when you clear the +hash by repeatedly calling DELETE until it is empty. You are therefore +advised to supply your own SCALAR method when you want to be absolutely +sure that your hash behaves nicely in scalar context. +.Sp +In our example we can just call \f(CW\*(C`scalar\*(C'\fR on the underlying hash +referenced by \f(CW\*(C`$self\->{LIST}\*(C'\fR: +.Sp +.Vb 5 +\& sub SCALAR { +\& carp &whowasi if $DEBUG; +\& my $self = shift; +\& return scalar $self\->{LIST}\->%* +\& } +.Ve +.Sp +NOTE: In perl 5.25 the behavior of scalar \f(CW%hash\fR on an untied hash changed +to return the count of keys. Prior to this it returned a string containing +information about the bucket setup of the hash. See +"bucket_ratio" in Hash::Util for a backwards compatibility path. +.IP "UNTIE this" 4 +.IX Xref "UNTIE" +.IX Item "UNTIE this" +This is called when \f(CW\*(C`untie\*(C'\fR occurs. See "The \f(CW\*(C`untie\*(C'\fR Gotcha" below. +.IP "DESTROY this" 4 +.IX Xref "DESTROY" +.IX Item "DESTROY this" +This method is triggered when a tied hash is about to go out of +scope. You don't really need it unless you're trying to add debugging +or have auxiliary state to clean up. Here's a very simple function: +.Sp +.Vb 3 +\& sub DESTROY { +\& carp &whowasi if $DEBUG; +\& } +.Ve +.PP +Note that functions such as \fBkeys()\fR and \fBvalues()\fR may return huge lists +when used on large objects, like DBM files. You may prefer to use the +\&\fBeach()\fR function to iterate over such. Example: +.PP +.Vb 7 +\& # print out history file offsets +\& use NDBM_File; +\& tie(%HIST, \*(AqNDBM_File\*(Aq, \*(Aq/usr/lib/news/history\*(Aq, 1, 0); +\& while (($key,$val) = each %HIST) { +\& print $key, \*(Aq = \*(Aq, unpack(\*(AqL\*(Aq,$val), "\en"; +\& } +\& untie(%HIST); +.Ve +.SS "Tying FileHandles" +.IX Xref "filehandle, tying" +.IX Subsection "Tying FileHandles" +This is partially implemented now. +.PP +A class implementing a tied filehandle should define the following +methods: TIEHANDLE, at least one of PRINT, PRINTF, WRITE, READLINE, GETC, +READ, and possibly CLOSE, UNTIE and DESTROY. The class can also provide: BINMODE, +OPEN, EOF, FILENO, SEEK, TELL \- if the corresponding perl operators are +used on the handle. +.PP +When STDERR is tied, its PRINT method will be called to issue warnings +and error messages. This feature is temporarily disabled during the call, +which means you can use \f(CWwarn()\fR inside PRINT without starting a recursive +loop. And just like \f(CW\*(C`_\|_WARN_\|_\*(C'\fR and \f(CW\*(C`_\|_DIE_\|_\*(C'\fR handlers, STDERR's PRINT +method may be called to report parser errors, so the caveats mentioned under +"%SIG" in perlvar apply. +.PP +All of this is especially useful when perl is embedded in some other +program, where output to STDOUT and STDERR may have to be redirected +in some special way. See nvi and the Apache module for examples. +.PP +When tying a handle, the first argument to \f(CW\*(C`tie\*(C'\fR should begin with an +asterisk. So, if you are tying STDOUT, use \f(CW*STDOUT\fR. If you have +assigned it to a scalar variable, say \f(CW$handle\fR, use \f(CW*$handle\fR. +\&\f(CW\*(C`tie $handle\*(C'\fR ties the scalar variable \f(CW$handle\fR, not the handle inside +it. +.PP +In our example we're going to create a shouting handle. +.PP +.Vb 1 +\& package Shout; +.Ve +.IP "TIEHANDLE classname, LIST" 4 +.IX Xref "TIEHANDLE" +.IX Item "TIEHANDLE classname, LIST" +This is the constructor for the class. That means it is expected to +return a blessed reference of some sort. The reference can be used to +hold some internal information. +.Sp +.Vb 1 +\& sub TIEHANDLE { print "<shout>\en"; my $i; bless \e$i, shift } +.Ve +.IP "WRITE this, LIST" 4 +.IX Xref "WRITE" +.IX Item "WRITE this, LIST" +This method will be called when the handle is written to via the +\&\f(CW\*(C`syswrite\*(C'\fR function. +.Sp +.Vb 5 +\& sub WRITE { +\& $r = shift; +\& my($buf,$len,$offset) = @_; +\& print "WRITE called, \e$buf=$buf, \e$len=$len, \e$offset=$offset"; +\& } +.Ve +.IP "PRINT this, LIST" 4 +.IX Xref "PRINT" +.IX Item "PRINT this, LIST" +This method will be triggered every time the tied handle is printed to +with the \f(CWprint()\fR or \f(CWsay()\fR functions. Beyond its self reference +it also expects the list that was passed to the print function. +.Sp +.Vb 1 +\& sub PRINT { $r = shift; $$r++; print join($,,map(uc($_),@_)),$\e } +.Ve +.Sp +\&\f(CWsay()\fR acts just like \f(CWprint()\fR except $\e will be localized to \f(CW\*(C`\en\*(C'\fR so +you need do nothing special to handle \f(CWsay()\fR in \f(CWPRINT()\fR. +.IP "PRINTF this, LIST" 4 +.IX Xref "PRINTF" +.IX Item "PRINTF this, LIST" +This method will be triggered every time the tied handle is printed to +with the \f(CWprintf()\fR function. +Beyond its self reference it also expects the format and list that was +passed to the printf function. +.Sp +.Vb 5 +\& sub PRINTF { +\& shift; +\& my $fmt = shift; +\& print sprintf($fmt, @_); +\& } +.Ve +.IP "READ this, LIST" 4 +.IX Xref "READ" +.IX Item "READ this, LIST" +This method will be called when the handle is read from via the \f(CW\*(C`read\*(C'\fR +or \f(CW\*(C`sysread\*(C'\fR functions. +.Sp +.Vb 8 +\& sub READ { +\& my $self = shift; +\& my $bufref = \e$_[0]; +\& my(undef,$len,$offset) = @_; +\& print "READ called, \e$buf=$bufref, \e$len=$len, \e$offset=$offset"; +\& # add to $$bufref, set $len to number of characters read +\& $len; +\& } +.Ve +.IP "READLINE this" 4 +.IX Xref "READLINE" +.IX Item "READLINE this" +This method is called when the handle is read via \f(CW\*(C`<HANDLE>\*(C'\fR +or \f(CW\*(C`readline HANDLE\*(C'\fR. +.Sp +As per \f(CW\*(C`readline\*(C'\fR, in scalar context it should return +the next line, or \f(CW\*(C`undef\*(C'\fR for no more data. In list context it should +return all remaining lines, or an empty list for no more data. The strings +returned should include the input record separator \f(CW$/\fR (see perlvar), +unless it is \f(CW\*(C`undef\*(C'\fR (which means "slurp" mode). +.Sp +.Vb 10 +\& sub READLINE { +\& my $r = shift; +\& if (wantarray) { +\& return ("all remaining\en", +\& "lines up\en", +\& "to eof\en"); +\& } else { +\& return "READLINE called " . ++$$r . " times\en"; +\& } +\& } +.Ve +.IP "GETC this" 4 +.IX Xref "GETC" +.IX Item "GETC this" +This method will be called when the \f(CW\*(C`getc\*(C'\fR function is called. +.Sp +.Vb 1 +\& sub GETC { print "Don\*(Aqt GETC, Get Perl"; return "a"; } +.Ve +.IP "EOF this" 4 +.IX Xref "EOF" +.IX Item "EOF this" +This method will be called when the \f(CW\*(C`eof\*(C'\fR function is called. +.Sp +Starting with Perl 5.12, an additional integer parameter will be passed. It +will be zero if \f(CW\*(C`eof\*(C'\fR is called without parameter; \f(CW1\fR if \f(CW\*(C`eof\*(C'\fR is given +a filehandle as a parameter, e.g. \f(CWeof(FH)\fR; and \f(CW2\fR in the very special +case that the tied filehandle is \f(CW\*(C`ARGV\*(C'\fR and \f(CW\*(C`eof\*(C'\fR is called with an empty +parameter list, e.g. \f(CWeof()\fR. +.Sp +.Vb 1 +\& sub EOF { not length $stringbuf } +.Ve +.IP "CLOSE this" 4 +.IX Xref "CLOSE" +.IX Item "CLOSE this" +This method will be called when the handle is closed via the \f(CW\*(C`close\*(C'\fR +function. +.Sp +.Vb 1 +\& sub CLOSE { print "CLOSE called.\en" } +.Ve +.IP "UNTIE this" 4 +.IX Xref "UNTIE" +.IX Item "UNTIE this" +As with the other types of ties, this method will be called when \f(CW\*(C`untie\*(C'\fR happens. +It may be appropriate to "auto CLOSE" when this occurs. See +"The \f(CW\*(C`untie\*(C'\fR Gotcha" below. +.IP "DESTROY this" 4 +.IX Xref "DESTROY" +.IX Item "DESTROY this" +As with the other types of ties, this method will be called when the +tied handle is about to be destroyed. This is useful for debugging and +possibly cleaning up. +.Sp +.Vb 1 +\& sub DESTROY { print "</shout>\en" } +.Ve +.PP +Here's how to use our little example: +.PP +.Vb 5 +\& tie(*FOO,\*(AqShout\*(Aq); +\& print FOO "hello\en"; +\& $a = 4; $b = 6; +\& print FOO $a, " plus ", $b, " equals ", $a + $b, "\en"; +\& print <FOO>; +.Ve +.SS "UNTIE this" +.IX Xref "UNTIE" +.IX Subsection "UNTIE this" +You can define for all tie types an UNTIE method that will be called +at \fBuntie()\fR. See "The \f(CW\*(C`untie\*(C'\fR Gotcha" below. +.ie n .SS "The ""untie"" Gotcha" +.el .SS "The \f(CWuntie\fP Gotcha" +.IX Xref "untie" +.IX Subsection "The untie Gotcha" +If you intend making use of the object returned from either \fBtie()\fR or +\&\fBtied()\fR, and if the tie's target class defines a destructor, there is a +subtle gotcha you \fImust\fR guard against. +.PP +As setup, consider this (admittedly rather contrived) example of a +tie; all it does is use a file to keep a log of the values assigned to +a scalar. +.PP +.Vb 1 +\& package Remember; +\& +\& use v5.36; +\& use IO::File; +\& +\& sub TIESCALAR { +\& my $class = shift; +\& my $filename = shift; +\& my $handle = IO::File\->new( "> $filename" ) +\& or die "Cannot open $filename: $!\en"; +\& +\& print $handle "The Start\en"; +\& bless {FH => $handle, Value => 0}, $class; +\& } +\& +\& sub FETCH { +\& my $self = shift; +\& return $self\->{Value}; +\& } +\& +\& sub STORE { +\& my $self = shift; +\& my $value = shift; +\& my $handle = $self\->{FH}; +\& print $handle "$value\en"; +\& $self\->{Value} = $value; +\& } +\& +\& sub DESTROY { +\& my $self = shift; +\& my $handle = $self\->{FH}; +\& print $handle "The End\en"; +\& close $handle; +\& } +\& +\& 1; +.Ve +.PP +Here is an example that makes use of this tie: +.PP +.Vb 2 +\& use strict; +\& use Remember; +\& +\& my $fred; +\& tie $fred, \*(AqRemember\*(Aq, \*(Aqmyfile.txt\*(Aq; +\& $fred = 1; +\& $fred = 4; +\& $fred = 5; +\& untie $fred; +\& system "cat myfile.txt"; +.Ve +.PP +This is the output when it is executed: +.PP +.Vb 5 +\& The Start +\& 1 +\& 4 +\& 5 +\& The End +.Ve +.PP +So far so good. Those of you who have been paying attention will have +spotted that the tied object hasn't been used so far. So lets add an +extra method to the Remember class to allow comments to be included in +the file; say, something like this: +.PP +.Vb 6 +\& sub comment { +\& my $self = shift; +\& my $text = shift; +\& my $handle = $self\->{FH}; +\& print $handle $text, "\en"; +\& } +.Ve +.PP +And here is the previous example modified to use the \f(CW\*(C`comment\*(C'\fR method +(which requires the tied object): +.PP +.Vb 2 +\& use strict; +\& use Remember; +\& +\& my ($fred, $x); +\& $x = tie $fred, \*(AqRemember\*(Aq, \*(Aqmyfile.txt\*(Aq; +\& $fred = 1; +\& $fred = 4; +\& comment $x "changing..."; +\& $fred = 5; +\& untie $fred; +\& system "cat myfile.txt"; +.Ve +.PP +When this code is executed there is no output. Here's why: +.PP +When a variable is tied, it is associated with the object which is the +return value of the TIESCALAR, TIEARRAY, or TIEHASH function. This +object normally has only one reference, namely, the implicit reference +from the tied variable. When \fBuntie()\fR is called, that reference is +destroyed. Then, as in the first example above, the object's +destructor (DESTROY) is called, which is normal for objects that have +no more valid references; and thus the file is closed. +.PP +In the second example, however, we have stored another reference to +the tied object in \f(CW$x\fR. That means that when \fBuntie()\fR gets called +there will still be a valid reference to the object in existence, so +the destructor is not called at that time, and thus the file is not +closed. The reason there is no output is because the file buffers +have not been flushed to disk. +.PP +Now that you know what the problem is, what can you do to avoid it? +Prior to the introduction of the optional UNTIE method the only way +was the good old \f(CW\*(C`\-w\*(C'\fR flag. Which will spot any instances where you call +\&\fBuntie()\fR and there are still valid references to the tied object. If +the second script above this near the top \f(CW\*(C`use warnings \*(Aquntie\*(Aq\*(C'\fR +or was run with the \f(CW\*(C`\-w\*(C'\fR flag, Perl prints this +warning message: +.PP +.Vb 1 +\& untie attempted while 1 inner references still exist +.Ve +.PP +To get the script to work properly and silence the warning make sure +there are no valid references to the tied object \fIbefore\fR \fBuntie()\fR is +called: +.PP +.Vb 2 +\& undef $x; +\& untie $fred; +.Ve +.PP +Now that UNTIE exists the class designer can decide which parts of the +class functionality are really associated with \f(CW\*(C`untie\*(C'\fR and which with +the object being destroyed. What makes sense for a given class depends +on whether the inner references are being kept so that non-tie-related +methods can be called on the object. But in most cases it probably makes +sense to move the functionality that would have been in DESTROY to the UNTIE +method. +.PP +If the UNTIE method exists then the warning above does not occur. Instead the +UNTIE method is passed the count of "extra" references and can issue its own +warning if appropriate. e.g. to replicate the no UNTIE case this method can +be used: +.PP +.Vb 6 +\& sub UNTIE +\& { +\& my ($obj,$count) = @_; +\& carp "untie attempted while $count inner references still exist" +\& if $count; +\& } +.Ve +.SH "SEE ALSO" +.IX Header "SEE ALSO" +See DB_File or Config for some interesting \fBtie()\fR implementations. +A good starting point for many \fBtie()\fR implementations is with one of the +modules Tie::Scalar, Tie::Array, Tie::Hash, or Tie::Handle. +.SH BUGS +.IX Header "BUGS" +The normal return provided by \f(CWscalar(%hash)\fR is not +available. What this means is that using \f(CW%tied_hash\fR in boolean +context doesn't work right (currently this always tests false, +regardless of whether the hash is empty or hash elements). +[ This paragraph needs review in light of changes in 5.25 ] +.PP +Localizing tied arrays or hashes does not work. After exiting the +scope the arrays or the hashes are not restored. +.PP +Counting the number of entries in a hash via \f(CW\*(C`scalar(keys(%hash))\*(C'\fR +or \f(CWscalar(values(%hash)\fR) is inefficient since it needs to iterate +through all the entries with FIRSTKEY/NEXTKEY. +.PP +Tied hash/array slices cause multiple FETCH/STORE pairs, there are no +tie methods for slice operations. +.PP +You cannot easily tie a multilevel data structure (such as a hash of +hashes) to a dbm file. The first problem is that all but GDBM and +Berkeley DB have size limitations, but beyond that, you also have problems +with how references are to be represented on disk. One +module that does attempt to address this need is DBM::Deep. Check your +nearest CPAN site as described in perlmodlib for source code. Note +that despite its name, DBM::Deep does not use dbm. Another earlier attempt +at solving the problem is MLDBM, which is also available on the CPAN, but +which has some fairly serious limitations. +.PP +Tied filehandles are still incomplete. \fBsysopen()\fR, \fBtruncate()\fR, +\&\fBflock()\fR, \fBfcntl()\fR, \fBstat()\fR and \-X can't currently be trapped. +.SH AUTHOR +.IX Header "AUTHOR" +Tom Christiansen +.PP +TIEHANDLE by Sven Verdoolaege <\fIskimo@dns.ufsia.ac.be\fR> and Doug MacEachern <\fIdougm@osf.org\fR> +.PP +UNTIE by Nick Ing-Simmons <\fInick@ing\-simmons.net\fR> +.PP +SCALAR by Tassilo von Parseval <\fItassilo.von.parseval@rwth\-aachen.de\fR> +.PP +Tying Arrays by Casey West <\fIcasey@geeknest.com\fR> |