Home > other >  how can I invoke a subroutine reference generated from a string in perl?
how can I invoke a subroutine reference generated from a string in perl?

Time:07-04

Several times in my code, I do the following.

  1. generate a path from the name of a subroutine
  2. invoke the subroutine and return a reference to what the subroutine created
  3. 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.

  1. Generate the ref to the sub
  2. save the ref to a new named variable
  3. 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' ];
}


  • Related