http://qs321.pair.com?node_id=743987

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

When a code reference is assigned to a named subroutine (e.g. sub foo {...}), how is the association between the name stored?

Is there any way to get from the code reference to the name initially associated with it? And, if so, is this access always available, or only limited to certain types of blocks (e.g. BEGIN, CHECK)? Can this be done on a machine without access to the B:: modules, i.e. a non-development machine?

Many thanks in advance, beth

Replies are listed 'Best First'.
Re: Is there a way to access the name under which a subroutine was first defined?
by Anonymous Monk on Feb 16, 2009 at 07:32 UTC
      Thanks! It didn't occur to me to search for "sub reference" as a synonym for "coderef" or "code reference"!

      It will take me some time to go through the various suggestions and links, but from a brief scan, most of the suggestions, bar one, seem to rely on one or more B:: modules. I am looking for a solution that could be used on non-development machines, so those don't really apply.

      There is one, however, that looks very interesting: Devel::Peek. What I'm not clear on though is how that relates to ordinary Perl data types. The documentation seems to be written with XS developers in mind (of which I am not one). I would be grateful for a simple demo of how that module might be used within a MODIFY_CODE_ATTRIBUTES function to decorate a Perl (not XS) subroutine, e.g. with logging code before and after the original subroutine code.

      MODIFY_CODE_ATTRIBUTES only gets the code reference to which the attribute is attached, not the name. Without the name, there is no way to use the attribute to "decorate" foo(...) with before and after code, and then assign the decorated subroutine back to foo(...), like this:

      package XYZ; sub MODIFY_CODE_ATTRIBUTES { my $sPackage = shift @_; my $crSub = shift @_; foreach my $sAttribute (@_) { if ($sAttribute eq 'MagicLogger') { my $sName = mythical_original_name_finder($crSub); *{$sName} = sub { #fancy logging stuff before &$crSub(@_); #fancy logging stuff after } } } }

      Yes, yes, I know this can be done without attributes. There are loads of CPAN function decorator modules, but I'd like to know if it can be done using the current attribute interface. The most commonly recommended method for doing this, Attribute::Handlers, does not play nicely with the FETCH and MODIFY functions of the currently supported interface (nor should it - it was written in 2001). The issue is discussed in some detail here.

      My feeling is that the decoration implementation would be a lot cleaner if we had an attribute handler module that used FETCH and MODIFY. But to do that, I need a way to get from the coderef back to the original name of the function so I can reassign a new subroutine to the symbol table.

      And yes, yes, I know that the attribute interface is still experimental and even if we can find something now it may not work in the next release. Still, I'd like to know.

      Best, beth

      Update:

      Just took a look at Devel::Peek. I don't know that this will help - it appears that the package's MODIFY_CODE_ATTRIBUTES function is called before the association between the original name and coderef is set up. Here's the output when Devel::Peek::Dump is called from within a MODIFY_CODE_ATTRIBUTES method:

      The GVGV::GV value is supposed to be the name of the subroutine (see here), but in this case it is set to 0x0.

        seem to rely on one or more B:: modules.

        What issue do you have with B? B is an interface to the guts, something you will have to access in this case. Whether B is used or not, the same code will get executed one way or another to get the info you want.

Re: Is there a way to access the name under which a subroutine was first defined?
by Anonymous Monk on Feb 16, 2009 at 08:52 UTC
Re: Is there a way to access the name under which a subroutine was first defined?
by Thilosophy (Curate) on Feb 16, 2009 at 11:34 UTC
    I am using this code (which was inspired by Devel::Symdump) to get from coderef to name in my attribute handlers:
    # code for this inspired by Devel::Symdump sub _find_name_of_sub_in_pkg{ my ($ref, $pkg) = @_; no strict 'refs'; #return *{$ref}{NAME} if ref $ref eq 'GLOB'; while (my ($key,$val) = each(%{*{"$pkg\::"}})) { local(*ENTRY) = $val; if (defined $val && defined *ENTRY{CODE}) { next unless *ENTRY{CODE} eq $ref; # rewind "each" my $a = scalar keys %{*{"$pkg\::"}}; return $key; } } die "failed to find name for StartRunmode code ref $ref in package + $pkg\n"; }
    This only works if you know which package you are looking at, but I think in your case, you do.

    Update: Another major catch is that depending on which phase the handler runs, the subroutine will not yet be installed in the symbol table, so there is no name yet. Attribute::Handlers I think allows you to run at CHECK, when it may work. Maybe. Attributes are not for the faint of heart.

    Update 2: I probably managed to confuse not only myself by now, but have a look at this:

    use strict; package Beth; use Attribute::Handlers; sub print_name { my ($package, $symbol, $referent, $attr, $data, $phase, $filename, $linenum) = @_; if (ref $symbol eq 'GLOB'){ print *{$symbol}{NAME}; } print "\n"; } sub Lion :ATTR(CODE,BEGIN) { print_name(@_); } sub Tiger :ATTR(CODE,CHECK) { print_name(@_); } sub bar : Lion { print "pling.\n"; } sub foo : Tiger { print "pling.\n"; }
    The CHECK phase attribute can print the name by just looking at the GLOB it gets, whereas it is still anonymous for the BEGIN attribute. Not sure how to write anything except BEGIN handlers without Attribute::Handlers (only with plain MODIFY_CODE_ATTRIBUTES).
      Attributes are not for the faint of heart

      Ain't that the truth! Thank you so much for the suggestion, but it didn't work within MODIFY_CODE_ATTRIBUTES. Sigh... Your "another major catch" seems to be right on. Your proposal exhibits the same behavior as Devel::Peek::Dump - works great after compilation (even in a BEGIN block) but not in MODIFY_CODE_ATTRIBUTES. The association between symbol and name does not appear to be set up until after the MODIFY_CODE_ATTRIBUTES function completes. This small script...

      outputs...

      Dumping CODE(0x81a8e60) in MODIFY_CODE_ATTRIBUTES: Warning: could not find name for CODE(0x81a8e60) at Monks/Snippet.pm l +ine 23. Name via Thilosophy method: <undef> Dumping CODE(0x81a8e60) in BEGIN block after compilation. Name via Thilosophy method: <bar>

      I am more than a little surprised that it is so difficult to use MODIFY_CODE_ATTRIBUTES to redefine functions. When I scanned the web last night in response to your query on attribute handling it seemed that one of the major use case for attributes is decorating functions. It seems a little strange to add a feature that can't be used for one of its major use cases.

      Best, beth

      Update: fixed some typos, added comment about blocks, some clarifications.

      Further update: Just reread the Perl documentation for attributes and I think I finally understand what the last sentence of this quote might mean. Do you suppose "cloned copies ... used as closures" means decorated functions?... and the reason for this non-implementation being that the coderef is only partially set up? (i.e. not part yet of the symbol table)

      WARNING: the mechanisms described here are still experimental. Do not rely on the current implementation. In particular, there is no provision for applying package attributes to 'cloned' copies of subroutines used as closures.

      It does appear that the only way to get decorated functions is to set up CHECK handlers or do ugly things like store a hash keyed by code ref and then match up the names and the coderefs later on, a la Class::Declare.

        I hate to bring this up, but while we are at it: the CHECK (and INIT) phases do not really work under mod_perl ...

        I am more than a little surprised that it is so difficult to use MODIFY_CODE_ATTRIBUTES to redefine functions.

        I think this part of Perl is a construction site that has been abandoned half-finished. Which only makes it more interesting to the enterprising hacker...

        I am now looking at ways to get a similarly concise syntax as attributes offer using other (more established) mechanisms. Any ideas?

Re: Is there a way to access the name under which a subroutine was first defined?
by shmem (Chancellor) on Feb 16, 2009 at 09:39 UTC

    Initially? The definitions don't carry a timestamp. It all depends on when you are doing your checks.

    $\ = $/; sub foo; print "foo ref is: ",\&foo; my $ref = \&foo; for (keys %main::) { print "1: ",*{$_}{NAME} if *{$_}{CODE} and *{$_}{CODE} eq $ref; } *bar = *foo; sub foo { print "in foo" } for (keys %main::) { print "2: ",*{$_}{NAME} if *{$_}{CODE} and *{$_}{CODE} eq $ref; } bar(); __END__ foo ref is: CODE(0x91cf78c) 1: foo 2: bar 2: foo in foo

      By "initially" I meant the name "foo" assigned by sub foo {...} rather than any aliases assigned at a later point.

      But as you point out, it definitely does seems to be a "when" issue. My new toy Devel::Peek::Dump(...) also reports the subroutine name correctly if it is called after the sub is compiled, but not when the subroutine's attributes are being processed via MODIFY_CODE_ATTRIBUTES (during the compilation of the subroutine definition).

      This is illustrated by the following small script:

      which reports GVGV:GV = 0x0 when called within the MODIFY_CODE_ATTRIBUTES method but GVGV::GV = 0x819ba28    "main" :: "bar" when called after bar(...) is compiled, as the captured output below shows:

      Best, beth

      Update: replaced original example with script illustrating different outputs from within MODIFY_CODE_ATTRIBUTES and after compilation; added explanation of what I meant by "initially".

        It definitely does seems to be a compilation stage issue. My new toy Devel::Peek::Dump(...) also reports the subroutine name correctly if it is called after the sub is compiled, but not when the subroutine's attributes are being processed via MODIFY_CODE_ATTRIBUTES.

        That's not a phase issue. Keep in mind that MODIFY_CODE_ATTRIBUTES actually wraps the subroutine into another one, by saving the reference from the typeglob and assigning the reference of the new (outer) subroutine to it: the subroutine reference *foo{CODE} is necessarily changed. Update: well, the contents of that. But the address in the typeglob remains the same, it seems... hmm.