在深度嵌套的 Perl 模块中仅供内部使用 class - 避免笨拙的命名
Internal-use-only class in a deeply nested Perl module - avoiding awkward naming
假设一个名为 MyApplication/Subcomponent.pm
的 Perl 模块,并且文件通常以 package MyApplication::Subcomponent;
开头,这个模块恰好定义了一组实用函数。其中一个实用函数需要创建一个本地的、仅供内部使用的实例 class。 class 的定义非常简单,所以我将其粘贴在这里。是一个RAIIclass保存和恢复环境变量的值
{
package MyApplication::Subcomponent::restore_ENV_THING;
sub DESTROY {
my $self = shift;
$ENV{THING} = ${ $self };
}
sub new {
my $class = shift;
my $old_value = $ENV{THING};
$ENV{THING} = shift;
return bless $old_value, $class;
}
}
我的理解是我必须在包名的全局space中给这个class一个名字,而且我必须从根开始命名它:我不能只写package restore_ENV_THING;
因为那会影响 namespace.
的 top 级别
所以这里的问题是:如果有的话,我可以做什么,以便 使用 这个 class 的代码——保证在同一个文件作为上面的代码块和它下面的词法 - 可以写成
sub utility_routine_that_needs_to_save_and_restore_THING {
my $restorer = restore_ENV_THING->new($temporary_value);
...
}
而不是
sub utility_routine_that_needs_to_save_and_restore_THING {
my $restorer = MyApplication::Subcomponent::restore_ENV_THING
->new($temporary_value);
...
}
如果你知道一个技巧可以让我不必须给class包名的全局space一个名字(也许与 open my $fh, ...
的做法并不完全不同?)我也想听听。
请注意 MyApplication
对其代码有一些不寻常的限制:
- 受支持的最早的 Perl 版本是 5.006。是的,你没看错,5.006。特别是这意味着我不能使用词法范围的子。
- 不能使用 CPAN 模块,只能使用 5.006 标准库中包含的模块。这就是为什么我不只使用 Guard or Scope::Guard,以及为什么我使用原语手动定义 class。
- 无法发送任何 XS 代码本身。这就是为什么我对 RAII 使用
sub DESTROY
的原因,即使如果在调用者中错误处理了保护对象,那也不会正常工作;据我所知,如果不降到 C,就没有办法做得更好。
包裹 alias 可以吗?
{
package MyApplication::Subcomponent::restore_ENV_THING;
BEGIN {
*MS:: = *MyApplication::Subcomponent::; # (initials for name)?
};
sub DESTROY {
my $self = shift;
$ENV{THING} = ${ $self };
}
sub new {
my $class = shift;
my $old_value = $ENV{THING};
$ENV{THING} = shift;
return bless $old_value, $class;
}
}
现在可以用作MS::restore_ENV_THING
。
这对所有包和作用域都是全局的,是一个编译时别名。它确实与 main::
符号 table 混为一谈,但只要您选择一个“免费”名称就可以了。
然后是包,例如 aliased pragma, Package::Alias,以及许多其他包,但它们确实具有微妙之处,因此我建议您先阅读它们。每当我使用它时,我发现如上所示的内置别名是一个很好的解决方案。
MyApplication::Subcomponent::restore_ENV_THING->new($temporary_value);
是
的缩写
"MyApplication::Subcomponent::restore_ENV_THING"->new($temporary_value);
所以你只需要
use constant restore_ENV_THING => "MyApplication::Subcomponent::restore_ENV_THING";
restore_ENV_THING->new($temporary_value);
假设一个名为 MyApplication/Subcomponent.pm
的 Perl 模块,并且文件通常以 package MyApplication::Subcomponent;
开头,这个模块恰好定义了一组实用函数。其中一个实用函数需要创建一个本地的、仅供内部使用的实例 class。 class 的定义非常简单,所以我将其粘贴在这里。是一个RAIIclass保存和恢复环境变量的值
{
package MyApplication::Subcomponent::restore_ENV_THING;
sub DESTROY {
my $self = shift;
$ENV{THING} = ${ $self };
}
sub new {
my $class = shift;
my $old_value = $ENV{THING};
$ENV{THING} = shift;
return bless $old_value, $class;
}
}
我的理解是我必须在包名的全局space中给这个class一个名字,而且我必须从根开始命名它:我不能只写package restore_ENV_THING;
因为那会影响 namespace.
所以这里的问题是:如果有的话,我可以做什么,以便 使用 这个 class 的代码——保证在同一个文件作为上面的代码块和它下面的词法 - 可以写成
sub utility_routine_that_needs_to_save_and_restore_THING {
my $restorer = restore_ENV_THING->new($temporary_value);
...
}
而不是
sub utility_routine_that_needs_to_save_and_restore_THING {
my $restorer = MyApplication::Subcomponent::restore_ENV_THING
->new($temporary_value);
...
}
如果你知道一个技巧可以让我不必须给class包名的全局space一个名字(也许与 open my $fh, ...
的做法并不完全不同?)我也想听听。
请注意 MyApplication
对其代码有一些不寻常的限制:
- 受支持的最早的 Perl 版本是 5.006。是的,你没看错,5.006。特别是这意味着我不能使用词法范围的子。
- 不能使用 CPAN 模块,只能使用 5.006 标准库中包含的模块。这就是为什么我不只使用 Guard or Scope::Guard,以及为什么我使用原语手动定义 class。
- 无法发送任何 XS 代码本身。这就是为什么我对 RAII 使用
sub DESTROY
的原因,即使如果在调用者中错误处理了保护对象,那也不会正常工作;据我所知,如果不降到 C,就没有办法做得更好。
包裹 alias 可以吗?
{
package MyApplication::Subcomponent::restore_ENV_THING;
BEGIN {
*MS:: = *MyApplication::Subcomponent::; # (initials for name)?
};
sub DESTROY {
my $self = shift;
$ENV{THING} = ${ $self };
}
sub new {
my $class = shift;
my $old_value = $ENV{THING};
$ENV{THING} = shift;
return bless $old_value, $class;
}
}
现在可以用作MS::restore_ENV_THING
。
这对所有包和作用域都是全局的,是一个编译时别名。它确实与 main::
符号 table 混为一谈,但只要您选择一个“免费”名称就可以了。
然后是包,例如 aliased pragma, Package::Alias,以及许多其他包,但它们确实具有微妙之处,因此我建议您先阅读它们。每当我使用它时,我发现如上所示的内置别名是一个很好的解决方案。
MyApplication::Subcomponent::restore_ENV_THING->new($temporary_value);
是
的缩写"MyApplication::Subcomponent::restore_ENV_THING"->new($temporary_value);
所以你只需要
use constant restore_ENV_THING => "MyApplication::Subcomponent::restore_ENV_THING";
restore_ENV_THING->new($temporary_value);