Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Introspection, strict, warnings and "defined"

by davies (Prior)
on Dec 04, 2011 at 21:21 UTC ( [id://941764]=perlquestion: print w/replies, xml ) Need Help??

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

I am trying to write some code that will act as a POD pre-processor. The idea is that anything looking like keyword will be checked against a list of subs in a module. If the suspected keyword is a sub name, the sub will be called. If not, the line will be treated as just some more POD text.

To do this, I need to know what subs exist in the module that I am planning to write. Googling led me to this, which in turn gave me this. This second reference suggests that the same technique can be used for scalars, arrays and hashes. With significant help from Martin Berends, I got the code from the one liners given to the following:

use strict; use warnings; use diagnostics; no strict 'refs'; my $pkg = shift; eval("use $pkg"); my @all = keys %{$pkg . "::"}; print "Subs:\n"; my %subs; for (@all) { if (defined &{$pkg . "::$_"}) { $subs{$_} = ''; } } print join(", ", sort keys %subs), "\n"; print "\nVars:\n"; my %vars; for (@all) { if (defined ${$pkg . "::$_"}) { $vars{$_} = ''; } } print join(", ", sort keys %vars), "\n"; print "\nArrs:\n"; my %arrs; for (@all) { if (defined @{$pkg . "::$_"}) { $arrs{$_} = ''; } } print join(", ", sort keys %arrs), "\n"; print "\nHash:\n"; my %hash; for (@all) { if (defined %{$pkg . "::$_"}) { $hash{$_} = ''; } } print join(", ", sort keys %hash), "\n";

This sort of works and is enough to do what I want, as I can get the list of subs that I need. But it doesn't work as I would expect and raises questions I don't know how to go about answering.

1. Is there any way to avoid the no strict 'refs' statement? I've never worked without strictures before and it leaves me feeling naked (not a pretty sight).

2. Is there any way to write a use statement to accept a variable without the eval technique Martin produced? It seems inelegant to me.

3. While the message is longer using diagnostics, nothing seems to be able to get rid of the run-time warning

defined(%hash) is deprecated at Z:\Data\Perl\PkgName.pl line 39 (#1) (D deprecated) defined() is not usually useful on hashes because i +t checks for an undefined scalar value. If you want to see if the h +ash is empty, just use if (%hash) { # not empty } for example. (Maybe you should just omit the defined()?)

Even commenting out strict, warnings and diagnostics fails to get rid of it. Removing the defined bit seems to work, but the code is very repetitive and my instincts are to refactor it to something DRYer, but that can't be done as elegantly if the hash version has to be materially different. I don't understand why that should be necessary.

4. This might well be part of the previous question - what is defined actually doing? My guess is that there is some DWIMmery going on. I have always used it to compare the value of a scalar to undef. Clearly it can't be doing this for subs, but what is happening to scalars? Is it returning those scalars that get assigned a value, or simply any scalar that exists? This latter view is the one implied by the second web page I link to above, written by someone with the knowledge to read MJD's code and understand it, i.e. someone who knows far more than I do. But in that case, it seems to be breaking down when looking for hashes.

5. Again, this may be related. If the code is run on Data::Dumper, almost all the scalars are the same as subs. The @all array I create contains only one reference to each name. I thought it was bad practice to have a scalar and a sub of the same name. Is my code faulty? Is my understanding faulty? Are there standards for using scalars with the same name as subs that anyone can point me to?

As I said above, I have the code I need to get the list of subs, so the answers to this aren't terribly important. But my curiosity has been aroused.

TIA & Regards,

John Davies

Update: fixed minor typo

Replies are listed 'Best First'.
Re: Introspection, strict, warnings and "defined"
by ikegami (Patriarch) on Dec 04, 2011 at 21:34 UTC

    Is there any way to avoid the no strict 'refs' statement?

    Yes, the symbol table is available as %::, but it's easier not to. Just limit the scope of no strict 'refs' to where you need it.

    Is there any way to write a use statement to accept a variable without the eval technique Martin produced? It seems inelegant to me.

    use if 1, $pkg;

    or

    my $file = $pkg; $file =~ s{::}{/}g; $file .= '.pm'; require $file; import $file;

    The second has the advantage that you can avoid the import you don't want to perform anyway.

    While the message is longer using diagnostics, nothing seems to be able to get rid of the run-time warning

    You're doing something wrong if you're using defined(%hash).

    You should be checking if the HASH slot of the symbol table entry is populated (*{"${pkg}::$_"}{HASH}).

    Again, this may be related. If the code is run on Data::Dumper, almost all the scalars are the same as subs.

    Scalars are almost(?) always created for globs when they are created.

Re: Introspection, strict, warnings and "defined"
by chromatic (Archbishop) on Dec 04, 2011 at 22:57 UTC

    Do you need a list of all of the functions in a namespace or do you need instead to know if a given named function is in a namespace?

    If the latter, then you can get by with can().


    Improve your skills with Modern Perl: the free book.

      Thank you - I didn't know about can. I could get by with it, but I think it would be inefficient. I am not planning more than 15-20 extensions, but each one might come up many times. I would therefore have to test each keyword candidate for every occurrence, which seems inelegant to me, or make an entry in a table every time I found one, which would complicate the code, possibly beyond my ability to keep it bug free. But I will look into can, as it might well be what I need.

      Regards,

      John Davies

        I think it would be inefficient

        It's essentially doing in C what you're doing in Perl, and it's not passing much data back and forth, so it's exactly the kind of optimization for which writing in C is a benefit. (If you're writing a preprocessor, you're also doing IO in a pipeline, so it's probably not an efficiency problem at all.)

        With that said, an optimization on the Perl side is to use the orcish maneuver, but you have to be careful to distinguish between "Have I already checked and not found anything there?" and "Have I not checked at all." Even so you'll still have to perform the lookup in Perl or in C for every keyword occurrence if I understand your design correctly.


        Improve your skills with Modern Perl: the free book.

Re: Introspection, strict, warnings and "defined"
by BrowserUk (Patriarch) on Dec 05, 2011 at 01:36 UTC

    Would autoloading work for you?

    #! perl -slw use strict; sub AUTOLOAD { no strict 'refs'; our $AUTOLOAD; my $funcname = $AUTOLOAD; my $sub = *{ $AUTOLOAD } = sub { print "$funcname called with [@_] +"; }; print "sub $AUTOLOAD autoloaded"; goto \&$sub; } fred( 1,2,3,4, 5 ); fred( 'a'..'d'); __END__ C:\test>autoload-t.pl sub main::fred autoloaded main::fred called with [1 2 3 4 5] main::fred called with [a b c d]

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2024-03-28 12:33 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found