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

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

Hi

I knew this once, but I cannot remember the details and I am too stupid to find it online, so I hope for some feedback here...

I know that the symbol-table for package main is %{main::}.

The symbol-table has an entry for each symbol that points to a stash which has slots for the different variable types (SCALAR, CODE etc)

What is the syntax to find out whether a symbol has a code-reference in that symbol's stash?

Many thanks!

Replies are listed 'Best First'.
Re: accessing stashes
by choroba (Cardinal) on Mar 01, 2019 at 17:11 UTC
    You can do that with the symbol table, but using defined and exists is cleaner.
    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; { package My; sub one { print "123"; } sub two; } say ${My::}{one}; # *My::one say ${My::}{two}; # -1 say ${My::}{three}; # say defined *My::one{CODE}; # 1 say defined *My::two{CODE}; # 1 say defined *My::three{CODE}; # say exists &My::one; # 1 say exists &My::two; # 1 say exists &My::three; # say defined &My::one; # 1 say defined &My::two; # say defined &My::three; #

    Update: added the first paragraph of says.

    Update 2: Interestingly, a forward declared sub with prototypes returns the prototypes with the first syntax:

    sub four (&@); # ... say ${My::}{four}; # &@

    Update 3: ... unless you add an attribute, too, which makes it output the glob name.

    sub five (&@) :method; say ${My::}{five}; # *My::five

    Update 4: The -1 is printed only if you remove all other mentions of My::three, otherwise *My::three is printed.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      That comes close, but I want something like this (the code below does NOT work):
      use strict; sub hubba { } for my $symbol qw(hubba bubba) { print "$_: "; print defined *My::$symbol{CODE} ? "yep": "nope"; print "\n"; }
      That should print
      hubba: yep bubba: nope
      How can I do that?

        $My::{$symbol} returns a glob.

        *NAME{SLOT} accesses a glob's slot.
        *BLOCK{SLOT} accesses a glob's slot.
        *$NAME{SLOT} accesses a glob's slot.
        EXPR->*{SLOT} accesses a glob's slot.[1]

        So,

        my $glob = $My::{$symbol}; $glob && *{ $glob }{CODE} $glob && *$glob{CODE} $glob && $glob->*{CODE}

        1. Requires Perl 5.24+. Available in Perl 5.20+ by adding both use feature qw( postderef ); and no warnings qw( experimental::postderef );, or by adding use experimental qw( postderef );.
Re: accessing stashes
by NetWallah (Canon) on Mar 01, 2019 at 17:28 UTC
    >perl -E " sub testing{234}; ref($main::{$_}) eq qq|CODE| and say q +q|-->\$main::$_ is a Code ref| for sort keys %main::" -->$main::testing is a Code ref

                    As a computer, I find your faith in technology amusing.

      It works for me in blead, but doesn't output anything in 5.18.2. Also, it doesn't seem to work for other packages than main.

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      It also doesn't work if there's a scalar/array/hash/... with the same name as the sub.