Обнаружение, объявленное переменными пакета в жемчуге

Данный

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

Как я могу определить это $f, но нет $g, был объявлен? Экспромтом, я думал это *{main::g}{SCALAR} могло бы быть не определено, но это - добросовестный СКАЛЯР касательно.

Фон: я хотел бы импортировать переменную в main::, но карп или карканье, если та переменная уже объявляется.

EDIT, добавленная f подпрограмма в ответ на первоначальный ответ @DVK.

ОТВЕТ (2010-07-27)

Это не легко, но это возможно.

eval техника является самой портативной, работающий над жемчугом, более старым, чем 5,10. В более свежем жемчуге, самосозерцательных модулях как Devel::Peek и B может различить.

9
задан Community 23 May 2017 в 11:47
поделиться

5 ответов

Я старался изо всех сил, даже до того, что попытался спросить eval STRING , был ли $ main :: f объявлен через ] наш или мой . (Это потребовало дублирования, закрытия и последующего восстановления 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;
1
ответ дан 4 December 2019 в 21:07
поделиться

Вы можете проверить определенную подпрограмму следующим образом:

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

К сожалению, тот же метод работает только с переменной пакета, если было присвоено значение:

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

/ I3az /

1
ответ дан 4 December 2019 в 21:07
поделиться

В старых версиях 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;
3
ответ дан 4 December 2019 в 21:07
поделиться

РЕЗЮМЕ

На данный момент, после довольно обширного исследования, я твердо уверен, что в ситуация, когда запись в таблице символов с именем «X» была объявлена, но не назначена, невозможно в целом определить , какой из ссылочных типов в глобусе был фактически объявлен без использования глубокого исследования Devel :: вещи.

Другими словами, вы можете сказать только следующие две различные ситуации:

  1. X не был объявлен вообще (запись в таблице символов не существует)

  2. X был объявлен, и некоторые типы глобусов были фактически присвоены .

    Во втором случае

    • Вы можете найти, КАКИЕ из типов глобусов были назначены, а какие не были

    • НО, вы не можете выяснить, какой из не назначенные глобальные типы были объявлены и неназначены, а не объявлены вообще.

    Другими словами, для наш $ f = 1; наш @f; ; мы можем сказать, что $ main :: f - скаляр; но мы НЕ можем сказать, были ли объявлены @f и % f или нет - это вообще не отличается от нашего $ f = 1; наш% 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 , если я объявил их и добавил в цикл .

ОБНОВЛЕНИЕ

Я попробовал подход Брайана Д Фоя из главы 8 «Освоения perl» и почему-то не смог заставить его работать со скалярами, хешами или массивами; но как отмечено ниже 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
4
ответ дан 4 December 2019 в 21:07
поделиться

Devel :: Peek, кажется, может различать использованные и неиспользуемые вещи в слоте SCALAR:

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 = раздел, а именно SV, AV, HV и CV (скаляр, массив, хэш и код соответственно). Обратите внимание, что дамп * g показывает SV = 0x0 . К сожалению, не существует программного способа получить эту информацию. Тупой инструментальный подход - захватить вывод Dump () и проанализировать его.

1
ответ дан 4 December 2019 в 21:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: