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

RoU*_*oUS 1 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"
        }
    }
);
Run Code Online (Sandbox Code Playgroud)

'父母'包裹:

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

导出/导入,使得第二调用(的异常类my $e之一)的作品,就好像NameError是在定义Klass::Foo::Bar,但我还没有想通出来呢.

(并且在任何人说'但Exception::Class有一个漂亮的alias东西之前' ,我会指出别名是专门与异常的throw方法相关联的,所以我不能将它用于非自动抛出的new调用..)

有一两件事我想是把这个在异常封装的importer子(@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}\:\:"};
}
Run Code Online (Sandbox Code Playgroud)

但这最终要求我使用Klass::Foo::Bar::NameError而不仅仅是调用异常NameError.它似乎有效,但太好了.

希望导入NameErrormain::!

我担心,Typeglobs和符号表对我来说仍然有点神秘.

我确信有一种方法可以做我想做的事情(或者我正在做一些我不应该完全做的事情,但让我们暂时不管它).谁能帮我这个?

谢谢!

Eri*_*rom 5

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

sub short () {'a::long::name'}
Run Code Online (Sandbox Code Playgroud)

意思是:

BEGIN {*{__PACKAGE__.'::short'} = sub () {'a::long::name'}}
Run Code Online (Sandbox Code Playgroud)