Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detecting declared package variables in perl

Given

# package main;
our $f;
sub f{}
sub g {}
1;

How can I determine that $f, but not $g, has been declared? Off the cuff, I'd thought that *{main::g}{SCALAR} might be undefined, but it is a bona fide SCALAR ref.

Background: I'd like to import a variable into main::, but carp or croak if that variable is already declared.

EDIT Added an f subroutine in response to @DVK's initial answer.

ANSWER (2010-07-27)

This isn't easy, but it is possible.

An eval technique is most portable, working on perls older than 5.10. In more recent perls, introspective modules like Devel::Peek and B can discriminate.

like image 650
pilcrow Avatar asked Jul 26 '10 13:07

pilcrow


2 Answers

SUMMARY

At this point, after fairly extensive research, I am of a firm opinion that in a situation when a symbol table entry with the name "X" was declared but not assigned to, it is impossible to generically distinguish which of the reference types in a glob was actually declared witout using deep probing of Devel:: stuff.

In other words, you can tell only the following 2 distinct situations:

  1. X was not declared at all (symbol table entry does not exist)

  2. X was declared and some of the glob types were actually assigned to.

    In this second case,

    • You can find WHICH of the glob types were assigned to and which were not

    • BUT, you can not figure out which of the non-assigned-to glob types were declared-and-unassigned vs. which were not declared at all.

    In other words, for our $f = 1; our @f;; we can tell that $main::f is a scalar; but we can NOT tell whether @f and %f were declared or not - it is not distinguishable at all from our $f = 1; our %f; .

    Please note that the subroutine definitions follow this second rule as well, but declaring a named sub automatically assigns it a value (the code block), so you can never have a sub name in a "declared but not assigned to" state (caveat: might not be true for prototypes??? no clue).

ORIGINAL ANSWER

Well, very limited (and IMHO somewhat fragile) solution to distinguishing a scalar from a subroutine could be to use UNIVERSAL::can:

use strict; 
our $f; 
sub g {};
foreach my $n ("f","g","h") {
    # First off, check if we are in main:: namespace, 
    # and if we are, that we are a scalar
    no strict "refs"; 
    next unless exists $main::{$n} && *{"main::$n"}; 
    use strict "refs"; 
    # Now, we are a declared scalr, unless we are a executable subroutine:
    print "Declared: \$$n\n" unless UNIVERSAL::can("main",$n)
}

Result:

Declared: $f

Please note that {SCALAR} does not seem to work to weed out non-scalars in my testing - it happily passed through @A and %H if I declared them and added to the loop.

UPDATE

I tried brian d foy's approach from Chapter 8 of "Mastering perl" and somehow was unable to get it to work for scalars, hashes or arrays; but as noted below by draegtun it works for subroutines or for variables that were assigned to already:

> perl5.8 -we '{use strict; use Data::Dumper; 
  our $f; sub g {}; our @A=(); sub B{}; our $B; our %H=();
  foreach my $n ("f","g","h","STDOUT","A","H","B") {
      no strict "refs"; 
      next unless exists $main::{$n};
      print "Exists: $n\n";
      if ( defined ${$n}) { print "Defined scalar: $n\n"}; 
      if ( defined @{$n}) { print "Defined ARRAY: $n\n"}; 
      if ( defined %{$n}) { print "Defined HASH: $n\n"}; 
      if ( defined &{$n}) { print "Defined SUB: $n\n"}; 
      use strict "refs";}}'       

Exists: f
Exists: g
Defined SUB: g           <===== No other defined prints worked
Exists: STDOUT
Exists: A
Exists: H
Exists: B
Defined SUB: B           <===== No other defined prints worked
like image 172
DVK Avatar answered Oct 02 '22 04:10

DVK


Older perls (pre-5.10) will always have something in the scalar slot.

On newer perls, it appears that the old behavior is mimicked when you try to do *FOO{SCALAR}.

You can use the B introspection module to check the scalar slot, though:

# package main;
our $f;
sub f {}
sub g {}

use B;
use 5.010;
if ( ${ B::svref_2object(\*f)->SV } ) {
   say "f: Thar be a scalar tharrr!";
}
if ( ${ B::svref_2object(\*g)->SV } ) {
   say "g: Thar be a scalar tharrr!";
}

1;
like image 44
ysth Avatar answered Oct 02 '22 02:10

ysth