Search code examples
perldynamicthissubroutine

dynamic call to subroutines in perl


I'm a bit messed up with the following:

I have a function that calls subroutines in the following way:

sub someFunction {
    my $self = shift;
    my $type = $self->{'type'};

    if($type eq 'one_subroutine') {
        $self->updateOneSubroutine();
    }
    elsif($type eq 'another_one_sub') {
        $self->updateAnotherOneSub();
    }
(...)
    else {
        die "Unsupported '$type'";
    }

I have to change this to let each subroutine be coded in its own file, include all available files, and automagically call the subroutine inside.

I did this in a test file with the following code:

# Assume a routines subdir with one_subroutine.pm file with 
sub updateOneSubroutine(){
    $self = shift;
    $self->doSomeThings();

    (...) #my code
}
1;

test.pl

# Saves in routines hash_ref a pair of file_name => subRoutineName for each file in routines subdir.
# This will be used later to call subroutine.
opendir(DIR,"lib/routines") or die "routines directory not found";
for my $filename (readdir(DIR)) {
    if($filename=~m/\.pm$/){
        # includes file
        require "lib/routines/$filename";
        # get rid of file extension
        $filename=~s/(.*)\.pm/$1/g;
        my $subroutine = "update_${file}";
        # camelizes the subroutine name
        $subroutine=~s/_([a-z0-9])/\u$1/g;
        $routine->{ $filename }  = $subroutine;
    }
}

{
    no strict "refs";
    $routine->{$param}();
}

where param is something like "one_subroutine", that matches with a filename available.

Since each subroutine receives $self in the call, I should call the routine by $self->something();

I've tried $self->$routine->{$param}() , $self->${routine->${param}}() and many other things without success. I've checked chapter 9 "dynamic subroutines" of mastering perl, and a similar question to perl monks, but I can't still figure out how to reference the subroutine in a way that represents $self->updateAnotherOneSub() , or something similar that lets $self be read as a param in those subroutines.

Thanks in advance, Keber.


Solution

  • This seems a bit like an X/Y problem. What exactly are you trying to do? If it is to reduce loading time, then modules like AutoSplit/AutoLoader might be of interest to you.

    If it is to create some sort of data structure of subroutines, you should be installing anonymous subs into a hash, rather than giving them all names.

    Given a subroutine reference:

    my $code = sub {...};
    

    you would call it as:

    $self->$code(...);
    

    If instead you have a subroutine name, you can lookup the coderef:

    my $code = 'Package::With::The::Subroutines'->can('method_name');
    

    and if that succeeds (check it), then you can use $self->$code(...) to call it.


    Given this code:

    {
        no strict "refs";
        $routine->{$param}();
    }
    

    You would pass $self to the routine with:

    {
        no strict "refs";
        $routine->{$param}($self);
    }
    

    Or you could approach it the way I did above with can:

    'package'->can($routine->{$param})->($self)
    

    if you don't want to turn off strict 'refs'