13.5. Using Classes as StructsProblemYou're used to structured data types more complex than Perl's arrays and hashes, such as C's structs and Pascal's records. You've heard that Perl's classes are comparable, but you aren't an object-oriented programmer. SolutionUse the standard Class::Struct module to declare C-like structures:
use Class::Struct; # load struct-building module
struct Person => { # create a definition for a "Person"
name => '$', # name field is a scalar
age => '$', # age field is also a scalar
peers => '@', # but peers field is an array (reference)
};
my $p = Person->
Discussion
The
In the structure layout definition, the keys are the names of the fields and the values are the data type. This type can be one of the three base types,
The type can even be the name of another named structure - or any class, for that matter - which provides a constructor named
use Class::Struct;
struct Person => {name => '$', age => '$'};
struct Family => {head => 'Person', address => '$', members => '@'};
$folks = Family->
If you'd like to impose more parameter checking on the fields' values, supply your own version for the accessor method to override the default version. Let's say you wanted to make sure the age value contains only digits, and that it falls within reasonably human age requirements. Here's how that function might be coded: sub Person::age { use Carp; my ($self, $age) = @_; if (@_ > 2) { confess "too many arguments" } elsif (@_ == 1) { return $struct->{'age'} } elsif (@_ == 2) { carp "age `$age' isn't numeric" if $age !~ /^\d+/; carp "age `$age' is unreasonable" if $age > 150; $self->{'age'} = $age; } }
If you want to provide warnings only when the
-w
command-line flag is used, check the if ($^W) { carp "age `$age' isn't numeric" if $age !~ /^\d+/; carp "age `$age' is unreasonable" if $age > 150; } If you want to complain if -w is set, but to raise an exception if the user doesn't ask for warnings, do something like the following. Don't be confused by the pointer arrow; it's an indirect function call, not a method call. my $gripe = $^W ? \&carp : \&croak; $gripe->("age `$age' isn't numeric") if $age !~ /^\d+/; $gripe->("age `$age' is unreasonable") if $age > 150; Internally, the class is implemented using a hash, as most classes are. This makes your code easy to debug and manipulate. Consider the effect of printing out a structure in the debugger, for example. But the Class::Struct module also supports an array representation. Just specify the fields within square brackets instead of curly ones: struct Family => [head => 'Person', address => '$', members => '@'];
Empirical evidence suggests that selecting the array representation instead of a hash trims between 10% and 50% off the memory consumption of your objects, and up to 33% of the access time. The cost is less informative debugging information and more mental overhead when writing override functions, such as
The If all the fields are the same type, rather than writing it out this way: struct Card => { name => '$', color => '$', cost => '$', type => '$', release => '$', text => '$', };
you could use a struct Card => map { $_ => '$' } qw(name color cost type release text); Or, if you're a C programmer who prefers to precede the field name with its type, rather than vice-versa, just reverse the order: struct hostent => { reverse qw{ $ name @ aliases $ addrtype $ length @ addr_list }};
You can even make aliases, in the (dubious) spirit of #define h_type h_addrtype #define h_addr h_addr_list[0] In Perl, you might try this: # make (hostent object)-> As you see, you can add methods to a class - or functions to a package - simply by declaring a subroutine in the right namespace. You don't have to be in the file defining the class, subclass it, or do anything fancy and complicated. It would be much better to subclass it, however: package Extra::hostent; use Net::hostent; @ISA = qw(hostent); sub addr { shift->addr_list(0,@_) } 1; That one's already available in the standard Net::hostent class, so you needn't bother. Check out that module's source code as a form of inspirational reading. We can't be held responsible for what it inspires you to do, though. See Alsoperltoot (1), perlobj (1), and perlbot (1); the documentation for the standard Class::Struct module; the source code for the standard Net::hostent module; the documentation for the Alias module from CPAN; Recipe 13.3 |
|