Perl包:如何将类导入'use'r的命名空间?

时间:2011-02-26 01:33:21

标签: perl exception-handling namespaces packages perl-module

我正在开发一个为其“父”包定义异常(使用Exception::Class::Nested)的包。我不希望父包必须使用真正长的名称,但我不想污染任何其他名称空间。

所以我想做的是将类名的最后一个元素导出到use异常包的包的名称空间中。

例如,异常包的摘录:

package Klass:Foo::Bar::Exceptions;
use vars qw( @ISA @EXPORT @EXPORT_OK ... );
@ISA = qw( Klass::Foo::Bar Exporter );
use Exception::Class::Nested 0.04 (
    'Klass::Foo::Bar::Exceptions::BaseClass' => {
        description => 'Base class for exceptions',
        'Klass::Foo::Bar::Exceptions::NameError' => {
            error => "I don't like your face"
        }
    }
);

'父'包:

package Klass::Foo::Bar;
use Klass::Foo::Bar::Exceptions;
Klass::Foo::Bar::Exceptions::NameError->throw(error => "D'oh!");
my $e = NameError->new(error => 'Mwahaha!');

喜欢导出/导入异常类,以便第二次调用(my $e一次)就好像NameError中定义了Klass::Foo::Bar一样但是我还没弄明白。

(在有人说'但Exception::Class有漂亮的alias之前'之前,我会指出别名是专门与异常{{{ 1}}方法,所以我不能将它用于非自动抛出的throw调用..)

我尝试过的一件事是将它放在异常包中new sub importer是一个完全限定的异常类的数组(例如,{{1 }或者只是尾端(例如@snames):

'Klass::Foo::Bar::Exceptions::NameError'

但最终要求我使用'NameError'而不仅仅my $caller = caller(); $caller ||= 'main'; my @snames = @{$EXPORT_TAGS{exceptions}}; for my $exc (@snames) { $exc =~ s/^.*:://; no strict qw(subs refs); *{"${caller}\:\:${exc}\:\:"} = \*{__PACKAGE__ . "\:\:${exc}\:\:"}; } 来调用异常。它似乎有效,但太好了。

想要将Klass::Foo::Bar::NameError导入NameError

对我来说,Typeglobs和符号表仍然有点神秘,我很害怕。

我确信有一种方法可以做我想要的事情(或者我正在做一些我不应该完全做的事情,但是让我们暂时离开它)。任何人都可以帮我这个吗?

谢谢!

1 个答案:

答案 0 :(得分:5)

在您的示例import sub中,您正在对包存储器进行别名处理,这将无法执行您想要的操作。相反,您希望创建具有返回完整包名称的缩写名称的子例程:

sub import {
    my $caller = caller;
    for my $long (@{$EXPORT_TAGS{exceptions}}) { # for each full name
        my ($short) = $long =~ /([^:]+)$/;       # grab the last segment
        no strict 'refs';
        *{"$caller\::$short"} = sub () {$long};  # install a subroutine named 
                                                 # $short into the caller's pkg
                                                 # that returns $long
    }
}

分开最后一行,sub () {$long}创建一个不带参数的匿名子程序。代码引用包含单个变量$long,它保留循环迭代期间的值。这称为词法闭包,这基本上意味着只要子例程,子例程的编译环境($long及其值)将持续​​存在。

然后使用$short名称将此匿名子例程安装到调用者的包中。调用者包中子例程的完全限定名称为caller::subname"$caller\::$short"构造。然后将其取消引用为typeglob *{ ... }。使用引用分配给typeglob填充typeglob的那个槽。因此,分配代码引用会安装子例程。

换句话说,以下子程序声明:

sub short () {'a::long::name'}

与以下内容相同:

BEGIN {*{__PACKAGE__.'::short'} = sub () {'a::long::name'}}