📄 struct.pm
字号:
# autoconf -- create `configure' using m4 macros# Copyright 2001 Free Software Foundation, Inc.# This program is free software; you can redistribute it and/or modify# it under the terms of the GNU General Public License as published by# the Free Software Foundation; either version 2, or (at your option)# any later version.# This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.# You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA# 02111-1307, USA.# This file is basically Perl 5.6's Class::Struct, but made compatible# with Perl 5.5. If someday this has to be updated, be sure to rename# all the occurrences of Class::Struct into Automake::Struct, otherwise# if we `use' a Perl module (e.g., File::stat) that uses Class::Struct,# we would have two packages defining the same symbols. Boom.package Automake::Struct;## See POD after __END__use 5.005;use strict;use vars qw(@ISA @EXPORT $VERSION);use Carp;require Exporter;@ISA = qw(Exporter);@EXPORT = qw(struct);$VERSION = '0.58';## Tested on 5.002 and 5.003 without class membership tests:my $CHECK_CLASS_MEMBERSHIP = ($] >= 5.003_95);my $print = 0;sub printem { if (@_) { $print = shift } else { $print++ }}{ package Automake::Struct::Tie_ISA; sub TIEARRAY { my $class = shift; return bless [], $class; } sub STORE { my ($self, $index, $value) = @_; Automake::Struct::_subclass_error(); } sub FETCH { my ($self, $index) = @_; $self->[$index]; } sub FETCHSIZE { my $self = shift; return scalar(@$self); } sub DESTROY { }}sub struct { # Determine parameter list structure, one of: # struct( class => [ element-list ]) # struct( class => { element-list }) # struct( element-list ) # Latter form assumes current package name as struct name. my ($class, @decls); my $base_type = ref $_[1]; if ( $base_type eq 'HASH' ) { $class = shift; @decls = %{shift()}; _usage_error() if @_; } elsif ( $base_type eq 'ARRAY' ) { $class = shift; @decls = @{shift()}; _usage_error() if @_; } else { $base_type = 'ARRAY'; $class = (caller())[0]; @decls = @_; } _usage_error() if @decls % 2 == 1; # Ensure we are not, and will not be, a subclass. my $isa = do { no strict 'refs'; \@{$class . '::ISA'}; }; _subclass_error() if @$isa; tie @$isa, 'Automake::Struct::Tie_ISA'; # Create constructor. croak "function 'new' already defined in package $class" if do { no strict 'refs'; defined &{$class . "::new"} }; my @methods = (); my %refs = (); my %arrays = (); my %hashes = (); my %classes = (); my $got_class = 0; my $out = ''; $out = "{\n package $class;\n use Carp;\n sub new {\n"; $out .= " my (\$class, \%init) = \@_;\n"; $out .= " \$class = __PACKAGE__ unless \@_;\n"; my $cnt = 0; my $idx = 0; my( $cmt, $name, $type, $elem ); if( $base_type eq 'HASH' ){ $out .= " my(\$r) = {};\n"; $cmt = ''; } elsif( $base_type eq 'ARRAY' ){ $out .= " my(\$r) = [];\n"; } while( $idx < @decls ){ $name = $decls[$idx]; $type = $decls[$idx+1]; push( @methods, $name ); if( $base_type eq 'HASH' ){ $elem = "{'${class}::$name'}"; } elsif( $base_type eq 'ARRAY' ){ $elem = "[$cnt]"; ++$cnt; $cmt = " # $name"; } if( $type =~ /^\*(.)/ ){ $refs{$name}++; $type = $1; } my $init = "defined(\$init{'$name'}) ? \$init{'$name'} :"; if( $type eq '@' ){ $out .= " croak 'Initializer for $name must be array reference'\n"; $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'ARRAY';\n"; $out .= " \$r->$elem = $init [];$cmt\n"; $arrays{$name}++; } elsif( $type eq '%' ){ $out .= " croak 'Initializer for $name must be hash reference'\n"; $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'HASH';\n"; $out .= " \$r->$elem = $init {};$cmt\n"; $hashes{$name}++; } elsif ( $type eq '$') { $out .= " \$r->$elem = $init undef;$cmt\n"; } elsif( $type =~ /^\w+(?:::\w+)*$/ ){ $init = "defined(\$init{'$name'}) ? \%{\$init{'$name'}} : ()"; $out .= " croak 'Initializer for $name must be hash reference'\n"; $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'HASH';\n"; $out .= " \$r->$elem = '${type}'->new($init);$cmt\n"; $classes{$name} = $type; $got_class = 1; } else{ croak "'$type' is not a valid struct element type"; } $idx += 2; } $out .= " bless \$r, \$class;\n }\n"; # Create accessor methods. my( $pre, $pst, $sel ); $cnt = 0; foreach $name (@methods){ if ( do { no strict 'refs'; defined &{$class . "::$name"} } ) { carp "function '$name' already defined, overrides struct accessor method"; } else { $pre = $pst = $cmt = $sel = ''; if( defined $refs{$name} ){ $pre = "\\("; $pst = ")"; $cmt = " # returns ref"; } $out .= " sub $name {$cmt\n my \$r = shift;\n"; if( $base_type eq 'ARRAY' ){ $elem = "[$cnt]"; ++$cnt; } elsif( $base_type eq 'HASH' ){ $elem = "{'${class}::$name'}"; } if( defined $arrays{$name} ){ $out .= " my \$i;\n"; $out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n"; $sel = "->[\$i]"; } elsif( defined $hashes{$name} ){ $out .= " my \$i;\n"; $out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n"; $sel = "->{\$i}"; } elsif( defined $classes{$name} ){ if ( $CHECK_CLASS_MEMBERSHIP ) { $out .= " croak '$name argument is wrong class' if \@_ && ! UNIVERSAL::isa(\$_[0], '$classes{$name}');\n"; } } $out .= " croak 'Too many args to $name' if \@_ > 1;\n"; $out .= " \@_ ? ($pre\$r->$elem$sel = shift$pst) : $pre\$r->$elem$sel$pst;\n"; $out .= " }\n"; } } $out .= "}\n1;\n"; print $out if $print; my $result = eval $out; carp $@ if $@;}sub _usage_error { confess "struct usage error";}sub _subclass_error { croak 'struct class cannot be a subclass (@ISA not allowed)';}1; # for require__END__=head1 NAMEAutomake::Struct - declare struct-like datatypes as Perl classes=head1 SYNOPSIS use Automake::Struct; # declare struct, based on array: struct( CLASS_NAME => [ ELEMENT_NAME => ELEMENT_TYPE, ... ]); # declare struct, based on hash: struct( CLASS_NAME => { ELEMENT_NAME => ELEMENT_TYPE, ... }); package CLASS_NAME; use Automake::Struct; # declare struct, based on array, implicit class name: struct( ELEMENT_NAME => ELEMENT_TYPE, ... ); package Myobj; use Automake::Struct; # declare struct with four types of elements: struct( s => '$', a => '@', h => '%', c => 'My_Other_Class' ); $obj = new Myobj; # constructor # scalar type accessor: $element_value = $obj->s; # element value $obj->s('new value'); # assign to element # array type accessor: $ary_ref = $obj->a; # reference to whole array $ary_element_value = $obj->a(2); # array element value $obj->a(2, 'new value'); # assign to array element # hash type accessor: $hash_ref = $obj->h; # reference to whole hash $hash_element_value = $obj->h('x'); # hash element value $obj->h('x', 'new value'); # assign to hash element # class type accessor: $element_value = $obj->c; # object reference $obj->c->method(...); # call method of object $obj->c(new My_Other_Class); # assign a new object=head1 DESCRIPTIONC<Automake::Struct> exports a single function, C<struct>.Given a list of element names and types, and optionallya class name, C<struct> creates a Perl 5 class that implementsa "struct-like" data structure.The new class is given a constructor method, C<new>, for creatingstruct objects.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -