1package base; 2 3use strict 'vars'; 4use vars qw($VERSION); 5$VERSION = '2.05'; 6 7# constant.pm is slow 8sub SUCCESS () { 1 } 9 10sub PUBLIC () { 2**0 } 11sub PRIVATE () { 2**1 } 12sub INHERITED () { 2**2 } 13sub PROTECTED () { 2**3 } 14 15 16my $Fattr = \%fields::attr; 17 18sub has_fields { 19 my($base) = shift; 20 my $fglob = ${"$base\::"}{FIELDS}; 21 return( ($fglob && *$fglob{HASH}) ? 1 : 0 ); 22} 23 24sub has_version { 25 my($base) = shift; 26 my $vglob = ${$base.'::'}{VERSION}; 27 return( ($vglob && *$vglob{SCALAR}) ? 1 : 0 ); 28} 29 30sub has_attr { 31 my($proto) = shift; 32 my($class) = ref $proto || $proto; 33 return exists $Fattr->{$class}; 34} 35 36sub get_attr { 37 $Fattr->{$_[0]} = [1] unless $Fattr->{$_[0]}; 38 return $Fattr->{$_[0]}; 39} 40 41if ($] < 5.009) { 42 *get_fields = sub { 43 # Shut up a possible typo warning. 44 () = \%{$_[0].'::FIELDS'}; 45 my $f = \%{$_[0].'::FIELDS'}; 46 47 # should be centralized in fields? perhaps 48 # fields::mk_FIELDS_be_OK. Peh. As long as %{ $package . '::FIELDS' } 49 # is used here anyway, it doesn't matter. 50 bless $f, 'pseudohash' if (ref($f) ne 'pseudohash'); 51 52 return $f; 53 } 54} 55else { 56 *get_fields = sub { 57 # Shut up a possible typo warning. 58 () = \%{$_[0].'::FIELDS'}; 59 return \%{$_[0].'::FIELDS'}; 60 } 61} 62 63sub import { 64 my $class = shift; 65 66 return SUCCESS unless @_; 67 68 # List of base classes from which we will inherit %FIELDS. 69 my $fields_base; 70 71 my $inheritor = caller(0); 72 73 foreach my $base (@_) { 74 next if $inheritor->isa($base); 75 76 if (has_version($base)) { 77 ${$base.'::VERSION'} = '-1, set by base.pm' 78 unless defined ${$base.'::VERSION'}; 79 } 80 else { 81 local $SIG{__DIE__} = 'IGNORE'; 82 eval "require $base"; 83 # Only ignore "Can't locate" errors from our eval require. 84 # Other fatal errors (syntax etc) must be reported. 85 die if $@ && $@ !~ /^Can't locate .*? at \(eval /; 86 unless (%{"$base\::"}) { 87 require Carp; 88 Carp::croak(<<ERROR); 89Base class package "$base" is empty. 90 (Perhaps you need to 'use' the module which defines that package first.) 91ERROR 92 93 } 94 ${$base.'::VERSION'} = "-1, set by base.pm" 95 unless defined ${$base.'::VERSION'}; 96 } 97 push @{"$inheritor\::ISA"}, $base; 98 99 if ( has_fields($base) || has_attr($base) ) { 100 # No multiple fields inheritence *suck* 101 if ($fields_base) { 102 require Carp; 103 Carp::croak("Can't multiply inherit %FIELDS"); 104 } else { 105 $fields_base = $base; 106 } 107 } 108 } 109 110 if( defined $fields_base ) { 111 inherit_fields($inheritor, $fields_base); 112 } 113} 114 115 116sub inherit_fields { 117 my($derived, $base) = @_; 118 119 return SUCCESS unless $base; 120 121 my $battr = get_attr($base); 122 my $dattr = get_attr($derived); 123 my $dfields = get_fields($derived); 124 my $bfields = get_fields($base); 125 126 $dattr->[0] = @$battr; 127 128 if( keys %$dfields ) { 129 warn "$derived is inheriting from $base but already has its own ". 130 "fields!\n". 131 "This will cause problems.\n". 132 "Be sure you use base BEFORE declaring fields\n"; 133 } 134 135 # Iterate through the base's fields adding all the non-private 136 # ones to the derived class. Hang on to the original attribute 137 # (Public, Private, etc...) and add Inherited. 138 # This is all too complicated to do efficiently with add_fields(). 139 while (my($k,$v) = each %$bfields) { 140 my $fno; 141 if ($fno = $dfields->{$k} and $fno != $v) { 142 require Carp; 143 Carp::croak ("Inherited %FIELDS can't override existing %FIELDS"); 144 } 145 146 if( $battr->[$v] & PRIVATE ) { 147 $dattr->[$v] = PRIVATE | INHERITED; 148 } 149 else { 150 $dattr->[$v] = INHERITED | $battr->[$v]; 151 $dfields->{$k} = $v; 152 } 153 } 154 155 unless( keys %$bfields ) { 156 foreach my $idx (1..$#{$battr}) { 157 $dattr->[$idx] = $battr->[$idx] & INHERITED; 158 } 159 } 160} 161 162 1631; 164 165__END__ 166 167=head1 NAME 168 169base - Establish IS-A relationship with base classes at compile time 170 171=head1 SYNOPSIS 172 173 package Baz; 174 use base qw(Foo Bar); 175 176=head1 DESCRIPTION 177 178Allows you to both load one or more modules, while setting up inheritance from 179those modules at the same time. Roughly similar in effect to 180 181 package Baz; 182 BEGIN { 183 require Foo; 184 require Bar; 185 push @ISA, qw(Foo Bar); 186 } 187 188If any of the listed modules are not loaded yet, I<base> silently attempts to 189C<require> them (and silently continues if the C<require> failed). Whether to 190C<require> a base class module is determined by the absence of a global variable 191$VERSION in the base package. If $VERSION is not detected even after loading 192it, <base> will define $VERSION in the base package, setting it to the string 193C<-1, set by base.pm>. 194 195Will also initialize the fields if one of the base classes has it. 196Multiple inheritence of fields is B<NOT> supported, if two or more 197base classes each have inheritable fields the 'base' pragma will 198croak. See L<fields>, L<public> and L<protected> for a description of 199this feature. 200 201=head1 HISTORY 202 203This module was introduced with Perl 5.004_04. 204 205 206=head1 CAVEATS 207 208Due to the limitations of the implementation, you must use 209base I<before> you declare any of your own fields. 210 211 212=head1 SEE ALSO 213 214L<fields> 215 216=cut 217