BlueLines has asked for the wisdom of the Perl Monks concerning the following question:

This shows my ignorance of the subtle nuances of strict.

Here's a snippet of code:
#!/usr/bin/perl -w use strict; my %Foo = ('Routine' => 1, 'Option' => 0); foreach (keys %Foo) { &$_() if $Foo{$_}; } sub Routine { print "We're in routine.\n"; } sub Option { print "We're in option.\n"; }
This fails miserably with:
Can't use string ("Option") as a subroutine ref while "strict refs" in + use at ./foo.pl line 7.
This snippet works if I add this line: no strict 'refs';

My question is twofold.

First, how can I create a subroutine call from a string without having to add no strict 'refs'?

Second, why is this a considered a subroutine ref in the first place? I thought that a subroutine ref was this: \&Subref, which would return a memory address.

Any help or insight is appreciated.

BlueLines

Disclaimer: This post may contain inaccurate information, be habit forming, cause atomic warfare between peaceful countries, speed up male pattern baldness, interfere with your cable reception, exile you from certain third world countries, ruin your marriage, and generally spoil your day. No batteries included, no strings attached, your mileage may vary.

Replies are listed 'Best First'.
Re:
by rlk (Pilgrim) on Oct 06, 2000 at 07:47 UTC

    To answer your questions in reverse order....

    Second, why is this a considered a subroutine ref in the first place? I thought that a subroutine ref was this: \&Subref, which would return a memory address.

    That's a "hard" reference. A "soft" reference is something of the form

    $foo = 'bar'; $$foo = 'baz'; print $bar; #prints 'baz'

    To answer your first question, AFAIK, you can't. The whole point of use strict 'refs' is to prevent you from using soft references. Furthermore, you can't use hard references as hash keys (they get converted to strings, and reference count information is lost). Perhaps this would work instead?

    #!/usr/bin/perl -w use strict; my %Foo = ('Routine' => { SUB => \&Routine, FLAG => 1 }, 'Option' => { SUB => \&Option, FLAG => 0 } ); foreach (keys %Foo) { $Foo{$_}{SUB}->() if $Foo{$_}{FLAG}; }

    --
    Ryan Koppenhaver, Aspiring Perl Hacker
    "I ask for so little. Just fear me, love me, do as I say and I will be your slave."

Re: "No strict refs" will be the death of me
by btrott (Parson) on Oct 06, 2000 at 07:51 UTC
    Answers, to the best of my knowledge:

    1) You really can't. If you have a string, and you want to call a function of that name, or reference a variable of that name, etc., that's a soft reference. strict 'refs' doesn't allow you to use soft references.

    2) It's *not* a subroutine reference--that's the problem. You're trying to use a string, the name of the routine, as a reference. You're de-referencing it:

    &$_()
    Which says, use $_ as a subroutine reference. But it's not a subroutine reference. Hence the error message.

    You can either do no strict 'refs' (which you should keep in the smallest scope possible), or you can actually take a reference to the subroutine, and use that. I'd prefer the latter:

    my @routines = ( \&Routine, \&Option, ); $_->() for @routines;
    Now obviously, this doesn't do exactly what your code does, but the point of it is just for example purposes.
(dchetlin: Evil trick) Re: No strict refs etc...
by dchetlin (Friar) on Oct 06, 2000 at 12:58 UTC
    rlk and btrott gave the answers that you should be looking for. I have simply an interesting quirk of strict for your edification.

    Replace the following code in your foreach loop:

    &$_() if $Foo{$_};

    With this:

    &{\&{$_}}() if $Foo{$_};

    And you'll fly through strict with no problems. Why? Ask the dragons...

    -dlc

use the symbol table (re: strict refs)
by japhy (Canon) on Oct 06, 2000 at 16:04 UTC
    You can get around strict refs if you try:
    use strict; my %data = ( runme => 1, notme => 0, ); for (keys %data) { $main::{$_}->() if $data{$_}; } sub runme { print "you should see me\n" } sub notme { print "don't look at me!\n" }


    $_="goto+F.print+chop;\n=yhpaj";F1:eval