Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Getting name of sub from a hash-based dispatch table?

by theguvnor (Chaplain)
on Jan 21, 2004 at 19:25 UTC ( [id://322968]=perlquestion: print w/replies, xml ) Need Help??

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

I've got a hash containing a dispatch table - the standard setup where labels are the keys and the subroutines are the values in the hash. Like so:

%query=('action1'=>\&handler1, 'action2'=>\&handler2, 'action3'=>\&_do_not_advertise_this_function_externally, # and so on );

Question: how can I, prior to actually dispatching the call to the handler, find out the name of the subroutine that will be called? For instance for any given param $action, I would like to know whether I'll be going to a subroutine named with a leading '_' prior to calling $query{$action}->();.

I don't want to resort to using symbolic refs and encoding the sub names as text strings in the values of my dispatch table - it seems wrong somehow. Is there an easier (or "better") way?

[Jon]

Replies are listed 'Best First'.
•Re: Getting name of sub from a hash-based dispatch table?
by merlyn (Sage) on Jan 21, 2004 at 19:30 UTC

      All the subs would have names in this situation but point conceded.

      I'd hoped though that it would be reasonably clear that the reason for knowing the name was that I wanted to use a leading underscore to mark which handler subs should be publicised and which should not. That's the only reason - I was hoping a bit of Laziness (altering my naming convention) could save me coming up with any more elaborate means of maintaining a list of which ops should be advertised. ;-)

      [Jon]

Re: Getting name of sub from a hash-based dispatch table?
by broquaint (Abbot) on Jan 21, 2004 at 20:13 UTC
    There's usually a way with perl, but it's not necessarily nice ...
    sub lookup_subname { my($pkg, $sub) = @_; no strict 'refs'; return ( grep { $pkg->can($_) eq $sub } keys %{"$pkg\::"} )[0]; } sub foo; sub _bar; my %hash = ( foo => \&foo, bar => \&_foo ); for my $f (keys %hash) { my $name = lookup_subname __PACKAGE__, $hash{$f}; printf "%s is %s\n", $f, ( $name =~ /^_/ ? 'private' : 'public' ); } __output__ bar is private foo is public
    So as you can see that's not a particularly beautiful solution, although I could say the same for applying encapsulation to a simple dispatch table ;)
    HTH

    _________
    broquaint

      This is the type of thing I had in mind, though you're right broquaint, it's a touch more... verbose than I'd been hoping for.

      I guess this is Larry's waterbed theory at work - keeping the dispatch table structure simple just increases the work I have to do to determine some extra info from the sub names.

      Thanks,

      [Jon]

Re: Getting name of sub from a hash-based dispatch table?
by Fletch (Bishop) on Jan 21, 2004 at 20:00 UTC
    use constant PUBLIC => 0, PRIVATE => 1; %query = ( action1 => { visibility => PUBLIC, code => \&handler1, }, action2 => { visibility => PUBLIC, code => \&handler2, }, action3 => { visibility => PRIVATE, code => \&_do_not_advertise, }, ); my @public_actions = grep $query{$_}->{visibility} == PUBLIC, keys %query;

    If that's too verbose, just make subs which do the same thing (e.g. register_private( action3 => \&_do_not_advertise )). Or encapsulate things in a class and make visibility a method.

    Update: Or just have a register sub which takes an action name and a sub name and then adds the right elements.

    sub register_action { my( $action, $subname ) = @_; no strict 'refs'; $query{ $action } = { visibility => ($subname =~ /^_/), code => \&{$subname} }; }
Re: Getting name of sub from a hash-based dispatch table?
by ysth (Canon) on Jan 21, 2004 at 20:38 UTC
    Looking for a _ is probably the wrong way to decide this, as others have said. But here is how you get a sub name from a coderef:
    sub getsubname { use B 'svref_2object'; my $coderef = shift; join '::', eval { svref_2object($coderef)->GV->STASH->NAME }, eval { svref_2object($coderef)->GV->NAME } }
    Returns false if something goes wrong (like not getting a coderef). Name will be pkg::__ANON__ for anonymous subs. And if the coderef is to an exported sub, the original name and package will be given.
Re: Getting name of sub from a hash-based dispatch table?
by CountZero (Bishop) on Jan 21, 2004 at 20:05 UTC
    My idea is it that it is a "bad thing" to code into the name of the subroutine the fact whether or not the subroutine should be externally "advertised".

    Far better, IMHO, would it be to put this attribute into the dispatch-table like so:

    %query=('action1'=>[\&handler1,'public'], 'action2'=>[\&handler2, 'public'], 'action3'=>[\&_do_not_advertise_this_function_externally,'priva +te'], # and so on );

    Now you will have to dereference one level deeper to get the sub-reference and the attribute, but that seems a small loss given that you now have the attribute handy in the dispatch-table.

    If ever you want to change the public/private atribute, all you have to do is change this atribute in the dispatch-table. In your system, you would have to change the dispatch table and the actual subroutine name itself (and if you ever call such a subroutine directly from somewhere else in your code, you will have broken your program, unless you change the subroutine-call as well -- messy, messy).

    With the "attribute" solution you can even change the public/private programatically if necessary. [Whole new vistas of enormous possibilities unfold before your inner eye]

    CountZero

    "If you have four groups working on a compiler, you'll get a 4-pass compiler." - Conway's Law

Re: Getting name of sub from a hash-based dispatch table?
by BUU (Prior) on Jan 21, 2004 at 21:47 UTC
    Perhaps I'm missing something, but why don't you just not put the private subs in the hash table?

      Because then they would not be able to be called at all through the general dispatch. The problem isn't in hiding them from the dispatch; it's in hiding certain ones from a menu or help system so that the user is not presented with options that make no sense in a given context.

      But as has already been noted by others, perhaps the best solution is to make the data structure for the dispatch table a little more complex/sophisticated so it can contain the public/private attribute.

      [Jon]

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://322968]
Approved by jdtoronto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (4)
As of 2024-03-28 22:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found