4

Let's say I have the following piece of code:

my $compiled = eval 'sub { print( "Hello World\n" ); }';

I can call this by writing:

$compiled->();

So far so good. Now imagine I create 10 functions:

my @fns = ();
for ( my $i = 0; $i < 10; $i++ ) {
  push( @fns, eval "sub { print( 'I am function $i\n' ); }" );
}

I can call these 10 functions as follows:

foreach ( @fns ) {
  $_->();
}

Now, I want to create a dynamic function that calls each of my 10 functions explicitly:

my $evalcode = "sub {";
foreach ( @fns ) {
    # if I print $_ it shows something like
    #   "CODE(0x94084f8)", but trying to
    #   call "CODE(0x94084f8)->()" is invalid
    $evalcode .= "$_->();";
}
$evalcode .= "}";


my $dynamic_fn = eval $evalcode;
$dynamic_fn->();

Is it possible to take a stringified reference to a subroutine and call this directly?

PS why, you ask? Because I would like to write a dynamic routine that constructs a chain of if ( m/.../ ) { } elsif ( m/.../ ) { } ... checks that then call dynamic functions depending on the input string.

PP.
  • 10,764
  • 7
  • 45
  • 59

5 Answers5

14

Instead of string evals, you might want to use regular lexical closures:

my @functions;

for my $i (0 .. 9) {
    push @functions, sub { print "I am function $i\n" };
}

my $call_all_funcs = sub {
    for my $func (@functions) {
        $func->();
    }
};

$call_all_funcs->();

It is also possible to retrieve a code reference based in its address, but that'd be much more elaborate, harder to understand, and generally not a very good idea.

rafl
  • 11,980
  • 2
  • 55
  • 77
  • I was going to suggest this, but I don't think it will do for the OP. I think the code of the different subs is to be generated dynamically, which is why they want to use eval. – Colin Fine Oct 01 '10 at 15:07
  • 7
    It will. The second closure is the important bit. The list of functions generated earlier could also be generated with a string eval. But then again I still think there's ways to do whatever the OP wants without having to resort to `eval STRING` at all. Closures are really quite powerful. – rafl Oct 01 '10 at 15:09
8

How about using a closure instead of an eval string?

sub combine_subrefs {
  my @subs = @_;
  return sub { foreach my $subref (@subs) { $subref->() } };
}

my $dynamic_fn = combine_subrefs( @fns );
$dynamic_fn->();

I'm sure you can adapt this to do the elsif thing you mentioned as well.

aschepler
  • 70,891
  • 9
  • 107
  • 161
7

re: why, you ask? Because I would like to write a dynamic routine that constructs a chain of if ( m/.../ ) { } elsif ( m/.../ ) { } ... checks that then call dynamic functions depending on the input string.

To create an if & elsif chain like you described above can be done without having to resort to eval:

use 5.012;
use warnings;

my $build_ifelse_dispatch_table = sub {
    my @functions = @_;

    sub {
        my $text = shift;
        for (@functions) {
            my ($re, $code) = @$_;
            if ($text =~ $re) {
                $code->();
                last;
            }
        }
    };
};

my $dispatch_on = $build_ifelse_dispatch_table->( 
    [ qr/tom/   => sub { say "Tom!"   } ],   # if    (m/tom/)   { ... }
    [ qr/dick/  => sub { say "Dick!"  } ],   # elsif (m/dick/)  { ... }
    [ qr/harry/ => sub { say "Harry!" } ],   # elsif (m/harry/) { ... }
);


$dispatch_on->( 'peeping tom'         );  # Tom!
$dispatch_on->( 'spotty dick pudding' );  # Dick!
$dispatch_on->( 'harry potter'        );  # Harry!
$dispatch_on->( 'Tom, dick and harry' );  # Dick!

ref: Wikipedia entry on Dispatch Table.

/I3az/

draegtun
  • 22,441
  • 5
  • 48
  • 71
4

You're seeing something like CODE(0xDEADBEEF) when you print a subroutine reference because that's how Perl stringifies references. You see the say sort of thing if you print any sort of reference that doesn't overload stringification:

 $ perl -le 'print []'
 ARRAY(0x1008032b8)

Normally you can't really use that value for anything, and the number you see doesn't correspond necessarily to a real memory address.

For what you're doing, see the dynamic subroutine chapters I have in Mastering Perl. I talk quite a bit about the different things you can do to compose subroutines and work with anonymous subroutines. A module such as Data::Constraint might even give you some ideas. I also talk about this in my answer to How can a Perl force its caller to return?.

Community
  • 1
  • 1
brian d foy
  • 129,424
  • 31
  • 207
  • 592
1

It might be possible (see this similar problem and solutions), but maybe there is another way. How about a (global?) hash that maps stringified code references to the actual code references?

my @fns = ();
for ( my $i = 0; $i < 10; $i++ ) {
  my $fn = eval "sub { print('I am function $i\n'); } ";
  if (ref $fn eq 'CODE') {
      $CODETABLE{$fn} = $fn;
  }
  push @fns, $fn;
}

...

my $evalcode = "sub {";
foreach ( @fns ) {
    # convert stringified code ref to the actual code ref
    $evalcode .= "\$CODETABLE{\"$_\"}->();";
}
$evalcode .= "}";

(eval $evalcode)->();

I am function 0
I am function 1
I am function 2
I am function 3
I am function 4
I am function 5
I am function 6
I am function 7
I am function 8
I am function 9
Community
  • 1
  • 1
mob
  • 117,087
  • 18
  • 149
  • 283