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

Is is possible to determine the "name" by which name a reference to sub is called?

For example...

$divesub = \&somesub; $raisesub = \&somesub; sub somesub { if (called.by eq "divesub") { &dive; } else { &surface; } }

I know that a calling "name" is passed when calling a method on an object. Is there something that can be used here and||or is it possible to "abuse" the OO syntax?

Replies are listed 'Best First'.
Re: sub calling name
by chromatic (Archbishop) on Apr 11, 2002 at 03:53 UTC
    Short answer: no, the names are there only for programmer convenience. Anonymous subroutines and nasty people like me who abuse the symbol table make the other problematic.

    Long answer: yes, if you're really sneaky and use AUTOLOAD.

    Rhetorical answer: the long answer uses fewer words than the short one.

      Well, since $divesub = \&somesub; $raisesub = \&somesub;is equivalent to $divesub = \&somesub; $raisesub = $divesub;I must confess I am clueless about how to do this even with AUTOLOAD. I think you'll need XS.

      Thing is, those are not two references to a sub. At best, you have two copies of the same reference to a sub (unless you use some kind of source filter, I guess). There is no telling those two copies apart, anymore than you can tell apart $x and $y in $x = CGI::->new; $y = $x;.

      If you allow for setting different references, there is indeed more than one way to do it. Several have been given already. I have none ready-made, but I would look into using tied variables for this, as that just _might_ be the most transparant way of doing it -- though for better transparancy, I guess you'll have to overload the assignment operator as well. <understatement>This could get tricky.</understatement>

      The Sidhekin
      print "Just another Perl ${\(trickster and hacker)},"

Re: sub calling name
by Kanji (Parson) on Apr 11, 2002 at 04:00 UTC
    Is is possible to determine the "name" by which name a reference to sub is called?

    There are two ways I can think of, neither of which are (IMHO) elegant and both of which are kludges since you're not really getting the calling info from the reference, but rather by how you defined them.

    The first uses wrappers (anonymous or otherwise)...

    $divesub = sub { somesub('divesub') }; $raisesub = sub { somesub('raisesub') }; sub somesub { if ( $_[0] eq "divesub") { &dive; } else { &surface; } }

    ... while the second would be to rename your sub to the magical AUTOLOAD and point each reference to a non-existant sub so that it got ran instead ...

    $divesub = \&divesub; $raisesub = \&raisesub; sub AUTOLOAD { (my $sub = $AUTOLOAD) =~ s/.*:://; if ( $sub eq "divesub" ) { &dive; } else { &surface; } }

        --k.


      AUTOLOAD would be AUTOOVERKILL for this type of thing. I never thought of using a sub to trap an argument list to a function call. Thats pretty slick! For what I was thinking of doing that will work. There is a downside though - if you take a reference on that reference the arguments don't change.

      Cheers,
      -Dogma

Re: sub calling name
by rob_au (Abbot) on Apr 11, 2002 at 03:09 UTC
    See perlfunc:caller ... From the POD ...

    caller EXPR caller Returns the context of the current subroutine call. In scalar context, returns the caller's package name if there is a caller, that is, if we're in a subroutine or `eval()' or `require()', and the undefined value otherwise. In list context, returns ($package, $filename, $line) = caller; With EXPR, it returns some extra information that the debugger uses to print a stack trace. The value of EXPR indicates how many call frames to go back before the current one. ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require) = caller($i); Here `$subroutine' may be `"(eval)"' if the frame is not a subroutine call, but an `eval()'. In such a case additional elements `$evaltext' and `$is_require' are set: `$is_require' is true if the frame is created by a `require' or `use' statement, `$evaltext' contains the text of the `eval EXPR' statement. In particular, for a `eval BLOCK' statement, `$filename' is `"(eval)"', but `$evaltext' is undefined. (Note also that each `use' statement creates a `require' frame inside an `eval EXPR') frame. Furthermore, when called from within the DB package, caller returns more detailed information: it sets the list variable `@DB::args' to be the arguments with which the subroutine was invoked. Be aware that the optimizer might have optimized call frames away before `caller()' had a chance to get the information. That means that `caller(N)' might not return information about the call frame you expect it do, for `N > 1'. In particular, `@DB::args' might have information from the previous time `caller()' was called.

     

    Update - While caller does return information as to the calling subroutine and package, it does not return the reference name that has been used to called the subroutine - This can be confirmed as follows:

    #!/usr/bin/perl use Data::Dumper; use strict; my $divesub = \&somesub; my $raisesub = \&somesub; &{$divesub}; sub somesub { print STDERR Data::Dumper::Dumper( caller(0) ); }

    ... of which the output is as follows ...

    $VAR1 = 'main'; $VAR2 = './test'; $VAR3 = 10; $VAR4 = 'main::somesub'; $VAR5 = 0; $VAR6 = undef; $VAR7 = undef; $VAR8 = undef; $VAR9 = 0; $VAR10 = '';

    So, while it is possible that the caller function may still be of use to you in your endeavour, it does not address the immediate question.

    Thanks also to Kanji++ for pointing this out to me! :-)