2010-07-26 26 views
8

VớiPhát hiện biến gói khai báo trong perl

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

thế nào tôi có thể xác định rằng $f, nhưng không $g, đã được công bố? Tắt cuff, tôi nghĩ rằng *{main::g}{SCALAR} có thể không được xác định, nhưng nó là bona fide SCALAR ref.

Thông tin cơ bản: Tôi muốn nhập biến vào main::, nhưng cá chép hoặc croak nếu biến đó đã được khai báo.

EDIT Đã thêm f chương trình con để phản hồi câu trả lời ban đầu của @ DVK.

ĐÁP (2010-07-27)

Đây không phải là dễ dàng, nhưng nó có thể.

An eval technique là di động nhiều nhất, hoạt động trên các perls cũ hơn 5.10. Trong các perls gần đây, các mô đun nội suy như Devel::PeekB có thể phân biệt đối xử.

Trả lời

1

Tôi đã cố gắng hết sức, thậm chí là cố gắng hỏi eval STRING liệu $main::f đã được khai báo qua our hoặc my. (Điều này yêu cầu duping, đóng cửa, và sau đó khôi phục lại STDERR để cắt giảm trên chattiness.) Một khi bạn đã thay đổi gói, những tuyên bố không còn có thể nhìn thấy trên một switchback tạm thời.

kỹ thuật dưới đây sẽ phát hiện xem $f đã được tuyên bố qua

use vars qw/ $f /; 

Mã dưới đây:

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[\[email protected]%]\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; 
+0

+1, và tôi không bình thường upvote stringish() s. :) Đây là cách tiếp cận hiện tại của tôi nhiều hơn hoặc ít hơn. Quan trọng hơn, việc kiểm tra eval ở đây * không * gọi phương thức FETCH của tie() d vô hướng - đó là No Good (tm). Tôi tự hỏi, có thể local() izing $ SIG {__ WARN__} xử lý các thông báo lỗi? – pilcrow

+0

Có, FWIW, trong thử nghiệm của tôi nếu bạn bản địa hóa _ \ _ WARN \ _ \ _ xử lý (và $ @, quá, ra khỏi lịch sự) trước khi eval, bạn im lặng các lỗi mà không có bộ mô tả tập tin mô tả. – pilcrow

4

TÓM TẮT

Tại thời điểm này, sau khi nghiên cứu khá rộng, tôi một quan điểm vững chắc rằng trong một tình huống khi một mục bảng biểu tượng với tên chữ "X" đã kê khai nhưng không giao cho, nó là không thể phân biệt phổ biến loại tham chiếu nào trong một glob thực sự được khai báo bằng cách sử dụng thăm dò sâu của công cụ Devel ::.

Nói cách khác, bạn có thể nói chỉ có 2 tình huống riêng biệt sau:

  1. X đã không được công bố tại tất cả (entry bảng symbol không tồn tại)

  2. X được tuyên bố và một số các loại glob đã được gán cho.

    Trong trường hợp thứ nhì này,

    • Bạn thể tìm MÀ các loại glob đã được giao cho và đó không phải là

    • NHƯNG, bạn không thể con số ra của các loại glob không được gán cho đã được khai báo và chưa được gán so với các loại không được khai báo.

    Nói cách khác, đối với our $f = 1; our @f;; chúng ta có thể nói rằng $main::f là một vô hướng; nhưng chúng tôi KHÔNG thể nói liệu @f%f có được khai báo hay không - nó hoàn toàn không phân biệt được với số our $f = 1; our %f;. Xin lưu ý rằng các định nghĩa chương trình con cũng tuân thủ quy tắc thứ hai này, nhưng việc khai báo một tên được đặt tên sẽ tự động gán cho nó một giá trị (khối mã), vì vậy bạn không bao giờ có tên phụ trong "khai báo nhưng không được gán cho" nhà nước (caveat: có thể không đúng đối với nguyên mẫu ??? không có đầu mối).

ORIGINAL ĐÁP

Vâng, rất hạn chế (và IMHO hơi mong manh) giải pháp cho việc phân biệt một vô hướng từ một chương trình con có thể được sử dụng UNIVERSAL :: có thể:

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) 
} 

quả :

Declared: $f 

Xin lưu ý rằng {SCALAR} dường như không hoạt động để loại bỏ phi vô hướng trong thử nghiệm của tôi - nó vui vẻ được chuyển qua @A%H nếu tôi tuyên bố chúng và thêm vào vòng lặp.

CẬP NHẬT

tôi đã cố gắng tiếp cận brian d Foy từ Chương 8 của "Mastering perl" và bằng cách nào đó đã không thể có được nó để làm việc cho vô hướng, băm hoặc mảng; nhưng như đã nói ở dưới đây bởi draegtun nó hoạt động cho chương trình con hoặc cho các biến đó đã được giao cho đã:

> 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 
+0

+1 Tuyệt vời nỗ lực. FWIW, tôi không chắc chắn rằng \ * {name} {SCALAR} * đã bao giờ sai * - đó là kiểm tra tồn tại (có thể được thực hiện 'strict'-ly) bỏ qua" h ". Kiểm tra này không thành công, tuy nhiên, nếu phụ f {} cũng được xác định. – pilcrow

+0

@pilcrow - vâng, đó là chính xác những gì tôi có ý nghĩa trong đoạn cuối. Và bạn nói đúng về '$ f' +' & f' ... như tôi đã nói điều này khá dễ vỡ và hạn chế. – DVK

+1

@pilcrow @DVK Từ perlref: "' * foo {THING} 'trả về' undef' nếu THING cụ thể đó chưa được sử dụng, trừ trường hợp của vô hướng. '* Foo {SCALAR}' trả về một tham chiếu đến một vô hướng vô danh nếu '$ foo' chưa được sử dụng. Điều này có thể thay đổi trong bản phát hành trong tương lai." Điều này làm cho '& main :: f' so với' $ main :: f' là một trường hợp gây nhầm lẫn. –

0

Bạn có thể kiểm tra một chương trình con được xác định như sau:

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

Thật không may là cùng một phương pháp duy nhất hoạt động trên biến gói nếu giá trị đã được chỉ định:

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

/I3az/

1

Devel :: Peek dường như là khả năng phân biệt giữa những thứ đã qua sử dụng và chưa sử dụng trong các khe SCALAR:

use strict; 
use warnings; 
use Devel::Peek; 

our $f; 
sub f { } 
sub g { } 

Dump(*f); 
Dump(*g); 

Đầu ra là:

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" 

Các dòng quan tâm là dưới phần GP = , cụ thể là SV, AV, HV và CV (vô hướng, mảng, băm và mã, tương ứng). Lưu ý rằng kết xuất của *g hiển thị SV = 0x0. Thật không may, dường như không có cách nào để có được thông tin này. Một phương pháp cụ cùn sẽ là nắm bắt đầu ra của Dump() và phân tích cú pháp nó.

+1

Và các perls cũ hơn (trước 5.10) sẽ luôn có thứ gì đó trong khe vô hướng ... Trên các perls mới hơn, bạn có thể kiểm tra '$ {B :: svrev_2object (\\ * f) -> SV} == 0 ' – ysth

3

Cũ hơn perls (pre-5.10) sẽ luôn luôn có một cái gì đó trong khe vô hướng.

Trên perls mới hơn, có vẻ như hành vi cũ được bắt chước khi bạn cố gắng thực hiện * FOO {SCALAR}.

Bạn có thể sử dụng các mô-đun B mẫn để kiểm tra khe vô hướng, mặc dù:

# 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; 
Các vấn đề liên quan