#!/usr/bin/perl package Foo; use Class::FIOC; use strict; my $nextid=1; sub new { my $class = shift; my %args = @_; my $first = $args{first} || "unknown"; my $surname = $args{surname} || "unknown"; my $id = $nextid++; methods { fullname=>sub { return "$first $surname"; }, surname=>sub{ if (@_==2) { $surname = pop; } return $surname; }, first=>sub { if (@_==2) { $first=pop; } return $first; }, id=>sub{ return $id; }, debug=>sub{ print "First = $first\nSurname=$surname\nID=$id\n\n"; } }; } my $foo = new Foo(first=>"Fred",surname=>"Flintstone"); print $foo->first."\n"; print $foo->surname."\n"; print $foo->id."\n"; print $foo->fullname."\n"; $foo->debug; $foo = new Foo(first=>"Barney",surname=>"Rubble"); print $foo->first."\n"; print $foo->surname."\n"; print $foo->id."\n"; print $foo->fullname."\n"; $foo->debug; #### The "goto-&NAME" form is quite different from the other forms of "goto". In fact, it isn't a goto in the normal sense at all, and doesn't have the stigma associated with other gotos. #### package Class::FIOC; require Exporter; use strict; our @ISA = qw(Exporter); our @EXPORT = qw(methods); our %Classes; our $DEBUG=0; sub methods { my $class = caller(); my %method_map = %{+shift}; # any old ref will work, one day maybe the user can choose the ref my $self = bless [],$class; # see if we already have seen this class before if (my $method_names=$Classes{$class}) { while (my ($name,$ref)=each(%method_map)) { # validate reference refers to CODE if (ref($ref) ne "CODE") { die "values passed to 'methods' hash must be CODE references!\n"; } # validate that this is not a new method name # maybe new method names should be allowed? I don't know if (my $hash=$method_names->{$name}) { $hash->{$self}=$ref; }else { die "Cannot add new method '$name' to class $class!\n"; } } } else { my %method_names; while (my($name,$ref)=each(%method_map)) { my $public = $name=~s/^\+//; my %hash; # validate name is legal identifier if ($name!~/^[A-Za-z_]\w+/) { die "'$name' is not a valid identifier!\n"; } # validate reference refers to CODE if (ref($ref) ne "CODE") { die "values passed to 'methods' hash must be CODE references!\n"; } # create method my $eval = "sub $class\:\:$name { goto \$hash{\$_[0]}}\n"; eval $eval; # save the hash for later so we can store other instance methods in it $method_names{$name}=\%hash; $hash{$self}=$ref; } $Classes{$class}=\%method_names; no strict 'refs'; *{"$class\:\:DESTROY"} = sub { my $self = shift; while (my($name,$method)=each(%method_names)) { delete $method->{$self}; if ($DEBUG) { print "Destroying '$name' method for $self\n"; } } } } return $self; } 1;