1*0Sstevel@tonic-gatepackage fields; 2*0Sstevel@tonic-gate 3*0Sstevel@tonic-gaterequire 5.005; 4*0Sstevel@tonic-gateuse strict; 5*0Sstevel@tonic-gateno strict 'refs'; 6*0Sstevel@tonic-gateunless( eval q{require warnings::register; warnings::register->import} ) { 7*0Sstevel@tonic-gate *warnings::warnif = sub { 8*0Sstevel@tonic-gate require Carp; 9*0Sstevel@tonic-gate Carp::carp(@_); 10*0Sstevel@tonic-gate } 11*0Sstevel@tonic-gate} 12*0Sstevel@tonic-gateuse vars qw(%attr $VERSION); 13*0Sstevel@tonic-gate 14*0Sstevel@tonic-gate$VERSION = '2.03'; 15*0Sstevel@tonic-gate 16*0Sstevel@tonic-gate# constant.pm is slow 17*0Sstevel@tonic-gatesub PUBLIC () { 2**0 } 18*0Sstevel@tonic-gatesub PRIVATE () { 2**1 } 19*0Sstevel@tonic-gatesub INHERITED () { 2**2 } 20*0Sstevel@tonic-gatesub PROTECTED () { 2**3 } 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gate 23*0Sstevel@tonic-gate# The %attr hash holds the attributes of the currently assigned fields 24*0Sstevel@tonic-gate# per class. The hash is indexed by class names and the hash value is 25*0Sstevel@tonic-gate# an array reference. The first element in the array is the lowest field 26*0Sstevel@tonic-gate# number not belonging to a base class. The remaining elements' indices 27*0Sstevel@tonic-gate# are the field numbers. The values are integer bit masks, or undef 28*0Sstevel@tonic-gate# in the case of base class private fields (which occupy a slot but are 29*0Sstevel@tonic-gate# otherwise irrelevant to the class). 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gatesub import { 32*0Sstevel@tonic-gate my $class = shift; 33*0Sstevel@tonic-gate return unless @_; 34*0Sstevel@tonic-gate my $package = caller(0); 35*0Sstevel@tonic-gate # avoid possible typo warnings 36*0Sstevel@tonic-gate %{"$package\::FIELDS"} = () unless %{"$package\::FIELDS"}; 37*0Sstevel@tonic-gate my $fields = \%{"$package\::FIELDS"}; 38*0Sstevel@tonic-gate my $fattr = ($attr{$package} ||= [1]); 39*0Sstevel@tonic-gate my $next = @$fattr; 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate # Quiet pseudo-hash deprecation warning for uses of fields::new. 42*0Sstevel@tonic-gate bless \%{"$package\::FIELDS"}, 'pseudohash'; 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate if ($next > $fattr->[0] 45*0Sstevel@tonic-gate and ($fields->{$_[0]} || 0) >= $fattr->[0]) 46*0Sstevel@tonic-gate { 47*0Sstevel@tonic-gate # There are already fields not belonging to base classes. 48*0Sstevel@tonic-gate # Looks like a possible module reload... 49*0Sstevel@tonic-gate $next = $fattr->[0]; 50*0Sstevel@tonic-gate } 51*0Sstevel@tonic-gate foreach my $f (@_) { 52*0Sstevel@tonic-gate my $fno = $fields->{$f}; 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gate # Allow the module to be reloaded so long as field positions 55*0Sstevel@tonic-gate # have not changed. 56*0Sstevel@tonic-gate if ($fno and $fno != $next) { 57*0Sstevel@tonic-gate require Carp; 58*0Sstevel@tonic-gate if ($fno < $fattr->[0]) { 59*0Sstevel@tonic-gate if ($] < 5.006001) { 60*0Sstevel@tonic-gate warn("Hides field '$f' in base class") if $^W; 61*0Sstevel@tonic-gate } else { 62*0Sstevel@tonic-gate warnings::warnif("Hides field '$f' in base class") ; 63*0Sstevel@tonic-gate } 64*0Sstevel@tonic-gate } else { 65*0Sstevel@tonic-gate Carp::croak("Field name '$f' already in use"); 66*0Sstevel@tonic-gate } 67*0Sstevel@tonic-gate } 68*0Sstevel@tonic-gate $fields->{$f} = $next; 69*0Sstevel@tonic-gate $fattr->[$next] = ($f =~ /^_/) ? PRIVATE : PUBLIC; 70*0Sstevel@tonic-gate $next += 1; 71*0Sstevel@tonic-gate } 72*0Sstevel@tonic-gate if (@$fattr > $next) { 73*0Sstevel@tonic-gate # Well, we gave them the benefit of the doubt by guessing the 74*0Sstevel@tonic-gate # module was reloaded, but they appear to be declaring fields 75*0Sstevel@tonic-gate # in more than one place. We can't be sure (without some extra 76*0Sstevel@tonic-gate # bookkeeping) that the rest of the fields will be declared or 77*0Sstevel@tonic-gate # have the same positions, so punt. 78*0Sstevel@tonic-gate require Carp; 79*0Sstevel@tonic-gate Carp::croak ("Reloaded module must declare all fields at once"); 80*0Sstevel@tonic-gate } 81*0Sstevel@tonic-gate} 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gatesub inherit { 84*0Sstevel@tonic-gate require base; 85*0Sstevel@tonic-gate goto &base::inherit_fields; 86*0Sstevel@tonic-gate} 87*0Sstevel@tonic-gate 88*0Sstevel@tonic-gatesub _dump # sometimes useful for debugging 89*0Sstevel@tonic-gate{ 90*0Sstevel@tonic-gate for my $pkg (sort keys %attr) { 91*0Sstevel@tonic-gate print "\n$pkg"; 92*0Sstevel@tonic-gate if (@{"$pkg\::ISA"}) { 93*0Sstevel@tonic-gate print " (", join(", ", @{"$pkg\::ISA"}), ")"; 94*0Sstevel@tonic-gate } 95*0Sstevel@tonic-gate print "\n"; 96*0Sstevel@tonic-gate my $fields = \%{"$pkg\::FIELDS"}; 97*0Sstevel@tonic-gate for my $f (sort {$fields->{$a} <=> $fields->{$b}} keys %$fields) { 98*0Sstevel@tonic-gate my $no = $fields->{$f}; 99*0Sstevel@tonic-gate print " $no: $f"; 100*0Sstevel@tonic-gate my $fattr = $attr{$pkg}[$no]; 101*0Sstevel@tonic-gate if (defined $fattr) { 102*0Sstevel@tonic-gate my @a; 103*0Sstevel@tonic-gate push(@a, "public") if $fattr & PUBLIC; 104*0Sstevel@tonic-gate push(@a, "private") if $fattr & PRIVATE; 105*0Sstevel@tonic-gate push(@a, "inherited") if $fattr & INHERITED; 106*0Sstevel@tonic-gate print "\t(", join(", ", @a), ")"; 107*0Sstevel@tonic-gate } 108*0Sstevel@tonic-gate print "\n"; 109*0Sstevel@tonic-gate } 110*0Sstevel@tonic-gate } 111*0Sstevel@tonic-gate} 112*0Sstevel@tonic-gate 113*0Sstevel@tonic-gateif ($] < 5.009) { 114*0Sstevel@tonic-gate *new = sub { 115*0Sstevel@tonic-gate my $class = shift; 116*0Sstevel@tonic-gate $class = ref $class if ref $class; 117*0Sstevel@tonic-gate return bless [\%{$class . "::FIELDS"}], $class; 118*0Sstevel@tonic-gate } 119*0Sstevel@tonic-gate} else { 120*0Sstevel@tonic-gate *new = sub { 121*0Sstevel@tonic-gate my $class = shift; 122*0Sstevel@tonic-gate $class = ref $class if ref $class; 123*0Sstevel@tonic-gate require Hash::Util; 124*0Sstevel@tonic-gate my $self = bless {}, $class; 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate # The lock_keys() prototype won't work since we require Hash::Util :( 127*0Sstevel@tonic-gate &Hash::Util::lock_keys(\%$self, keys %{$class.'::FIELDS'}); 128*0Sstevel@tonic-gate return $self; 129*0Sstevel@tonic-gate } 130*0Sstevel@tonic-gate} 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gatesub phash { 133*0Sstevel@tonic-gate die "Pseudo-hashes have been removed from Perl" if $] >= 5.009; 134*0Sstevel@tonic-gate my $h; 135*0Sstevel@tonic-gate my $v; 136*0Sstevel@tonic-gate if (@_) { 137*0Sstevel@tonic-gate if (ref $_[0] eq 'ARRAY') { 138*0Sstevel@tonic-gate my $a = shift; 139*0Sstevel@tonic-gate @$h{@$a} = 1 .. @$a; 140*0Sstevel@tonic-gate if (@_) { 141*0Sstevel@tonic-gate $v = shift; 142*0Sstevel@tonic-gate unless (! @_ and ref $v eq 'ARRAY') { 143*0Sstevel@tonic-gate require Carp; 144*0Sstevel@tonic-gate Carp::croak ("Expected at most two array refs\n"); 145*0Sstevel@tonic-gate } 146*0Sstevel@tonic-gate } 147*0Sstevel@tonic-gate } 148*0Sstevel@tonic-gate else { 149*0Sstevel@tonic-gate if (@_ % 2) { 150*0Sstevel@tonic-gate require Carp; 151*0Sstevel@tonic-gate Carp::croak ("Odd number of elements initializing pseudo-hash\n"); 152*0Sstevel@tonic-gate } 153*0Sstevel@tonic-gate my $i = 0; 154*0Sstevel@tonic-gate @$h{grep ++$i % 2, @_} = 1 .. @_ / 2; 155*0Sstevel@tonic-gate $i = 0; 156*0Sstevel@tonic-gate $v = [grep $i++ % 2, @_]; 157*0Sstevel@tonic-gate } 158*0Sstevel@tonic-gate } 159*0Sstevel@tonic-gate else { 160*0Sstevel@tonic-gate $h = {}; 161*0Sstevel@tonic-gate $v = []; 162*0Sstevel@tonic-gate } 163*0Sstevel@tonic-gate [ $h, @$v ]; 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate} 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate1; 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate__END__ 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gate=head1 NAME 172*0Sstevel@tonic-gate 173*0Sstevel@tonic-gatefields - compile-time class fields 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate=head1 SYNOPSIS 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate { 178*0Sstevel@tonic-gate package Foo; 179*0Sstevel@tonic-gate use fields qw(foo bar _Foo_private); 180*0Sstevel@tonic-gate sub new { 181*0Sstevel@tonic-gate my Foo $self = shift; 182*0Sstevel@tonic-gate unless (ref $self) { 183*0Sstevel@tonic-gate $self = fields::new($self); 184*0Sstevel@tonic-gate $self->{_Foo_private} = "this is Foo's secret"; 185*0Sstevel@tonic-gate } 186*0Sstevel@tonic-gate $self->{foo} = 10; 187*0Sstevel@tonic-gate $self->{bar} = 20; 188*0Sstevel@tonic-gate return $self; 189*0Sstevel@tonic-gate } 190*0Sstevel@tonic-gate } 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate my $var = Foo->new; 193*0Sstevel@tonic-gate $var->{foo} = 42; 194*0Sstevel@tonic-gate 195*0Sstevel@tonic-gate # this will generate an error 196*0Sstevel@tonic-gate $var->{zap} = 42; 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate # subclassing 199*0Sstevel@tonic-gate { 200*0Sstevel@tonic-gate package Bar; 201*0Sstevel@tonic-gate use base 'Foo'; 202*0Sstevel@tonic-gate use fields qw(baz _Bar_private); # not shared with Foo 203*0Sstevel@tonic-gate sub new { 204*0Sstevel@tonic-gate my $class = shift; 205*0Sstevel@tonic-gate my $self = fields::new($class); 206*0Sstevel@tonic-gate $self->SUPER::new(); # init base fields 207*0Sstevel@tonic-gate $self->{baz} = 10; # init own fields 208*0Sstevel@tonic-gate $self->{_Bar_private} = "this is Bar's secret"; 209*0Sstevel@tonic-gate return $self; 210*0Sstevel@tonic-gate } 211*0Sstevel@tonic-gate } 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate=head1 DESCRIPTION 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gateThe C<fields> pragma enables compile-time verified class fields. 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gateNOTE: The current implementation keeps the declared fields in the %FIELDS 218*0Sstevel@tonic-gatehash of the calling package, but this may change in future versions. 219*0Sstevel@tonic-gateDo B<not> update the %FIELDS hash directly, because it must be created 220*0Sstevel@tonic-gateat compile-time for it to be fully useful, as is done by this pragma. 221*0Sstevel@tonic-gate 222*0Sstevel@tonic-gateB<Only valid for perl before 5.9.0:> 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gateIf a typed lexical variable holding a reference is used to access a 225*0Sstevel@tonic-gatehash element and a package with the same name as the type has 226*0Sstevel@tonic-gatedeclared class fields using this pragma, then the operation is 227*0Sstevel@tonic-gateturned into an array access at compile time. 228*0Sstevel@tonic-gate 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gateThe related C<base> pragma will combine fields from base classes and any 231*0Sstevel@tonic-gatefields declared using the C<fields> pragma. This enables field 232*0Sstevel@tonic-gateinheritance to work properly. 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gateField names that start with an underscore character are made private to 235*0Sstevel@tonic-gatethe class and are not visible to subclasses. Inherited fields can be 236*0Sstevel@tonic-gateoverridden but will generate a warning if used together with the C<-w> 237*0Sstevel@tonic-gateswitch. 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gateB<Only valid for perls before 5.9.0:> 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gateThe effect of all this is that you can have objects with named 242*0Sstevel@tonic-gatefields which are as compact and as fast arrays to access. This only 243*0Sstevel@tonic-gateworks as long as the objects are accessed through properly typed 244*0Sstevel@tonic-gatevariables. If the objects are not typed, access is only checked at 245*0Sstevel@tonic-gaterun time. 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate 248*0Sstevel@tonic-gateThe following functions are supported: 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gate=over 4 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate=item new 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gateB< perl before 5.9.0: > fields::new() creates and blesses a 255*0Sstevel@tonic-gatepseudo-hash comprised of the fields declared using the C<fields> 256*0Sstevel@tonic-gatepragma into the specified class. 257*0Sstevel@tonic-gate 258*0Sstevel@tonic-gateB< perl 5.9.0 and higher: > fields::new() creates and blesses a 259*0Sstevel@tonic-gaterestricted-hash comprised of the fields declared using the C<fields> 260*0Sstevel@tonic-gatepragma into the specified class. 261*0Sstevel@tonic-gate 262*0Sstevel@tonic-gateThis function is usable with or without pseudo-hashes. It is the 263*0Sstevel@tonic-gaterecommended way to construct a fields-based object. 264*0Sstevel@tonic-gate 265*0Sstevel@tonic-gateThis makes it possible to write a constructor like this: 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate package Critter::Sounds; 268*0Sstevel@tonic-gate use fields qw(cat dog bird); 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate sub new { 271*0Sstevel@tonic-gate my $self = shift; 272*0Sstevel@tonic-gate $self = fields::new($self) unless ref $self; 273*0Sstevel@tonic-gate $self->{cat} = 'meow'; # scalar element 274*0Sstevel@tonic-gate @$self{'dog','bird'} = ('bark','tweet'); # slice 275*0Sstevel@tonic-gate return $self; 276*0Sstevel@tonic-gate } 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate=item phash 279*0Sstevel@tonic-gate 280*0Sstevel@tonic-gateB< before perl 5.9.0: > 281*0Sstevel@tonic-gate 282*0Sstevel@tonic-gatefields::phash() can be used to create and initialize a plain (unblessed) 283*0Sstevel@tonic-gatepseudo-hash. This function should always be used instead of creating 284*0Sstevel@tonic-gatepseudo-hashes directly. 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gateIf the first argument is a reference to an array, the pseudo-hash will 287*0Sstevel@tonic-gatebe created with keys from that array. If a second argument is supplied, 288*0Sstevel@tonic-gateit must also be a reference to an array whose elements will be used as 289*0Sstevel@tonic-gatethe values. If the second array contains less elements than the first, 290*0Sstevel@tonic-gatethe trailing elements of the pseudo-hash will not be initialized. 291*0Sstevel@tonic-gateThis makes it particularly useful for creating a pseudo-hash from 292*0Sstevel@tonic-gatesubroutine arguments: 293*0Sstevel@tonic-gate 294*0Sstevel@tonic-gate sub dogtag { 295*0Sstevel@tonic-gate my $tag = fields::phash([qw(name rank ser_num)], [@_]); 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gatefields::phash() also accepts a list of key-value pairs that will 299*0Sstevel@tonic-gatebe used to construct the pseudo hash. Examples: 300*0Sstevel@tonic-gate 301*0Sstevel@tonic-gate my $tag = fields::phash(name => "Joe", 302*0Sstevel@tonic-gate rank => "captain", 303*0Sstevel@tonic-gate ser_num => 42); 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate my $pseudohash = fields::phash(%args); 306*0Sstevel@tonic-gate 307*0Sstevel@tonic-gateB< perl 5.9.0 and higher: > 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gatePseudo-hashes have been removed from Perl as of 5.10. Consider using 310*0Sstevel@tonic-gaterestricted hashes or fields::new() instead. Using fields::phash() 311*0Sstevel@tonic-gatewill cause an error. 312*0Sstevel@tonic-gate 313*0Sstevel@tonic-gate=back 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate=head1 SEE ALSO 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gateL<base> 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate=cut 320