【问题标题】:Perl packages: how to import classes into the 'use'r's namespace?Perl 包:如何将类导入 'use'r 的命名空间?
【发布时间】:2023-03-25 08:45:01
【问题描述】:

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

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

例如,异常包的摘录:

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 东西'之前,我会指出别名与异常的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}\:\:"};
}

但这最终要求我使用Klass::Foo::Bar::NameError 而不仅仅是NameError 调用异常。看起来效果不错,但效果也不错。

想将NameError 导入main::

恐怕Typeglobs和符号表对我来说还是有点神秘。

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

谢谢!

【问题讨论】:

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


    【解决方案1】:

    在您的示例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'}}
    

    【讨论】:

    • 我不太确定这是在做什么,但它确实有效。我必须研究这个才能理解它。谢谢!!!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    相关资源
    最近更新 更多