perlman
gods
<H2><A NAME="Autoloaded_Data_Methods">Autoloaded Data Methods</A></H2>
<P>
You probably began to get a little suspicious about the duplicated code way
back earlier when we first showed you the Person class, and then later the
Employee class. Each method used to access the hash fields looked virtually
identical. This should have tickled that great programming virtue,
Impatience, but for the time, we let Laziness win out, and so did nothing.
Proxy methods can cure this.
<P>
Instead of writing a new function every time we want a new data field,
we'll use the autoload mechanism to generate (actually, mimic) methods on
the fly. To verify that we're accessing a valid member, we will check
against an <CODE>_permitted</CODE> (pronounced ``under-permitted'') field, which is a reference to a file-scoped lexical (like a
<FONT SIZE=-1>C</FONT> file static) hash of permitted fields in this record called %fields. Why the underscore? For the same reason as the
<FONT SIZE=-1>_CENSUS</FONT> field we once used: as a marker that means ``for internal use only''.
<P>
Here's what the module initialization code and class constructor will look
like when taking this approach:
<P>
<PRE> package Person;
use Carp;
use vars qw($AUTOLOAD); # it's a package global
</PRE>
<P>
<PRE> my %fields = (
name => undef,
age => undef,
peers => undef,
);
</PRE>
<P>
<PRE> sub new {
my $that = shift;
my $class = ref($that) || $that;
my $self = {
_permitted => \%fields,
%fields,
};
bless $self, $class;
return $self;
}
</PRE>
<P>
If we wanted our record to have default values, we could fill those in
where current we have [perlfunc:undef|undef] in the <CODE>%fields</CODE> hash.
<P>
Notice how we saved a reference to our class data on the object itself?
Remember that it's important to access class data through the object itself
instead of having any method reference <CODE>%fields</CODE> directly, or
else you won't have a decent inheritance.
<P>
The real magic, though, is going to reside in our proxy method, which will handle all calls to undefined methods for objects of class Person (or subclasses of Person). It has to be called
<FONT SIZE=-1>AUTOLOAD.</FONT> Again, it's all caps because it's called for us implicitly by Perl itself, not by a user directly.
<P>
<PRE> sub AUTOLOAD {
my $self = shift;
my $type = ref($self)
or croak "$self is not an object";
</PRE>
<P>
<PRE> my $name = $AUTOLOAD;
$name =~ s/.*://; # strip fully-qualified portion
</PRE>
<P>
<PRE> unless (exists $self->{_permitted}->{$name} ) {
croak "Can't access `$name' field in class $type";
}
</PRE>
<P>
<PRE> if (@_) {
return $self->{$name} = shift;
} else {
return $self->{$name};
}
}
</PRE>
<P>
Pretty nifty, eh? All we have to do to add new data fields is modify
%fields. No new functions need be written.
<P>
<FONT SIZE=-1>I</FONT> could have avoided the <CODE>_permitted</CODE> field entirely, but
<FONT SIZE=-1>I</FONT> wanted to demonstrate how to store a reference to
class data on the object so you wouldn't have to access that class data
directly from an object method.
<P>
<HR>
<H2><A NAME="Inherited_Autoloaded_Data_Method">Inherited Autoloaded Data Methods</A></H2>
<P>
But what about inheritance? Can we define our Employee class similarly?
Yes, so long as we're careful enough.
<P>
Here's how to be careful:
<P>
<PRE> package Employee;
use Person;
use strict;
use vars qw(@ISA);
@ISA = qw(Person);
</PRE>
<P>
<PRE> my %fields = (
id => undef,
salary => undef,
);
</PRE>
<P>
<PRE> sub new {
my $that = shift;
my $class = ref($that) || $that;
my $self = bless $that->SUPER::new(), $class;
my($element);
foreach $element (keys %fields) {
$self->{_permitted}->{$element} = $fields{$element};
}
@{$self}{keys %fields} = values %fields;
return $self;
}
</PRE>
<P>
Once we've done this, we don't even need to have an
<FONT SIZE=-1>AUTOLOAD</FONT> function in the Employee package, because
we'll grab Person's version of that via inheritance, and it will all work
out just fine.
<P>
<HR>
<H1><A NAME="Metaclassical_Tools">Metaclassical Tools</A></H1>
<P>
Even though proxy methods can provide a more convenient approach to making
more struct-like classes than tediously coding up data methods as
functions, it still leaves a bit to be desired. For one thing, it means you
have to handle bogus calls that you don't mean to trap via your proxy. It
also means you have to be quite careful when dealing with inheritance, as
detailed above.
<P>
Perl programmers have responded to this by creating several different class construction classes. These metaclasses are classes that create other classes.
<FONT SIZE=-1>A</FONT> couple worth looking at are Class::Struct and Alias. These and other related metaclasses can be found in the modules directory on
<FONT SIZE=-1>CPAN.</FONT>
<P>
<HR>
<H2><A NAME="Class_Struct">Class::Struct</A></H2>
<P>
One of the older ones is Class::Struct. In fact, its syntax and interface were sketched out long before perl5 even solidified into a real thing. What it does is provide you a way to ``declare'' a class as having objects whose fields are of a specific type. The function that does this is called, not surprisingly enough,
<CODE>struct().</CODE> Because structures or records are not base types in Perl, each time you want to create a class to provide a record-like data object, you yourself have to define a
<CODE>new()</CODE> method, plus separate data-access methods for each of that record's fields. You'll quickly become bored with this process. The Class::Struct::struct() function alleviates this tedium.
<P>
Here's a simple example of using it:
<P>
<PRE> use Class::Struct qw(struct);
use Jobbie; # user-defined; see below
</PRE>
<P>
<PRE> struct 'Fred' => {
one => '$',
many => '@',
profession => Jobbie, # calls Jobbie->new()
};
</PRE>
<P>
<PRE> $ob = Fred->new;
$ob->one("hmmmm");
</PRE>
<P>
<PRE> $ob->many(0, "here");
$ob->many(1, "you");
$ob->many(2, "go");
print "Just set: ", $ob->many(2), "\n";
</PRE>
<P>
<PRE> $ob->profession->salary(10_000);
</PRE>
<P>
You can declare types in the struct to be basic Perl types, or user-defined types (classes). User types will be initialized by calling that class's
<CODE>new()</CODE> method.
<P>
Here's a real-world example of using struct generation. Let's say you wanted to override Perl's idea of
<CODE>gethostbyname()</CODE> and
<CODE>gethostbyaddr()</CODE> so that they would return objects that acted like
<FONT SIZE=-1>C</FONT> structures. We don't care about high-falutin'
<FONT SIZE=-1>OO</FONT> gunk. All we want is for these objects to act like structs in the
<FONT SIZE=-1>C</FONT> sense.
<P>
<PRE> use Socket;
use Net::hostent;
$h = gethostbyname("perl.com"); # object return
printf "perl.com's real name is %s, address %s\n",
$h->name, inet_ntoa($h->addr);
</PRE>
<P>
Here's how to do this using the Class::Struct module. The crux is going to
be this call:
<P>
<PRE> struct 'Net::hostent' => [ # note bracket
name => '$',
aliases => '@',
addrtype => '$',
'length' => '$',
addr_list => '@',
];
</PRE>
<P>
Which creates object methods of those names and types. It even creates a
<CODE>new()</CODE> method for us.
<P>
We could also have implemented our object this way:
<P>
<PRE> struct 'Net::hostent' => { # note brace
name => '$',
aliases => '@',
addrtype => '$',
'length' => '$',
addr_list => '@',
};
</PRE>
<P>
and then Class::Struct would have used an anonymous hash as the object
type, instead of an anonymous array. The array is faster and smaller, but
the hash works out better if you eventually want to do inheritance. Since
for this struct-like object we aren't planning on inheritance, this time
we'll opt for better speed and size over better flexibility.
<P>
Here's the whole implementation:
<P>
<PRE> package Net::hostent;
use strict;
</PRE>
<P>
<PRE> BEGIN {
use Exporter ();
use vars qw(@EXPORT @EXPORT_OK %EXPORT_TAGS);
@EXPORT = qw(gethostbyname gethostbyaddr gethost);
@EXPORT_OK = qw(
$h_name @h_aliases
$h_addrtype $h_length
@h_addr_list $h_addr
);
%EXPORT_TAGS = ( FIELDS => [ @EXPORT_OK, @EXPORT ] );
}
use vars @EXPORT_OK;
</PRE>
<P>
<PRE> # Class::Struct forbids use of @ISA
sub import { goto &Exporter::import }
</PRE>
<P>
<PRE> use Class::Struct qw(struct);
struct 'Net::hostent' => [
name => '$',
aliases => '@',
addrtype => '$',
'length' => '$',
addr_list => '@',
];
</PRE>
<P>
<PRE> sub addr { shift->addr_list->[0] }
</PRE>
<P>
<PRE> sub populate (@) {
return unless @_;
my $hob = new(); # Class::Struct made this!
$h_name = $hob->[0] = $_[0];
@h_aliases = @{ $hob->[1] } = split ' ', $_[1];
$h_addrtype = $hob->[2] = $_[2];
$h_length = $hob->[3] = $_[3];
$h_addr = $_[4];
@h_addr_list = @{ $hob->[4] } = @_[ (4 .. $#_) ];
return $hob;
}
</PRE>
<P>
<PRE> sub gethostbyname ($) { populate(CORE::gethostbyname(shift)) }
</PRE>
<P>
<PRE> sub gethostbyaddr ($;$) {
my ($addr, $addrtype);
$addr = shift;
require Socket unless @_;
$addrtype = @_ ? shift : Socket::AF_INET();
populate(CORE::gethostbyaddr($addr, $addrtype))
}
</PRE>
<P>
<PRE> sub gethost($) {
if ($_[0] =~ /^\d+(?:\.\d+(?:\.\d+(?:\.\d+)?)?)?$/) {
require Socket;
&gethostbyaddr(Socket::inet_aton(shift));
} else {
&gethostbyname;
}
}
</PRE>
<P>
<PRE> 1;
</PRE>
<P>
We've snuck in quite a fair bit of other concepts besides just dynamic
class creation, like overriding core functions, import/export bits,
function prototyping, short-cut function call via <CODE>&whatever</CODE>, and function replacement with <CODE>goto &whatever</CODE>. These all mostly make sense from the perspective of a traditional module,
but as you can see, we can also use them in an object module.
<P>
You can look at other object-based, struct-like overrides of core functions in the 5.004 release of Perl in File::stat, Net::hostent, Net::netent, Net::protoent, Net::servent, Time::gmtime, Time::localtime, User::grent, and User::pwent. These modules have a final component that's all lowercase, by convention reserved for compiler pragmas, because they affect the compilation and change a builtin function. They also have the type names that a
<FONT SIZE=-1>C</FONT> programmer would most expect.
<P>
<HR>
<H2><A NAME="Data_Members_as_Variables">Data Members as Variables</A></H2>
<P>
If you're used to
<FONT SIZE=-1>C++</FONT> objects, then you're accustomed to being able to
get at an object's data members as simple variables from within a method.
The Alias module provides for this, as well as a good bit more, such as the
possibility of private methods that the object can call but folks outside
the class cannot.
<P>
Here's an example of creating a Person using the Alias module. When you
update these magical instance variables, you automatically update value
fields in the hash. Convenient, eh?
<P>
<PRE> package Person;
</PRE>
<P>
<PRE> # this is the same as before...
sub new {
my $that = shift;
my $class = ref($that) || $that;
my $self = {
NAME => undef,
AGE => undef,
PEERS => [],
};
bless($self, $class);
return $self;
}
</PRE>
<P>
<PRE> use Alias qw(attr);
use vars qw($NAME $AGE $PEERS);
</PRE>
<P>
<PRE> sub name {
my $self = attr shift;
if (@_) { $NAME = shift; }
return $NAME;
}
</PRE>
<P>
<PRE> sub age {
my $self = attr shift;
if (@_) { $AGE = shift; }
return $AGE;
}
</PRE>
<P>
<PRE> sub peers {
my $self = attr shift;
if (@_) { @PEERS = @_; }
return @PEERS;
}
</PRE>
<P>
<PRE> sub exclaim {
my $self = attr shift;
return sprintf "Hi, I'm %s, age %d, working with %s",
$NAME, $AGE, join(", ", @PEERS);
}
</PRE>
<P>
<PRE> sub happy_birthday {
my $self = attr shift;
return ++$AGE;
}
</PRE>
<P>
The need for the <CODE>use vars</CODE> declaration is because what Alias does is play with package globals with
the same name as the fields. To use globals while <CODE>use strict</CODE> is in effect, you have to predeclare them. These package variables are localized to the block enclosing the
<CODE>attr()</CODE> call just as if you'd used a
<CODE>local()</CODE> on them. However, that means that they're still considered global variables with temporary values, just as with any other
<CODE>local().</CODE>
<P>
It would be nice to combine Alias with something like Class::Struct or
Class::MethodMaker.
<P>
<HR>
<H2><A NAME="NOTES">NOTES</A></H2>
<P>
<HR>
<H2><A NAME="Object_Terminology">Object Terminology</A></H2>
<P>
In the various
<FONT SIZE=-1>OO</FONT> literature, it seems that a lot of different words
are used to describe only a few different concepts. If you're not already
an object programmer, then you don't need to worry about all these fancy
words. But if you are, then you might like to know how to get at the same
concepts in Perl.
<P>
For example, it's common to call an object an <EM>instance</EM> of a class and to call those objects' methods <EM>instance methods</EM>. Data fields peculiar to each object are often called <EM>instance data</EM> or <EM>object
attributes</EM>, and data fields common to all members of that class are
<EM>class data</EM>, <EM>class attributes</EM>, or <EM>static data members</EM>.
<P>
Also, <EM>base class</EM>, <EM>generic class</EM>, and <EM>superclass</EM> all describe the same notion, whereas <EM>derived class</EM>, <EM>specific class</EM>, and
<EM>subclass</EM> describe the other related one.
<P>
<FONT SIZE=-1>C++</FONT> programmers have <EM>static methods</EM> and <EM>virtual methods</EM>, but Perl only has <EM>class methods</EM> and <EM>object methods</EM>. Actually, Perl only has methods. Whether a method gets used as a class or
object method is by usage only. You could accidentally call a class method
(one expecting a string argument) on an object (one expecting a reference),
or vice versa.
<P>
From the
<FONT SIZE=-1>C++</FONT> perspective, all methods in Perl are virtual.
This, by the way, is why they are never checked for function prototypes in
the argument list as regular builtin and user-defined functions can be.
<P>
Because a class is itself something of an object, Perl's classes can be
taken as describing both a ``class as meta-object'' (also called <EM>object
factory</EM>) philosophy and the ``class as type definition'' (<EM>declaring</EM>
behaviour, not <EM>defining</EM> mechanism) idea.
<FONT SIZE=-1>C++</FONT> supports the latter notion, but not the former.
<P>
<HR>
<H1><A NAME="SEE_ALSO">SEE ALSO</A></H1>
<P>
The following manpages will doubtless provide more background for this one:
[perlman:perlmod|the perlmod manpage],
[perlman:perlref|the perlref manpage],
[perlman:perlobj|the perlobj manpage],
[perlman:perlbot|the perlbot manpage],
[perlman:perltie|the perltie manpage], and
<U>the overload manpage</U><!--../lib/overload.html-->.
<P>
<HR>
<H1><A NAME="AUTHOR_AND_COPYRIGHT">AUTHOR AND COPYRIGHT</A></H1>
<P>
Copyright (c) 1997, 1998 Tom Christiansen All rights reserved.
<P>
When included as part of the Standard Version of Perl, or as part of its
complete documentation whether printed or otherwise, this work may be
distributed only under the terms of Perl's Artistic License. Any
distribution of this file or derivatives thereof <EM>outside</EM>
of that package require that special arrangements be made with copyright
holder.
<P>
Irrespective of its distribution, all code examples in this file are hereby placed into the public domain. You are permitted and encouraged to use this code in your own programs for fun or for profit as you see fit.
<FONT SIZE=-1>A</FONT> simple comment in the code giving credit would be courteous but is not required.
<P>
<HR>
<H1><A NAME="COPYRIGHT">COPYRIGHT</A></H1>
<P>
<HR>
<H2><A NAME="Acknowledgments">Acknowledgments</A></H2>
<P>
Thanks to Larry Wall, Roderick Schertler, Gurusamy Sarathy, Dean Roehrich,
Raphael Manfredi, Brent Halsey, Greg Bacon, Brad Appleton, and many others
for their helpful comments.
<HR>
<BR>Return to the [Library]<BR>