perl - 如何在 perl 中动态创建包?

标签 perl dynamic package compile-time

如果我可以动态创建包,我会遇到一种情况。下面的代码不会运行,但说明了我想做的事情的精神。

#!/usr/bin/perl
use strict;
use warnings;

use Data::Dumper;

my $basename    = "Xma";
my $len     = 7;
my $newClass    = sprintf("%s%d", $basename, $len);

printf("New class is %s\n", $newClass);

package $newClass {
    # Early modules.
    our @enum; BEGIN { @enum = qw( I_VAL I_SLOPE ); }

    use parent qw(Exporter);
    use enum::fields @enum;
    our @EXPORT = (@enum);

    our $classLen = $len;
    our $classBasename = $basename;

    sub new {
    my $invocant = shift;
    my $self = bless ([], ref $invocant || $invocant);
    return($self);
    }
}

1;

我在我的新类中包含了“额外”的东西,以说明新类是一个复杂的类,它将在继承链中。

我知道我可以用类似的东西做简单的包:
*{ "${class}::new" } = sub { return bless { }, $class };

但是我的新类/包会很大,所以我希望有一些更容易维护的东西。

你们中间的好奇者可能会问,“为什么?”好奇心很大。我需要在加载/编译时尽可能多地处理进程,并试图避免数百万运行时查找。我要换 $basename$len经常,但只在编译时。还有最重要的一点:好奇心。

最佳答案

我不想成为那个说它的人,但可变代码(?!)是 反模式对于我能想到的大多数用例,除了明显的代码生成工具或转译器之外,还有一个很大的危险信号。

最好只创建一个类或类层次结构,然后使用非元编程处理该类中的任何差异。

事实上,这就是 OOP 的总体思路。您创建的对象是您的类的"template"的实例或副本:

  package MyClass {
       sub new {
           my ( $class, %args ) = @_;
           bless { name => sprintf('%s%d', $args{basename}, $args{len}) }, $class;
       }
  }

  my $obj = MyClass->new( basename => 'Xma', len => 7 );
  printf "New class is %s\n", $obj->{ name };

如果你真的需要动态创建包/类,我能想到的最明智的方法是动态创建从一个或多个基类继承的类。
package MyClass {  
    sub new {
        my ( $class, %args ) = @_;
        bless \%args, $class;
    }

    sub foo {
        my $self = shift;
        return "instance of $self";
    }
}

sub build_class {
    my ( %args ) = @_;

    my $classname = sprintf( '%s::%s%d', $args{ parent }, $args{ basename }, $args{ len } );
    @{ "${classname}::ISA" } = ( $args{ parent } );

    return $classname;
}

my $classname = build_class( parent => 'MyClass', basename => 'Xma', len => 7 );
my $obj = $classname->new();

printf "New class is %s\n", ref $obj;  # is MyClass::Xma7
printf "Object is an %s\n", $obj->foo; # a MyClass::Xma7=HASH(...) object instance

如需更多面向 OOP 的元编程能力,请查看 Moose::Meta::ClassClass::MOP :
use Class::MOP;

my $basename = 'Xma';
my $len = 7;
my $classname = "MyClass::${basename}${len}";

Class::MOP::Class->create(
    $classname,
    attributes => [
        Class::MOP::Attribute->new( 'foo', is => 'rw', isa => 'Str' )
    ],
    methods => {}
);

$classname->new( foo => 'bar' );

另一方面,如果你真的需要在你的包中包含可变代码和/或你不需要/不想在每次程序运行时在运行时将所有包加载到内存中,我会说你可以使用 一组文件模板和一些模板语言您选择将新包输出到文件(即“MyClass/Xma7.pm”、“MyClass/Xma8.pm”)然后使用 use MyClasses::Xma7; 加载它们就像任何其他包一样。这里的优点是,考虑到元编程的错误倾向,您的代码将更容易调试和测试。

关于perl - 如何在 perl 中动态创建包?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57385262/

相关文章:

ruby - 如何在 BASH 中合并来自两个 CSV 文件的数据?

perl - 如何在 Perl 中获取目录(文件路径)分隔符?

java - 我可以使字节数组的大小动态化吗?

rust - 无法安装Racer : "pub(restricted) syntax is experimental (see issue #32409)"

java - Java中不明确的类名

perl - 如何在 Perl 中访问函数参数?

perl - Youtube API : Resume Upload *User Authentication Required*

动态数组的 C++ 内存泄漏

c - 如何从C中的动态数组中的二进制文件读取数据

Java 排序集合/api