Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

156 lines
4.4 KiB

  1. package fields;
  2. =head1 NAME
  3. fields - compile-time class fields
  4. =head1 SYNOPSIS
  5. {
  6. package Foo;
  7. use fields qw(foo bar _private);
  8. }
  9. ...
  10. my Foo $var = new Foo;
  11. $var->{foo} = 42;
  12. # This will generate a compile-time error.
  13. $var->{zap} = 42;
  14. {
  15. package Bar;
  16. use base 'Foo';
  17. use fields 'bar'; # hides Foo->{bar}
  18. use fields qw(baz _private); # not shared with Foo
  19. }
  20. =head1 DESCRIPTION
  21. The C<fields> pragma enables compile-time verified class fields. It
  22. does so by updating the %FIELDS hash in the calling package.
  23. If a typed lexical variable holding a reference is used to access a
  24. hash element and the %FIELDS hash of the given type exists, then the
  25. operation is turned into an array access at compile time. The %FIELDS
  26. hash maps from hash element names to the array indices. If the hash
  27. element is not present in the %FIELDS hash, then a compile-time error
  28. is signaled.
  29. Since the %FIELDS hash is used at compile-time, it must be set up at
  30. compile-time too. This is made easier with the help of the 'fields'
  31. and the 'base' pragma modules. The 'base' pragma will copy fields
  32. from base classes and the 'fields' pragma adds new fields. Field
  33. names that start with an underscore character are made private to a
  34. class and are not visible to subclasses. Inherited fields can be
  35. overridden but will generate a warning if used together with the C<-w>
  36. switch.
  37. The effect of all this is that you can have objects with named fields
  38. which are as compact and as fast arrays to access. This only works
  39. as long as the objects are accessed through properly typed variables.
  40. For untyped access to work you have to make sure that a reference to
  41. the proper %FIELDS hash is assigned to the 0'th element of the array
  42. object (so that the objects can be treated like an pseudo-hash). A
  43. constructor like this does the job:
  44. sub new
  45. {
  46. my $class = shift;
  47. no strict 'refs';
  48. my $self = bless [\%{"$class\::FIELDS"}], $class;
  49. $self;
  50. }
  51. =head1 SEE ALSO
  52. L<base>,
  53. L<perlref/Pseudo-hashes: Using an array as a hash>
  54. =cut
  55. use strict;
  56. no strict 'refs';
  57. use vars qw(%attr $VERSION);
  58. $VERSION = "0.02";
  59. # some constants
  60. sub _PUBLIC () { 1 }
  61. sub _PRIVATE () { 2 }
  62. sub _INHERITED () { 4 }
  63. # The %attr hash holds the attributes of the currently assigned fields
  64. # per class. The hash is indexed by class names and the hash value is
  65. # an array reference. The array is indexed with the field numbers
  66. # (minus one) and the values are integer bit masks (or undef). The
  67. # size of the array also indicate the next field index too assign for
  68. # additional fields in this class.
  69. sub import {
  70. my $class = shift;
  71. my $package = caller(0);
  72. my $fields = \%{"$package\::FIELDS"};
  73. my $fattr = ($attr{$package} ||= []);
  74. foreach my $f (@_) {
  75. if (my $fno = $fields->{$f}) {
  76. require Carp;
  77. if ($fattr->[$fno-1] & _INHERITED) {
  78. Carp::carp("Hides field '$f' in base class") if $^W;
  79. } else {
  80. Carp::croak("Field name '$f' already in use");
  81. }
  82. }
  83. $fields->{$f} = @$fattr + 1;
  84. push(@$fattr, ($f =~ /^_/) ? _PRIVATE : _PUBLIC);
  85. }
  86. }
  87. sub inherit # called by base.pm
  88. {
  89. my($derived, $base) = @_;
  90. if (defined %{"$derived\::FIELDS"}) {
  91. require Carp;
  92. Carp::croak("Inherited %FIELDS can't override existing %FIELDS");
  93. } else {
  94. my $base_fields = \%{"$base\::FIELDS"};
  95. my $derived_fields = \%{"$derived\::FIELDS"};
  96. $attr{$derived}[@{$attr{$base}}-1] = undef;
  97. while (my($k,$v) = each %$base_fields) {
  98. next if $attr{$base}[$v-1] & _PRIVATE;
  99. $attr{$derived}[$v-1] = _INHERITED;
  100. $derived_fields->{$k} = $v;
  101. }
  102. }
  103. }
  104. sub _dump # sometimes useful for debugging
  105. {
  106. for my $pkg (sort keys %attr) {
  107. print "\n$pkg";
  108. if (defined @{"$pkg\::ISA"}) {
  109. print " (", join(", ", @{"$pkg\::ISA"}), ")";
  110. }
  111. print "\n";
  112. my $fields = \%{"$pkg\::FIELDS"};
  113. for my $f (sort {$fields->{$a} <=> $fields->{$b}} keys %$fields) {
  114. my $no = $fields->{$f};
  115. print " $no: $f";
  116. my $fattr = $attr{$pkg}[$no-1];
  117. if (defined $fattr) {
  118. my @a;
  119. push(@a, "public") if $fattr & _PUBLIC;
  120. push(@a, "private") if $fattr & _PRIVATE;
  121. push(@a, "inherited") if $fattr & _INHERITED;
  122. print "\t(", join(", ", @a), ")";
  123. }
  124. print "\n";
  125. }
  126. }
  127. }
  128. 1;