struct.
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 new
method, plus separate data-access methods for each of that record's fields.
You'll quickly become bored with this process. The
Class::Template::struct() function alleviates this tedium.
Here's a simple example of using it:
use Class::Template qw(struct); use Jobbie; # user-defined; see below
struct 'Fred' => { one => '$', many => '@', profession => Jobbie, # calls Jobbie->new() };
$ob = Fred->new; $ob->one("hmmmm");
$ob->many(0, "here"); $ob->many(1, "you"); $ob->many(2, "go"); print "Just set: ", $ob->many(2), "\n";
$ob->profession->salary(10_000);
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
new
method.
Here's a real-world example of using struct generation. Let's say you wanted to override Perl's idea of gethostbyname
and gethostbyaddr
so that they would return objects that acted like
C structures. We don't care about high-falutin'
OO gunk. All we want is for these objects to act like structs in the
C sense.
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);
Here's how to do this using the Class::Template module. The crux is going to be this call:
struct 'Net::hostent' => [ # note bracket name => '$', aliases => '@', addrtype => '$', 'length' => '$', addr_list => '@', ];
Which creates object methods of those names and types. It even creates a
new
method for us.
We could also have implemented our object this way:
struct 'Net::hostent' => { # note brace name => '$', aliases => '@', addrtype => '$', 'length' => '$', addr_list => '@', };
and then Class::Template 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.
Here's the whole implementation:
package Net::hostent; use strict;
BEGIN { use Exporter (); use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @ISA = qw(Exporter); @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;
use Class::Template qw(struct); struct 'Net::hostent' => [ name => '$', aliases => '@', addrtype => '$', 'length' => '$', addr_list => '@', ];
sub addr { shift->addr_list->[0] }
sub populate (@) { return unless @_; my $hob = new(); # Class::Template 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; }
sub gethostbyname ($) { populate(CORE::gethostbyname(shift)) }
sub gethostbyaddr ($;$) { my ($addr, $addrtype); $addr = shift; require Socket unless @_; $addrtype = @_ ? shift : Socket::AF_INET(); populate(CORE::gethostbyaddr($addr, $addrtype)) }
sub gethost($) { if ($_[0] =~ /^\d+(?:\.\d+(?:\.\d+(?:\.\d+)?)?)?$/) { require Socket; &gethostbyaddr(Socket::inet_aton(shift)); } else { &gethostbyname; } }
1;
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, and short-cut function call via &whatever
. 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.
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 lower-case, by convention reserved for compiler pragmas, because they affect the compilation and change a built-in function. They also have the type names that a C programmer would most expect.