代码之家  ›  专栏  ›  技术社区  ›  pilcrow

在perl中检测声明的包变量

  •  9
  • pilcrow  · 技术社区  · 15 年前

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

    $f ,但不是 $g ,是否已申报?即兴的,我想 *{main::g}{SCALAR} 可能未定义,但它是 善意的

    main:: ,但如果已声明该变量,则为carp或croak。

    添加了一个 f 子例程响应@DVK的初始答案。

    答案(2010-07-27)

    这不容易,但有可能。

    eval technique 是最便携的,在5.10以上的perl上工作。在最近的perl中,内省模块 Devel::Peek B 能辨别。

    5 回复  |  直到 8 年前
        1
  •  1
  •   Greg Bacon    15 年前

    我尽力了,甚至试着问 eval STRING 是否 $main::f 已通过 our my . (这需要复制、关闭和稍后恢复 STDERR 一旦您更改了包,这些声明在临时切换中就不再可见了。

    下面的技术将检测 $f

    use vars qw/ $f /;
    

    代码如下:

    package MyModule;
    
    use warnings;
    use strict;
    
    # using $f will confuse the compiler, generating
    # warnings of 'Variable "%f" is not available'
    # although we're going for $main::f
    my $__f = "from MyModule";
    
    my %IMPORT_OK = (
      '$f' => [f => \$__f],
    );
    
    sub import {
      my($pkg,@imports) = @_;
      my $callpkg = caller;
    
      die "I don't speak your dirty Pig-Latin"
        if $callpkg !~ /\A\w+(::\w+)*\z/ ||
           grep !/\A[\$@%]\w+\z/, @imports;
    
      foreach my $name (@imports) {
        my($sym,$ref) = @{ $IMPORT_OK{$name} || [] };
        die "unknown import: $name" unless $sym;
    
        open my $saverr, ">&", \*STDERR or die "dup STDERR: $!";
        close STDERR;
    
        my $declared = eval qq{
          package $callpkg;
          my(undef)=$name;
          1;
        };
    
        open STDERR, ">&", $saverr or print "restore STDERR: $!";
        die "${callpkg}::$sym already exists" if $declared;
    
        {
          no strict 'refs';
          *{$callpkg . "::" . $sym} = $ref;
        }
      }
    }
    
    1;
    
        2
  •  4
  •   DVK    15 年前

    摘要

    在这一点上,经过相当广泛的研究,我坚定地认为,在一个名为“X”的符号表条目被声明但没有被赋值的情况下,它是 glob中的哪些引用类型实际上是在使用Devel::stuff的深度探测时声明的。

    1. 完全没有声明X(符号表条目不存在)

    2. 声明了X,并将一些glob类型实际分配给了。

      • 你呢 可以 找出哪些glob类型被分配了,哪些没有

      • 但是,你 不能 找出哪些未分配给glob的类型是声明的和未分配的,哪些是根本没有声明的。

      换言之 our $f = 1; our @f; $main::f 是标量; 但我们无法判断 @f %f 是否被宣布-这是无法区分的 our $f = 1; our %f; .

    原始答案

    好吧,区分标量和子例程的非常有限的解决方案是使用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)
    }
    

    Declared: $f
    

    请注意 {SCALAR} @A %H 如果我声明它们并添加到循环中。

    我尝试了brian d foy在“精通perl”第8章中的方法,但不知怎么的,它无法用于标量、哈希或数组;但正如draegtun在下面提到的,它适用于子程序或变量 已经分配给 :

    > 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
    
        3
  •  3
  •   ysth    15 年前

    旧的perl(5.10之前的版本)在标量槽中总是有一些东西。

    在较新的perl上,当您 试着做*FOO{SCALAR}。

    不过,您可以使用B内省模块检查标量插槽:

    # 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;
    
        4
  •  1
  •   draegtun    15 年前

    say 'g() defined in main' if defined &{'main::g'};
    

    不幸的是,相同的方法仅适用于已赋值的包变量:

    our $f = 1;
    say '$f defined with value in main' if defined ${'main::f'};
    

    /I3az公司/

        5
  •  1
  •   Michael Carman    15 年前

    Devel::Peek似乎能够区分标量插槽中已使用和未使用的内容:

    use strict;
    use warnings;
    use Devel::Peek;
    
    our $f;
    sub f { }
    sub g { }
    
    Dump(*f);
    Dump(*g);
    

    SV = PVGV(0x187360c) at 0x182c0f4
      REFCNT = 3
      FLAGS = (MULTI,IN_PAD)
      NAME = "f"
      NAMELEN = 1
      GvSTASH = 0x24a084    "main"
      GP = 0x1874bd4
        SV = 0x182c0a4
        REFCNT = 1
        IO = 0x0
        FORM = 0x0  
        AV = 0x0
        HV = 0x0
        CV = 0x24a234
        CVGEN = 0x0
        LINE = 6
        FILE = "c:\temp\foo.pl"
        FLAGS = 0xa
        EGV = 0x182c0f4 "f"
    SV = PVGV(0x187362c) at 0x18514dc
      REFCNT = 2
      FLAGS = (MULTI,IN_PAD)
      NAME = "g"
      NAMELEN = 1
      GvSTASH = 0x24a084    "main"
      GP = 0x1874cbc
        SV = 0x0
        REFCNT = 1
        IO = 0x0
        FORM = 0x0  
        AV = 0x0
        HV = 0x0
        CV = 0x1865234
        CVGEN = 0x0
        LINE = 8
        FILE = "c:\temp\foo.pl"
        FLAGS = 0xa
        EGV = 0x18514dc "g"
    

    兴趣线在下面 GP = *g SV = 0x0 . 不幸的是,似乎没有一个程序化的方法来获取这些信息。一种钝器方法是捕捉 Dump() 然后分析它。

    推荐文章