2

Suppose I have a function foo (or ::foo, or main::foo if you prefer), and I define

use strict;
my $sub_name = 'foo';

I want to invoke foo indirectly, as "the function whose name is stored in $sub_name". (For the sake of this example, assume that the invocation should pass the list 1, 2, 3 as arguments.)

I know that there's a way to do this by working with the symbol table for main:: directly, treating it like a hash-like data structure.

This symbol-table incantation is what I'm looking for.

I've done this sort of thing many times before, but I have not programmed Perl in many years, and I no longer remember the incantation.

(I'd prefer to do this without having to resort to no strict, but no biggie if that's not possible.)

kjo
  • 33,683
  • 52
  • 148
  • 265

2 Answers2

3

I'd simply use a symbolic reference.

my $sub = \&$qualified_sub_name;    # \&$symbol is except from strict 'refs'.

$sub->()

But you requested that we avoid using symbolic reference. That's way too complex. (It's also might not handle weird but legit misuse of colons.)

my $pkg = \%::;
my $sub_name = $qualified_sub_name;
$pkg = $pkg->{$1} while $sub_name =~ s/^(.*?::)//sg;
my $sub = $pkg->{$sub_name};
$sub = *{ $pkg->{$sub_name} }{CODE}
   if ref(\$sub) eq 'GLOB';  # Skip if glob optimized away.

$sub->()
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 1
    Your answer jogged my memory. What I was looking for was simply the expression `*{$::{'foo'}}{CODE}(1,2,3)`. Thanks! – kjo Dec 02 '17 at 21:16
  • 2
    @ikegami No, perl optimizes the case of a simple sub `foo` if no other slots (such as `$foo` or `@foo`) exist. It'll store a coderef directly in the symbol table. – melpomene Dec 02 '17 at 21:20
  • 1
    The other interesting case is constants: `perl -wE 'use constant foo => 42; say $::{foo}'` - `SCALAR(0x3a9808)`. Not a mess, just standard Perl behavior. – melpomene Dec 02 '17 at 21:22
  • 1
    @ikegami Yeah, the optimization is broken in non-`main` packages, but it's currently being fixed in blead (and breaking broken modules elsewhere). – melpomene Dec 03 '17 at 07:08
1

You can use can:

my $sub_name = 'foo';
my $coderef = main->can($sub_name);
$coderef->(@args);

As others have mentioned, you should note that this can return also methods like "can" or "isa". Also, if $sub_name contains Some::Module::subname, this will also be called.

If you're not sure what's in $sub_name, you probably want a different approach. Use this only if you have control over $sub_name and it can contain only expected values. (I assumed this, that's why I wrote this answer.)

tinita
  • 3,987
  • 1
  • 21
  • 23
  • This may return unexpected results if `$sub_name` is e.g. `"isa"`. – melpomene Dec 02 '17 at 21:13
  • The OP asked how to perform a sub call, not a method call (which you did wrong anyway!!). Your approach can lead to the wrong sub being called. – ikegami Dec 02 '17 at 21:26
  • ikegami: `$coderef->()` is a sub call, not a method call. – tinita Dec 02 '17 at 21:32
  • Yes, but your code is not the same as `main::foo()`, the sub call the OP requested. You partially implemented a method call instead. – ikegami Dec 02 '17 at 21:33
  • I agree that it's not the same in all circumstances, but if the subname is just `foo`, and `main` does not inherit from another package that implements `foo`, then it's just the same as `main::foo()` – tinita Dec 02 '17 at 21:58
  • 1
    @ikegami: Even though I was indeed asking for `*{$::{foo}}{CODE}`, AFAICT, `main->can('foo')` yields exactly the same entity, so I don't understand your objection. – kjo Dec 02 '17 at 21:59
  • 1
    @kjo, Quite the contrary, and as I already said, it does NOT always yield the same result. `can` performs a method search. – ikegami Dec 02 '17 at 22:01