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

Hi, I am trying to list all the input arguments of a method. For eg,
package Module1; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } sub allVars($no1) { my ($self, $no1, $no2, $name1, $name2) = @_; print "all vars = $no1 $no2 $name1 $name2\n"; } 1;

I want to list the arguments of allVars that is - $no1, $no2, $name1 and $name2. I tried using symbol table of Perl. But I could extract only the methods of my module.

foreach my $key (sort keys %Module1::) { print $key ==> will return all methods of Module1 }
Is there any way to list arguments of a method using symbol table? Is there any Perl Module I can use?
I have been told that it works in python with inspect::getargs module.
Any help would be appreciated.
Thanks!

Replies are listed 'Best First'.
Re: List arguments of a method
by davido (Cardinal) on May 15, 2014 at 06:20 UTC

    If ever I say something is impossible someone will come along and show that with a CPAN module I've previously been unaware of, deep XS magic, or a crazy source filter an implementation of just that is possible and exists. So I won't say this is impossible.

    However, consider the limitations, and more importantly, reconsider why you need this. It seems that what you are asking is not to determine what parameters were passed, but instead, how they were passed. You don't want to know that the first param was "foo", you want to know that a variable named $bar was used to pass it in.

    But you have no guarantees that the value was passed by a single scalar variable. Consider the following examples:

    my $val = "foo"; my $valref = \$val; my @valarray = ("foo"); my %valhash = ( somekey => 'foo' ); my %otherhash = ( foo => 'dummy_value' ); allvars($val); allvars($$valref); allvars($valarray[0]); allvars(@valarray); allvars("foo"); allvars(join '', map { chr } (102,111,111)); allvars($valhash{somekey}); allvars(keys %otherhash);

    All of these sub calls will hold 'foo' in $_[0], but what would you hope to learn by inspecting how the value was passed to the sub?

    This is certainly an XY problem. Is it possible that you just want a way of doing named parameters? This can be facilitated by passing a hash or hashref:

    my $params = { this => 'foo', that => 'bar', other => 'baz', } sub param_names { my $params = shift; print "$_: $params->{$_}\n" for keys %$params; } param_names($params);

    (Ignoring prototypes, because there's still an impedance mismatch between what it seems is wanted here, and what they provide)


    Dave

Re: List arguments of a method
by tobyink (Canon) on May 15, 2014 at 08:43 UTC

    As others have said, no this can't be done. And certainly not via the symbol table because lexicals don't live there.

    PadWalker provides a function called peek_sub which will tell you the names of lexical variables used within a sub. However, not all lexical variables necessarily correspond to parameters. The sub might contain, say, a foreach my $x (@array) loop where $x is just a loop variable; nothing to do with a function parameters.

    A better alternative would be to write your module using one of the many CPAN extensions that provide declarative sugar for method signatures, and provide an introspection API. Here's an example using Kavorka:

    use v5.14; package Module1 { use Moo; use Kavorka; method allVars (Int $no1, Int $no2, Str $name1, Str $name2) { say "all vars = $no1 $no2 $name1 $name2"; } } say "Let's check the method call works..."; my $obj = Module1->new; $obj->allVars(1, 2, "Foo", "Bar"); say "Now let's inspect its parameters..."; my $info = Kavorka->info(\&Module1::allVars); for my $parameter ($info->signature->positional_params) { say $parameter->name, " has type ", $parameter->type->name; }

    The output is:

    Let's check the method call works... all vars = 1 2 Foo Bar Now let's inspect its parameters... $no1 has type Int $no2 has type Int $name1 has type Str $name2 has type Str

    Function::Parameters would similarly work. Method::Signatures would not because although it provides very similar declarative sugar, it doesn't seem to have an introspection API. MooseX::Method::Signatures would also work, but it's incredibly slow.

    use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re: List arguments of a method
by kcott (Archbishop) on May 15, 2014 at 06:31 UTC

    G'day perlUser9,

    You have an illegal prototype in "sub allVars($no1)":

    $ perl -Mstrict -Mwarnings -e 'sub allVars($no1) { 1 }' Illegal character in prototype for main::allVars : $no1 at -e line 1.

    See perlsub or, if that's too heavy going, start with "perlintro: Writing subroutines".

    From the first line of code in &allVars:

    my ($self, $no1, $no2, $name1, $name2) = @_;

    I assume you're calling it something like this:

    $object->allVars(...)

    You seem to understand that all the arguments are in @_; and, you've assigned the first of those to $self. Why do then say "the arguments of allVars that is - $no1, $no2, $name1 and $name2"? What happened to $self?

    I suspect you've misunderstood some key concept; unfortunately, I don't know what that might be. Also, this rather looks like an "XY Problem". Perhaps it would be better to explain your problem instead of your attempted solution.

    My best guess at what you want (although I strongly suspect this guess is wrong) is along these lines:

    #!/usr/bin/env perl -l use strict; use warnings; package Some::Module; sub new { bless {} => shift } sub all_vars { print "all vars = @_" } package main; my $obj = Some::Module::->new; $obj->all_vars(qw{a b c d});

    Output:

    all vars = Some::Module=HASH(0x7fc55c003098) a b c d

    By the way, lexical variables don't live in the symbol table. If you were looking for them there, you won't find them.

    -- Ken

Re: List arguments of a method
by moritz (Cardinal) on May 15, 2014 at 09:10 UTC
    I want to list the arguments of allVars that is - $no1, $no2, $name1 and $name2.

    In the usual Perl nomenclature, the arguments are simply the values that are passed in, and those can be found in @_.

    Perl 5 doesn't (yet) support formal parameter lists / signatures of subroutines, so you can't introspect them.

    Perl 6 exposes signatures as objects, and allows you introspect that parameter list:

    $ perl6-m -e 'sub f($x, $a, $b) { }; say &f.signature.params.perl' (Any $x, Any $a, Any $b).list $ perl6-m -e 'sub f($x, $a, $b) { }; say &f.signature.params[1].name' $a $ perl6-m -e 'sub f($x, $a, $b) { }; say &f.signature.params[1].type.^ +name' Any

      "Perl 6 exposes signatures as objects, and allows you introspect that parameter list"

      FYI, the Kavorka signature syntax and introspection API shown in my answer is very much inspired by Perl 6.

      Your example:

      $ perl6-m -e 'sub f($x, $a, $b) { }; say &f.signature.params[1].name' $a

      ... translates to:

      perl -MKavorka -E'fun f($x, $a, $b){ }; say Kavorka->info(\&f)->signat +ure->params->[1]->name'

      My policy was generally, "if in doubt, do things like Perl 6".

      I do really need to experiment with blessing coderefs, allowing things like: (\&f)->signature to "just work".

      use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
Re: List arguments of a method
by andal (Hermit) on May 15, 2014 at 06:27 UTC

    I don't believe this is possible in Perl. Here all of the arguments are passed in @_ array, so effectively they have no names. In the function you can copy values from that array to any variables you like, or not copy them at all.

Re: List arguments of a method
by LanX (Saint) on May 15, 2014 at 10:37 UTC
    IF I understand your question correctly (the code given is broken) this depends again on the quality of your sources.

    You could use B::Deparse (->coderef2text) to dynamically reconstruct the source of a given sub and parse the first line with @_ using a regex.

    Here are dragons!!!

    ... this use of @_ is only a convention and not guaranteed.

    But again you might only be interested to have a limited personal solution for your IDE....

    ... but this is life inspection... wouldn't you need static parsing¹???

    Please clarify your question!!!

    Cheers Rolf

    ( addicted to the Perl Programming Language)

    ¹)eg with PPI ?