Several times in my code, I do the following.
- generate a path from the name of a subroutine
- invoke the subroutine and return a reference to what the subroutine created
- store the reference to the path.
I want to replace these blocks with a function, on the
DRY: Don't repeat yourself
principle.
Thanks to
Generating a subroutine reference from a string,
I know how to generate a ref to an arbitrary subroutine inside dontrepeatyourself
below.
But when I try to invoke the arbitrary subroutine, I get
Can't use string ("doggy") as a subroutine ref while "strict refs" in use
Here is my code. writeeverythingtwice
does the job but requires the user to type the sub name twice when invoking. What if the user fails to type the same name in both fields? Chaos and confusion and wasted time debugging.
Surely there is a way to invoke a subroutine inside dontrepeatyourself
?
#!/usr/bin/env perl
use strict; use warnings;
use Data::Dumper qw(Dumper); $Data::Dumper::Sortkeys = 1;
use feature 'say';
writeeverythingtwice(name=>'doggy',ref=>\&doggy,);
dontrepeatyourself(subname=>'doggy',);
sub dontrepeatyourself
{
use Cwd 'abs_path'; use Storable;
# https://stackoverflow.com/questions/30236740/generating-a-subroutine-reference-from-a-string
my$whoiam= (caller(0))[3]; say 'you are now inside ',$whoiam;
my$HOMEarg={@_};
say ref \&{ $HOMEarg->{subname} };
my $reftostore=\&{ $HOMEarg->{subname} }->(); # generates error
my$path=abs_path('.') . '/' . $HOMEarg->{subname} . '.perlstorable' ;
store($reftostore,$path);
say 'we have stored to ',$path;
}
sub writeeverythingtwice
{
use Cwd 'abs_path'; use Storable;
# https://stackoverflow.com/questions/30236740/generating-a-subroutine-reference-from-a-string
my$whoiam= (caller(0))[3]; say 'you are now inside ',$whoiam;
my$HOMEarg={@_};
my$path=abs_path('.') . '/' . $HOMEarg->{name} . '.perlstorable' ;
my$reftostore=$HOMEarg->{ref}->();
store($reftostore,$path);
say 'we have stored to ',$path;
}
sub doggy
{
my$whoiam= (caller(0))[3]; say 'you are now inside ',$whoiam;
return [ 'foo','bar' ];
}
CodePudding user response:
my $ref = do {
no strict qw( refs );
\&$name
};
$ref->()
That said, \&$name
is actually exempt from strictures, so you simply need
my $ref = \&$name;
$ref->()
What I might do:
my %dispatch =
map { $_ => \&$_ }
qw(
doggy
...
);
That allows you to perform some validation.
my $ref = $dispatch->{ $name }
or die( "..." );
$ref->()
But that's probably unnecessary unless doggy
comes from an external source.
CodePudding user response:
One answer, which requires an additional line of code, works as follows.
- Generate the ref to the sub
- save the ref to a new named variable
- invoke the sub from the new named variable.
Thus we have
#!/usr/bin/env perl
use strict; use warnings;
use Data::Dumper qw(Dumper); $Data::Dumper::Sortkeys = 1;
use feature 'say';
writeeverythingtwice(name=>'doggy',ref=>\&doggy,);
dontrepeatyourself(subname=>'doggy',);
sub dontrepeatyourself
{
use Cwd 'abs_path'; use Storable;
# https://stackoverflow.com/questions/30236740/generating-a-subroutine-reference-from-a-string
my$whoiam= (caller(0))[3]; say 'you are now inside ',$whoiam;
my$HOMEarg={@_};
my $subref=\&{ $HOMEarg->{subname} };
say $subref;
#my $reftostore=\&{ $HOMEarg->{subname} }->(); # generates error
my $reftostore=$subref->();
my$path=abs_path('.') . '/' . $HOMEarg->{subname} . '.perlstorable' ;
store($reftostore,$path);
say 'we have stored to ',$path;
}
sub writeeverythingtwice
{
use Cwd 'abs_path'; use Storable;
# https://stackoverflow.com/questions/30236740/generating-a-subroutine-reference-from-a-string
my$whoiam= (caller(0))[3]; say 'you are now inside ',$whoiam;
my$HOMEarg={@_};
my$path=abs_path('.') . '/' . $HOMEarg->{name} . '.perlstorable' ;
my$reftostore=$HOMEarg->{ref}->();
store($reftostore,$path);
say 'we have stored to ',$path;
}
sub doggy
{
my$whoiam= (caller(0))[3]; say 'you are now inside ',$whoiam;
return [ 'foo','bar' ];
}