summaryrefslogtreecommitdiffstats
path: root/upstream/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:43:11 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:43:11 +0000
commitfc22b3d6507c6745911b9dfcc68f1e665ae13dbc (patch)
treece1e3bce06471410239a6f41282e328770aa404a /upstream/mageia-cauldron/man3pm/Hash::Util::FieldHash.3pm
parentInitial commit. (diff)
downloadmanpages-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.3pm860
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.