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/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm | |
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/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm')
-rw-r--r-- | upstream/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm | 860 |
1 files changed, 860 insertions, 0 deletions
diff --git a/upstream/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm b/upstream/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm new file mode 100644 index 00000000..0128602d --- /dev/null +++ b/upstream/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm @@ -0,0 +1,860 @@ +.\" -*- 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 "Hash::Util::FieldHash 3pm" +.TH Hash::Util::FieldHash 3pm 2023-11-28 "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 +Hash::Util::FieldHash \- Support for Inside\-Out Classes +.SH SYNOPSIS +.IX Header "SYNOPSIS" +.Vb 2 +\& ### Create fieldhashes +\& use Hash::Util qw(fieldhash fieldhashes); +\& +\& # Create a single field hash +\& fieldhash my %foo; +\& +\& # Create three at once... +\& fieldhashes \e my(%foo, %bar, %baz); +\& # ...or any number +\& fieldhashes @hashrefs; +\& +\& ### Create an idhash and register it for garbage collection +\& use Hash::Util::FieldHash qw(idhash register); +\& idhash my %name; +\& my $object = \e do { my $o }; +\& # register the idhash for garbage collection with $object +\& register($object, \e %name); +\& # the following entry will be deleted when $object goes out of scope +\& $name{$object} = \*(AqJohn Doe\*(Aq; +\& +\& ### Register an ordinary hash for garbage collection +\& use Hash::Util::FieldHash qw(id register); +\& my %name; +\& my $object = \e do { my $o }; +\& # register the hash %name for garbage collection of $object\*(Aqs id +\& register $object, \e %name; +\& # the following entry will be deleted when $object goes out of scope +\& $name{id $object} = \*(AqJohn Doe\*(Aq; +.Ve +.SH FUNCTIONS +.IX Header "FUNCTIONS" +\&\f(CW\*(C`Hash::Util::FieldHash\*(C'\fR offers a number of functions in support of +"The Inside-out Technique" of class construction. +.IP id 4 +.IX Item "id" +.Vb 1 +\& id($obj) +.Ve +.Sp +Returns the reference address of a reference \f(CW$obj\fR. If \f(CW$obj\fR is +not a reference, returns \f(CW$obj\fR. +.Sp +This function is a stand-in replacement for +Scalar::Util::refaddr, +that is, it returns +the reference address of its argument as a numeric value. The only +difference is that \f(CWrefaddr()\fR returns \f(CW\*(C`undef\*(C'\fR when given a +non-reference while \f(CWid()\fR returns its argument unchanged. +.Sp +\&\f(CWid()\fR also uses a caching technique that makes it faster when +the id of an object is requested often, but slower if it is needed +only once or twice. +.IP id_2obj 4 +.IX Item "id_2obj" +.Vb 1 +\& $obj = id_2obj($id) +.Ve +.Sp +If \f(CW$id\fR is the id of a registered object (see "register"), returns +the object, otherwise an undefined value. For registered objects this +is the inverse function of \f(CWid()\fR. +.IP register 4 +.IX Item "register" +.Vb 2 +\& register($obj) +\& register($obj, @hashrefs) +.Ve +.Sp +In the first form, registers an object to work with for the function +\&\f(CWid_2obj()\fR. In the second form, it additionally marks the given +hashrefs down for garbage collection. This means that when the object +goes out of scope, any entries in the given hashes under the key of +\&\f(CWid($obj)\fR will be deleted from the hashes. +.Sp +It is a fatal error to register a non-reference \f(CW$obj\fR. Any non-hashrefs +among the following arguments are silently ignored. +.Sp +It is \fInot\fR an error to register the same object multiple times with +varying sets of hashrefs. Any hashrefs that are not registered yet +will be added, others ignored. +.Sp +Registry also implies thread support. When a new thread is created, +all references are replaced with new ones, including all objects. +If a hash uses the reference address of an object as a key, that +connection would be broken. With a registered object, its id will +be updated in all hashes registered with it. +.IP idhash 4 +.IX Item "idhash" +.Vb 1 +\& idhash my %hash +.Ve +.Sp +Makes an idhash from the argument, which must be a hash. +.Sp +An \fIidhash\fR works like a normal hash, except that it stringifies a +\&\fIreference used as a key\fR differently. A reference is stringified +as if the \f(CWid()\fR function had been invoked on it, that is, its +reference address in decimal is used as the key. +.IP idhashes 4 +.IX Item "idhashes" +.Vb 2 +\& idhashes \e my(%hash, %gnash, %trash) +\& idhashes \e @hashrefs +.Ve +.Sp +Creates many idhashes from its hashref arguments. Returns those +arguments that could be converted or their number in scalar context. +.IP fieldhash 4 +.IX Item "fieldhash" +.Vb 1 +\& fieldhash %hash; +.Ve +.Sp +Creates a single fieldhash. The argument must be a hash. Returns +a reference to the given hash if successful, otherwise nothing. +.Sp +A \fIfieldhash\fR is, in short, an idhash with auto-registry. When an +object (or, indeed, any reference) is used as a fieldhash key, the +fieldhash is automatically registered for garbage collection with +the object, as if \f(CW\*(C`register $obj, \e %fieldhash\*(C'\fR had been called. +.IP fieldhashes 4 +.IX Item "fieldhashes" +.Vb 1 +\& fieldhashes @hashrefs; +.Ve +.Sp +Creates any number of field hashes. Arguments must be hash references. +Returns the converted hashrefs in list context, their number in scalar +context. +.SH DESCRIPTION +.IX Header "DESCRIPTION" +A word on terminology: I shall use the term \fIfield\fR for a scalar +piece of data that a class associates with an object. Other terms that +have been used for this concept are "object variable", "(object) property", +"(object) attribute" and more. Especially "attribute" has some currency +among Perl programmer, but that clashes with the \f(CW\*(C`attributes\*(C'\fR pragma. The +term "field" also has some currency in this sense and doesn't seem +to conflict with other Perl terminology. +.PP +In Perl, an object is a blessed reference. The standard way of associating +data with an object is to store the data inside the object's body, that is, +the piece of data pointed to by the reference. +.PP +In consequence, if two or more classes want to access an object they +\&\fImust\fR agree on the type of reference and also on the organization of +data within the object body. Failure to agree on the type results in +immediate death when the wrong method tries to access an object. Failure +to agree on data organization may lead to one class trampling over the +data of another. +.PP +This object model leads to a tight coupling between subclasses. +If one class wants to inherit from another (and both classes access +object data), the classes must agree about implementation details. +Inheritance can only be used among classes that are maintained together, +in a single source or not. +.PP +In particular, it is not possible to write general-purpose classes +in this technique, classes that can advertise themselves as "Put me +on your \f(CW@ISA\fR list and use my methods". If the other class has different +ideas about how the object body is used, there is trouble. +.PP +For reference \f(CW\*(C`Name_hash\*(C'\fR in "Example 1" shows the standard implementation of +a simple class \f(CW\*(C`Name\*(C'\fR in the well-known hash based way. It also demonstrates +the predictable failure to construct a common subclass \f(CW\*(C`NamedFile\*(C'\fR +of \f(CW\*(C`Name\*(C'\fR and the class \f(CW\*(C`IO::File\*(C'\fR (whose objects \fImust\fR be globrefs). +.PP +Thus, techniques are of interest that store object data \fInot\fR in +the object body but some other place. +.SS "The Inside-out Technique" +.IX Subsection "The Inside-out Technique" +With \fIinside-out\fR classes, each class declares a (typically lexical) +hash for each field it wants to use. The reference address of an +object is used as the hash key. By definition, the reference address +is unique to each object so this guarantees a place for each field that +is private to the class and unique to each object. See \f(CW\*(C`Name_id\*(C'\fR +in "Example 1" for a simple example. +.PP +In comparison to the standard implementation where the object is a +hash and the fields correspond to hash keys, here the fields correspond +to hashes, and the object determines the hash key. Thus the hashes +appear to be turned \fIinside out\fR. +.PP +The body of an object is never examined by an inside-out class, only +its reference address is used. This allows for the body of an actual +object to be \fIanything at all\fR while the object methods of the class +still work as designed. This is a key feature of inside-out classes. +.SS "Problems of Inside-out" +.IX Subsection "Problems of Inside-out" +Inside-out classes give us freedom of inheritance, but as usual there +is a price. +.PP +Most obviously, there is the necessity of retrieving the reference +address of an object for each data access. It's a minor inconvenience, +but it does clutter the code. +.PP +More important (and less obvious) is the necessity of garbage +collection. When a normal object dies, anything stored in the +object body is garbage-collected by perl. With inside-out objects, +Perl knows nothing about the data stored in field hashes by a class, +but these must be deleted when the object goes out of scope. Thus +the class must provide a \f(CW\*(C`DESTROY\*(C'\fR method to take care of that. +.PP +In the presence of multiple classes it can be non-trivial +to make sure that every relevant destructor is called for +every object. Perl calls the first one it finds on the +inheritance tree (if any) and that's it. +.PP +A related issue is thread-safety. When a new thread is created, +the Perl interpreter is cloned, which implies that all reference +addresses in use will be replaced with new ones. Thus, if a class +tries to access a field of a cloned object its (cloned) data will +still be stored under the now invalid reference address of the +original in the parent thread. A general \f(CW\*(C`CLONE\*(C'\fR method must +be provided to re-establish the association. +.SS Solutions +.IX Subsection "Solutions" +\&\f(CW\*(C`Hash::Util::FieldHash\*(C'\fR addresses these issues on several +levels. +.PP +The \f(CWid()\fR function is provided in addition to the +existing \f(CWScalar::Util::refaddr()\fR. Besides its short name +it can be a little faster under some circumstances (and a +bit slower under others). Benchmark if it matters. The +working of \f(CWid()\fR also allows the use of the class name +as a \fIgeneric object\fR as described further down. +.PP +The \f(CWid()\fR function is incorporated in \fIid hashes\fR in the sense +that it is called automatically on every key that is used with +the hash. No explicit call is necessary. +.PP +The problems of garbage collection and thread safety are both +addressed by the function \f(CWregister()\fR. It registers an object +together with any number of hashes. Registry means that when the +object dies, an entry in any of the hashes under the reference +address of this object will be deleted. This guarantees garbage +collection in these hashes. It also means that on thread +cloning the object's entries in registered hashes will be +replaced with updated entries whose key is the cloned object's +reference address. Thus the object-data association becomes +thread-safe. +.PP +Object registry is best done when the object is initialized +for use with a class. That way, garbage collection and thread +safety are established for every object and every field that is +initialized. +.PP +Finally, \fIfield hashes\fR incorporate all these functions in one +package. Besides automatically calling the \f(CWid()\fR function +on every object used as a key, the object is registered with +the field hash on first use. Classes based on field hashes +are fully garbage-collected and thread safe without further +measures. +.SS "More Problems" +.IX Subsection "More Problems" +Another problem that occurs with inside-out classes is serialization. +Since the object data is not in its usual place, standard routines +like \f(CWStorable::freeze()\fR, \f(CWStorable::thaw()\fR and +\&\f(CWData::Dumper::Dumper()\fR can't deal with it on their own. Both +\&\f(CW\*(C`Data::Dumper\*(C'\fR and \f(CW\*(C`Storable\*(C'\fR provide the necessary hooks to +make things work, but the functions or methods used by the hooks +must be provided by each inside-out class. +.PP +A general solution to the serialization problem would require another +level of registry, one that associates \fIclasses\fR and fields. +So far, the functions of \f(CW\*(C`Hash::Util::FieldHash\*(C'\fR are unaware of +any classes, which I consider a feature. Therefore \f(CW\*(C`Hash::Util::FieldHash\*(C'\fR +doesn't address the serialization problems. +.SS "The Generic Object" +.IX Subsection "The Generic Object" +Classes based on the \f(CWid()\fR function (and hence classes based on +\&\f(CWidhash()\fR and \f(CWfieldhash()\fR) show a peculiar behavior in that +the class name can be used like an object. Specifically, methods +that set or read data associated with an object continue to work as +class methods, just as if the class name were an object, distinct from +all other objects, with its own data. This object may be called +the \fIgeneric object\fR of the class. +.PP +This works because field hashes respond to keys that are not references +like a normal hash would and use the string offered as the hash key. +Thus, if a method is called as a class method, the field hash is presented +with the class name instead of an object and blithely uses it as a key. +Since the keys of real objects are decimal numbers, there is no +conflict and the slot in the field hash can be used like any other. +The \f(CWid()\fR function behaves correspondingly with respect to non-reference +arguments. +.PP +Two possible uses (besides ignoring the property) come to mind. +A singleton class could be implemented this using the generic object. +If necessary, an \f(CWinit()\fR method could die or ignore calls with +actual objects (references), so only the generic object will ever exist. +.PP +Another use of the generic object would be as a template. It is +a convenient place to store class-specific defaults for various +fields to be used in actual object initialization. +.PP +Usually, the feature can be entirely ignored. Calling \fIobject +methods\fR as \fIclass methods\fR normally leads to an error and isn't used +routinely anywhere. It may be a problem that this error isn't +indicated by a class with a generic object. +.SS "How to use Field Hashes" +.IX Subsection "How to use Field Hashes" +Traditionally, the definition of an inside-out class contains a bare +block inside which a number of lexical hashes are declared and the +basic accessor methods defined, usually through \f(CW\*(C`Scalar::Util::refaddr\*(C'\fR. +Further methods may be defined outside this block. There has to be +a DESTROY method and, for thread support, a CLONE method. +.PP +When field hashes are used, the basic structure remains the same. +Each lexical hash will be made a field hash. The call to \f(CW\*(C`refaddr\*(C'\fR +can be omitted from the accessor methods. DESTROY and CLONE methods +are not necessary. +.PP +If you have an existing inside-out class, simply making all hashes +field hashes with no other change should make no difference. Through +the calls to \f(CW\*(C`refaddr\*(C'\fR or equivalent, the field hashes never get to +see a reference and work like normal hashes. Your DESTROY (and +CLONE) methods are still needed. +.PP +To make the field hashes kick in, it is easiest to redefine \f(CW\*(C`refaddr\*(C'\fR +as +.PP +.Vb 1 +\& sub refaddr { shift } +.Ve +.PP +instead of importing it from \f(CW\*(C`Scalar::Util\*(C'\fR. It should now be possible +to disable DESTROY and CLONE. Note that while it isn't disabled, +DESTROY will be called before the garbage collection of field hashes, +so it will be invoked with a functional object and will continue to +function. +.PP +It is not desirable to import the functions \f(CW\*(C`fieldhash\*(C'\fR and/or +\&\f(CW\*(C`fieldhashes\*(C'\fR into every class that is going to use them. They +are only used once to set up the class. When the class is up and running, +these functions serve no more purpose. +.PP +If there are only a few field hashes to declare, it is simplest to +.PP +.Vb 1 +\& use Hash::Util::FieldHash; +.Ve +.PP +early and call the functions qualified: +.PP +.Vb 1 +\& Hash::Util::FieldHash::fieldhash my %foo; +.Ve +.PP +Otherwise, import the functions into a convenient package like +\&\f(CW\*(C`HUF\*(C'\fR or, more general, \f(CW\*(C`Aux\*(C'\fR +.PP +.Vb 4 +\& { +\& package Aux; +\& use Hash::Util::FieldHash \*(Aq:all\*(Aq; +\& } +.Ve +.PP +and call +.PP +.Vb 1 +\& Aux::fieldhash my %foo; +.Ve +.PP +as needed. +.SS "Garbage-Collected Hashes" +.IX Subsection "Garbage-Collected Hashes" +Garbage collection in a field hash means that entries will "spontaneously" +disappear when the object that created them disappears. That must be +borne in mind, especially when looping over a field hash. If anything +you do inside the loop could cause an object to go out of scope, a +random key may be deleted from the hash you are looping over. That +can throw the loop iterator, so it's best to cache a consistent snapshot +of the keys and/or values and loop over that. You will still have to +check that a cached entry still exists when you get to it. +.PP +Garbage collection can be confusing when keys are created in a field hash +from normal scalars as well as references. Once a reference is \fIused\fR with +a field hash, the entry will be collected, even if it was later overwritten +with a plain scalar key (every positive integer is a candidate). This +is true even if the original entry was deleted in the meantime. In fact, +deletion from a field hash, and also a test for existence constitute +\&\fIuse\fR in this sense and create a liability to delete the entry when +the reference goes out of scope. If you happen to create an entry +with an identical key from a string or integer, that will be collected +instead. Thus, mixed use of references and plain scalars as field hash +keys is not entirely supported. +.SH EXAMPLES +.IX Header "EXAMPLES" +The examples show a very simple class that implements a \fIname\fR, consisting +of a first and last name (no middle initial). The name class has four +methods: +.IP \(bu 4 +\&\f(CWinit()\fR +.Sp +An object method that initializes the first and last name to its +two arguments. If called as a class method, \f(CWinit()\fR creates an +object in the given class and initializes that. +.IP \(bu 4 +\&\f(CWfirst()\fR +.Sp +Retrieve the first name +.IP \(bu 4 +\&\f(CWlast()\fR +.Sp +Retrieve the last name +.IP \(bu 4 +\&\f(CWname()\fR +.Sp +Retrieve the full name, the first and last name joined by a blank. +.PP +The examples show this class implemented with different levels of +support by \f(CW\*(C`Hash::Util::FieldHash\*(C'\fR. All supported combinations +are shown. The difference between implementations is often quite +small. The implementations are: +.IP \(bu 4 +\&\f(CW\*(C`Name_hash\*(C'\fR +.Sp +A conventional (not inside-out) implementation where an object is +a hash that stores the field values, without support by +\&\f(CW\*(C`Hash::Util::FieldHash\*(C'\fR. This implementation doesn't allow +arbitrary inheritance. +.IP \(bu 4 +\&\f(CW\*(C`Name_id\*(C'\fR +.Sp +Inside-out implementation based on the \f(CWid()\fR function. It needs +a \f(CW\*(C`DESTROY\*(C'\fR method. For thread support a \f(CW\*(C`CLONE\*(C'\fR method (not shown) +would also be needed. Instead of \f(CWHash::Util::FieldHash::id()\fR the +function \f(CW\*(C`Scalar::Util::refaddr\*(C'\fR could be used with very little +functional difference. This is the basic pattern of an inside-out +class. +.IP \(bu 4 +\&\f(CW\*(C`Name_idhash\*(C'\fR +.Sp +Idhash-based inside-out implementation. Like \f(CW\*(C`Name_id\*(C'\fR it needs +a \f(CW\*(C`DESTROY\*(C'\fR method and would need \f(CW\*(C`CLONE\*(C'\fR for thread support. +.IP \(bu 4 +\&\f(CW\*(C`Name_id_reg\*(C'\fR +.Sp +Inside-out implementation based on the \f(CWid()\fR function with explicit +object registry. No destructor is needed and objects are thread safe. +.IP \(bu 4 +\&\f(CW\*(C`Name_idhash_reg\*(C'\fR +.Sp +Idhash-based inside-out implementation with explicit object registry. +No destructor is needed and objects are thread safe. +.IP \(bu 4 +\&\f(CW\*(C`Name_fieldhash\*(C'\fR +.Sp +FieldHash-based inside-out implementation. Object registry happens +automatically. No destructor is needed and objects are thread safe. +.PP +These examples are realized in the code below, which could be copied +to a file \fIExample.pm\fR. +.SS "Example 1" +.IX Subsection "Example 1" +.Vb 1 +\& use strict; use warnings; +\& +\& { +\& package Name_hash; # standard implementation: the +\& # object is a hash +\& sub init { +\& my $obj = shift; +\& my ($first, $last) = @_; +\& # create an object if called as class method +\& $obj = bless {}, $obj unless ref $obj; +\& $obj\->{ first} = $first; +\& $obj\->{ last} = $last; +\& $obj; +\& } +\& +\& sub first { shift()\->{ first} } +\& sub last { shift()\->{ last} } +\& +\& sub name { +\& my $n = shift; +\& join \*(Aq \*(Aq => $n\->first, $n\->last; +\& } +\& +\& } +\& +\& { +\& package Name_id; +\& use Hash::Util::FieldHash qw(id); +\& +\& my (%first, %last); +\& +\& sub init { +\& my $obj = shift; +\& my ($first, $last) = @_; +\& # create an object if called as class method +\& $obj = bless \e my $o, $obj unless ref $obj; +\& $first{ id $obj} = $first; +\& $last{ id $obj} = $last; +\& $obj; +\& } +\& +\& sub first { $first{ id shift()} } +\& sub last { $last{ id shift()} } +\& +\& sub name { +\& my $n = shift; +\& join \*(Aq \*(Aq => $n\->first, $n\->last; +\& } +\& +\& sub DESTROY { +\& my $id = id shift; +\& delete $first{ $id}; +\& delete $last{ $id}; +\& } +\& +\& } +\& +\& { +\& package Name_idhash; +\& use Hash::Util::FieldHash; +\& +\& Hash::Util::FieldHash::idhashes( \e my (%first, %last) ); +\& +\& sub init { +\& my $obj = shift; +\& my ($first, $last) = @_; +\& # create an object if called as class method +\& $obj = bless \e my $o, $obj unless ref $obj; +\& $first{ $obj} = $first; +\& $last{ $obj} = $last; +\& $obj; +\& } +\& +\& sub first { $first{ shift()} } +\& sub last { $last{ shift()} } +\& +\& sub name { +\& my $n = shift; +\& join \*(Aq \*(Aq => $n\->first, $n\->last; +\& } +\& +\& sub DESTROY { +\& my $n = shift; +\& delete $first{ $n}; +\& delete $last{ $n}; +\& } +\& +\& } +\& +\& { +\& package Name_id_reg; +\& use Hash::Util::FieldHash qw(id register); +\& +\& my (%first, %last); +\& +\& sub init { +\& my $obj = shift; +\& my ($first, $last) = @_; +\& # create an object if called as class method +\& $obj = bless \e my $o, $obj unless ref $obj; +\& register( $obj, \e (%first, %last) ); +\& $first{ id $obj} = $first; +\& $last{ id $obj} = $last; +\& $obj; +\& } +\& +\& sub first { $first{ id shift()} } +\& sub last { $last{ id shift()} } +\& +\& sub name { +\& my $n = shift; +\& join \*(Aq \*(Aq => $n\->first, $n\->last; +\& } +\& } +\& +\& { +\& package Name_idhash_reg; +\& use Hash::Util::FieldHash qw(register); +\& +\& Hash::Util::FieldHash::idhashes \e my (%first, %last); +\& +\& sub init { +\& my $obj = shift; +\& my ($first, $last) = @_; +\& # create an object if called as class method +\& $obj = bless \e my $o, $obj unless ref $obj; +\& register( $obj, \e (%first, %last) ); +\& $first{ $obj} = $first; +\& $last{ $obj} = $last; +\& $obj; +\& } +\& +\& sub first { $first{ shift()} } +\& sub last { $last{ shift()} } +\& +\& sub name { +\& my $n = shift; +\& join \*(Aq \*(Aq => $n\->first, $n\->last; +\& } +\& } +\& +\& { +\& package Name_fieldhash; +\& use Hash::Util::FieldHash; +\& +\& Hash::Util::FieldHash::fieldhashes \e my (%first, %last); +\& +\& sub init { +\& my $obj = shift; +\& my ($first, $last) = @_; +\& # create an object if called as class method +\& $obj = bless \e my $o, $obj unless ref $obj; +\& $first{ $obj} = $first; +\& $last{ $obj} = $last; +\& $obj; +\& } +\& +\& sub first { $first{ shift()} } +\& sub last { $last{ shift()} } +\& +\& sub name { +\& my $n = shift; +\& join \*(Aq \*(Aq => $n\->first, $n\->last; +\& } +\& } +\& +\& 1; +.Ve +.PP +To exercise the various implementations the script below can +be used. +.PP +It sets up a class \f(CW\*(C`Name\*(C'\fR that is a mirror of one of the implementation +classes \f(CW\*(C`Name_hash\*(C'\fR, \f(CW\*(C`Name_id\*(C'\fR, ..., \f(CW\*(C`Name_fieldhash\*(C'\fR. That determines +which implementation is run. +.PP +The script first verifies the function of the \f(CW\*(C`Name\*(C'\fR class. +.PP +In the second step, the free inheritability of the implementation +(or lack thereof) is demonstrated. For this purpose it constructs +a class called \f(CW\*(C`NamedFile\*(C'\fR which is a common subclass of \f(CW\*(C`Name\*(C'\fR and +the standard class \f(CW\*(C`IO::File\*(C'\fR. This puts inheritability to the test +because objects of \f(CW\*(C`IO::File\*(C'\fR \fImust\fR be globrefs. Objects of \f(CW\*(C`NamedFile\*(C'\fR +should behave like a file opened for reading and also support the \f(CWname()\fR +method. This class juncture works with exception of the \f(CW\*(C`Name_hash\*(C'\fR +implementation, where object initialization fails because of the +incompatibility of object bodies. +.SS "Example 2" +.IX Subsection "Example 2" +.Vb 1 +\& use strict; use warnings; $| = 1; +\& +\& use Example; +\& +\& { +\& package Name; +\& use parent \*(AqName_id\*(Aq; # define here which implementation to run +\& } +\& +\& +\& # Verify that the base package works +\& my $n = Name\->init(qw(Albert Einstein)); +\& print $n\->name, "\en"; +\& print "\en"; +\& +\& # Create a named file handle (See definition below) +\& my $nf = NamedFile\->init(qw(/tmp/x Filomena File)); +\& # use as a file handle... +\& for ( 1 .. 3 ) { +\& my $l = <$nf>; +\& print "line $_: $l"; +\& } +\& # ...and as a Name object +\& print "...brought to you by ", $nf\->name, "\en"; +\& exit; +\& +\& +\& # Definition of NamedFile +\& package NamedFile; +\& use parent \*(AqName\*(Aq; +\& use parent \*(AqIO::File\*(Aq; +\& +\& sub init { +\& my $obj = shift; +\& my ($file, $first, $last) = @_; +\& $obj = $obj\->IO::File::new() unless ref $obj; +\& $obj\->open($file) or die "Can\*(Aqt read \*(Aq$file\*(Aq: $!"; +\& $obj\->Name::init($first, $last); +\& } +\& _\|_END_\|_ +.Ve +.SH GUTS +.IX Header "GUTS" +To make \f(CW\*(C`Hash::Util::FieldHash\*(C'\fR work, there were two changes to +\&\fIperl\fR itself. \f(CW\*(C`PERL_MAGIC_uvar\*(C'\fR was made available for hashes, +and weak references now call uvar \f(CW\*(C`get\*(C'\fR magic after a weakref has been +cleared. The first feature is used to make field hashes intercept +their keys upon access. The second one triggers garbage collection. +.ie n .SS "The ""PERL_MAGIC_uvar"" interface for hashes" +.el .SS "The \f(CWPERL_MAGIC_uvar\fP interface for hashes" +.IX Subsection "The PERL_MAGIC_uvar interface for hashes" +\&\f(CW\*(C`PERL_MAGIC_uvar\*(C'\fR \fIget\fR magic is called from \f(CW\*(C`hv_fetch_common\*(C'\fR and +\&\f(CW\*(C`hv_delete_common\*(C'\fR through the function \f(CW\*(C`hv_magic_uvar_xkey\*(C'\fR, which +defines the interface. The call happens for hashes with "uvar" magic +if the \f(CW\*(C`ufuncs\*(C'\fR structure has equal values in the \f(CW\*(C`uf_val\*(C'\fR and \f(CW\*(C`uf_set\*(C'\fR +fields. Hashes are unaffected if (and as long as) these fields +hold different values. +.PP +Upon the call, the \f(CW\*(C`mg_obj\*(C'\fR field will hold the hash key to be accessed. +Upon return, the \f(CW\*(C`SV*\*(C'\fR value in \f(CW\*(C`mg_obj\*(C'\fR will be used in place of the +original key in the hash access. The integer index value in the first +parameter will be the \f(CW\*(C`action\*(C'\fR value from \f(CW\*(C`hv_fetch_common\*(C'\fR, or \-1 +if the call is from \f(CW\*(C`hv_delete_common\*(C'\fR. +.PP +This is a template for a function suitable for the \f(CW\*(C`uf_val\*(C'\fR field in +a \f(CW\*(C`ufuncs\*(C'\fR structure for this call. The \f(CW\*(C`uf_set\*(C'\fR and \f(CW\*(C`uf_index\*(C'\fR +fields are irrelevant. +.PP +.Vb 10 +\& IV watch_key(pTHX_ IV action, SV* field) { +\& MAGIC* mg = mg_find(field, PERL_MAGIC_uvar); +\& SV* keysv = mg\->mg_obj; +\& /* Do whatever you need to. If you decide to +\& supply a different key newkey, return it like this +\& */ +\& sv_2mortal(newkey); +\& mg\->mg_obj = newkey; +\& return 0; +\& } +.Ve +.SS "Weakrefs call uvar magic" +.IX Subsection "Weakrefs call uvar magic" +When a weak reference is stored in an \f(CW\*(C`SV\*(C'\fR that has "uvar" magic, \f(CW\*(C`set\*(C'\fR +magic is called after the reference has gone stale. This hook can be +used to trigger further garbage-collection activities associated with +the referenced object. +.SS "How field hashes work" +.IX Subsection "How field hashes work" +The three features of key hashes, \fIkey replacement\fR, \fIthread support\fR, +and \fIgarbage collection\fR are supported by a data structure called +the \fIobject registry\fR. This is a private hash where every object +is stored. An "object" in this sense is any reference (blessed or +unblessed) that has been used as a field hash key. +.PP +The object registry keeps track of references that have been used as +field hash keys. The keys are generated from the reference address +like in a field hash (though the registry isn't a field hash). Each +value is a weak copy of the original reference, stored in an \f(CW\*(C`SV\*(C'\fR that +is itself magical (\f(CW\*(C`PERL_MAGIC_uvar\*(C'\fR again). The magical structure +holds a list (another hash, really) of field hashes that the reference +has been used with. When the weakref becomes stale, the magic is +activated and uses the list to delete the reference from all field +hashes it has been used with. After that, the entry is removed from +the object registry itself. Implicitly, that frees the magic structure +and the storage it has been using. +.PP +Whenever a reference is used as a field hash key, the object registry +is checked and a new entry is made if necessary. The field hash is +then added to the list of fields this reference has used. +.PP +The object registry is also used to repair a field hash after thread +cloning. Here, the entire object registry is processed. For every +reference found there, the field hashes it has used are visited and +the entry is updated. +.SS "Internal function Hash::Util::FieldHash::_fieldhash" +.IX Subsection "Internal function Hash::Util::FieldHash::_fieldhash" +.Vb 2 +\& # test if %hash is a field hash +\& my $result = _fieldhash \e %hash, 0; +\& +\& # make %hash a field hash +\& my $result = _fieldhash \e %hash, 1; +.Ve +.PP +\&\f(CW\*(C`_fieldhash\*(C'\fR is the internal function used to create field hashes. +It takes two arguments, a hashref and a mode. If the mode is boolean +false, the hash is not changed but tested if it is a field hash. If +the hash isn't a field hash the return value is boolean false. If it +is, the return value indicates the mode of field hash. When called with +a boolean true mode, it turns the given hash into a field hash of this +mode, returning the mode of the created field hash. \f(CW\*(C`_fieldhash\*(C'\fR +does not erase the given hash. +.PP +Currently there is only one type of field hash, and only the boolean +value of the mode makes a difference, but that may change. +.SH AUTHOR +.IX Header "AUTHOR" +Anno Siegel (ANNO) wrote the xs code and the changes in perl proper +Jerry Hedden (JDHEDDEN) made it faster +.SH "COPYRIGHT AND LICENSE" +.IX Header "COPYRIGHT AND LICENSE" +Copyright (C) 2006\-2007 by (Anno Siegel) +.PP +This library is free software; you can redistribute it and/or modify +it under the same terms as Perl itself, either Perl version 5.8.7 or, +at your option, any later version of Perl 5 you may have available. |