creating named subroutines in a loop

P

petercable

Howdy all!

Currently I have some code which looks something like this:

sub varname {
my $self = shift;
if (@_) { $self->{-VARNAME} = shift }
return $self->{-VARNAME};
}

and

sub varname2 {
my $self = shift;
if (@_) { $self->{-VARNAME2} = shift }
if (ref($self->{-VARNAME2})) {
return ${$self->{-VARNAME2}};
} else {
return $self->{-VARNAME};
}
}

The problem I have is that I have several variables that fit each type
and I currently have one subroutine for each of the variables. I'm
fairly certain I should be able to generate these subroutines
dynamically with a couple of foreach loops, but I'm unsure how to go
about it.

Any advice?

Pete
 
J

J. Gleixner

Howdy all!

Currently I have some code which looks something like this:

sub varname {
my $self = shift;
if (@_) { $self->{-VARNAME} = shift }
return $self->{-VARNAME};
}

and

sub varname2 {
my $self = shift;
if (@_) { $self->{-VARNAME2} = shift }
if (ref($self->{-VARNAME2})) {
return ${$self->{-VARNAME2}};
} else {
return $self->{-VARNAME};
}
}

The problem I have is that I have several variables that fit each type
and I currently have one subroutine for each of the variables. I'm
fairly certain I should be able to generate these subroutines
dynamically with a couple of foreach loops, but I'm unsure how to go
about it.

Any advice?

perldoc AutoLoader

To find your favorite search engine and search for "perl autoloader".
 
P

petercable

J. Gleixner said:
perldoc AutoLoader

To find your favorite search engine and search for "perl autoloader".

After looking over Autoloader, it seems like this would have the
opposite effect of my intended goal. I was looking to move the
generation of the subroutines into a pair of loops for two reasons.
First, repeating nearly exact code over and over again seems like
asking for trouble, both from a debugging and a reuse standpoint, and
secondly if I were able to iterate across a list of variable names,
adding more variables or changing the internal logic of every
setter/getter subroutine would be trivial.

I may have misunderstood Autoloader, but it seems like I would still
have to define each subroutine individually, then run autosplit to
create a bunch of .al files containing the pre-declarations. This seems
like I am getting more complicated, rather than less.

I'm also confused by this:

"If any module's own AUTOLOAD subroutine has no need to fallback to the
AutoLoader's AUTOLOAD subroutine (because it doesn't have any AutoSplit
subroutines), then that module should not use AutoLoader at all."

I really don't need to load these subs dynamically, as I intend to call
every one of them, so I'm not sure how I would go about doing this with
Autoload.

What I did notice that caught my eye was the example AUTOLOAD sub,
which has some sort of glob magic going on:

*$AUTOLOAD = sub { $val }; # same as: eval "sub $AUTOLOAD { $val }"
goto &$AUTOLOAD;

which seems like it might do something similar to what I am trying to
accomplish, but I have exactly _zero_ experience with globs.

I was thinking I might be able to do something like this, but I'd hate
to make it into the SAQ's.... :)

my @regvars = qw/var1 var2 var3/;
my @derefvars = qw/var4 var5 var6/;

foreach my $var (@regvars) {
sub $var {
my $self = shift;
if (@_) { $self->{uc("-".$var)} = shift }
return $self->{uc("-".$var)};
}
}
foreach my $var (@derefvars) {
sub $var {
my $self = shift;
if (@_) { $self->{uc("-".$var)} = shift }
if (ref($self->{uc("-".$var)}) {
return ${$self->{uc("-".$var)}};
} else {
return $self->{uc("-".$var)}
}
}
}

But, I don't have access to my code, nor access to the Internet where
my code is, so I can't just try it out (short of writing another short
little test app.) My goal, however, wasn't just to get it to work, it
works now and I could probably get it to work with some sort of looping
structure, but I'd rather do it _right_!!! This is my first attempt at
dabbling with OO programming in perl and I'm basically just working
from perltoot at this point.

So, is there a correct/less dangerous/more efficient etc. way of doing
this. I'm sure someone out there can put me in my place/point me in the
right direction and I'll probably slap myself for not seeing it in the
first place.

Thanks in advance!!!

Pete
 
T

Todd W

What I did notice that caught my eye was the example AUTOLOAD sub,
which has some sort of glob magic going on:

*$AUTOLOAD = sub { $val }; # same as: eval "sub $AUTOLOAD { $val }"
goto &$AUTOLOAD;

which seems like it might do something similar to what I am trying to
accomplish, but I have exactly _zero_ experience with globs.

I was thinking I might be able to do something like this, but I'd hate
to make it into the SAQ's.... :)

my @regvars = qw/var1 var2 var3/;
my @derefvars = qw/var4 var5 var6/;

foreach my $var (@regvars) {
sub $var {
my $self = shift;
if (@_) { $self->{uc("-".$var)} = shift }
return $self->{uc("-".$var)};
}
}
foreach my $var (@derefvars) {
sub $var {
my $self = shift;
if (@_) { $self->{uc("-".$var)} = shift }
if (ref($self->{uc("-".$var)}) {
return ${$self->{uc("-".$var)}};
} else {
return $self->{uc("-".$var)}
}
}
}

Pretty close. Here is what I use:

[root@waveright root]# perl
use warnings;
use strict;

package MyClass;

{
no strict 'refs';
foreach my $method ( qw| foo bar bazz | ) {
*$method = sub {
my $self = shift;
$self->{$method} = shift if ( @_ );
$self->{$method};
}
}
}

package main;

my $MyObject = bless( { }, 'MyClass' );

$MyObject->foo( 'data1' );
$MyObject->bar( 'data2' );

print( 'foo is: ', $MyObject->foo, "\n" );
Ctrl-D
foo is: data1

Enjoy,

Todd W.
 
P

petercable

Todd said:
Pretty close. Here is what I use:

[root@waveright root]# perl
use warnings;
use strict;

package MyClass;

{
no strict 'refs';
foreach my $method ( qw| foo bar bazz | ) {
*$method = sub {
my $self = shift;
$self->{$method} = shift if ( @_ );
$self->{$method};
}
}
}

package main;

my $MyObject = bless( { }, 'MyClass' );

$MyObject->foo( 'data1' );
$MyObject->bar( 'data2' );

print( 'foo is: ', $MyObject->foo, "\n" );
Ctrl-D
foo is: data1

Enjoy,

Todd W.

Thanks, I'll give it a shot!!!

Pete
 
A

A. Sinan Unur

sub varname {
my $self = shift;
if (@_) { $self->{-VARNAME} = shift }
return $self->{-VARNAME};
}

While it is certainly done quite often, I am now convinced that
combining get/set in this way is not a good idea. I had always
felt uneasy about the style, and reading the discussion in
"Perl Best Practices" is what finally convinced me. The basic
argument is that code is no longer self documenting.

....
The problem I have is that I have several variables that fit each type
and I currently have one subroutine for each of the variables. I'm
fairly certain I should be able to generate these subroutines
dynamically with a couple of foreach loops, but I'm unsure how to go
about it.

I am going to recommend taking a look at Class::Std. No, it very
likely does not provide a performance advantage, but it makes it
possible to write:

#!/usr/bin/perl

package Person;

use strict;
use warnings;

use Class::Std;

{
my %fname :ATTR( :init_arg<first_name> :get<first_name> :set<first_name> );
my %lname :ATTR( :init_arg<last_name> :get<last_name> :set<last_name> );
}

package main;

use strict;
use warnings;

my $self = Person->new( {
first_name => 'Ali',
last_name => 'Unur',
} );

printf "%s, %s\n", $self->get_last_name, $self->get_first_name;

__END__
 
C

Charlton Wilbur

JG> perldoc AutoLoader

JG> To find your favorite search engine and search for "perl
JG> autoloader".

Alternately, as it looks like you're writing accessors, there are
Class::Accessor and Class::Accessor::Chained.

Charlton
 
P

P Cable

A. Sinan Unur suggested:
taking a look at Class::Std

and

Charlton said:
Alternately, as it looks like you're writing accessors, there are
Class::Accessor and Class::Accessor::Chained.

along with Todd W's advice of:
{
no strict 'refs';
foreach my $method ( qw| foo bar bazz | ) {
*$method = sub {
my $self = shift;
$self->{$method} = shift if ( @_ );
$self->{$method};
}
}
}

Thanks everyone who responded. I ended up using the glob/anonymous
subroutine method Todd suggested, as it best fits the code I had
already written. I also forgot to mention in my original post that I am
stuck with perl 5.003 and only those modules included in the standard
distribution and Tk. The various Class::* modules seem very useful
indeed and hopefully one day the powers that be will deem me worthy to
get access to CPAN and/or a more recent interpreter.

I'm glad to hear another good opinion on "Perl Best Practices" as I am
planning on picking it up. I really don't need the set/mutator portion
of the subroutine, as I normally call a ->configure() to set values, so
I'll definitely look at removing it.

Thanks again for the helpful responses (and for not making me feel like
an idiot!)

Pete
 
E

Eric Bohlman

already written. I also forgot to mention in my original post that I am
stuck with perl 5.003 and only those modules included in the standard
distribution and Tk. The various Class::* modules seem very useful
indeed and hopefully one day the powers that be will deem me worthy to
get access to CPAN and/or a more recent interpreter.

For the latter, it might help to inform the powers that be that version
5.003 was released *before* perl's developers undertook a major effort to
eliminate possible buffer-overrun risks.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,754
Messages
2,569,527
Members
44,999
Latest member
MakersCBDGummiesReview

Latest Threads

Top