xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/fields.pm (revision 0:68f95e015346)
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