[英]How do I create an in-memory class and then include it in Perl?
所以我在Perl玩弄了一些黑魔法(最终我们都做了:-)而且我对我应该如何做这一切感到困惑。 这是我开始的:
use strict;
use warnings;
use feature ':5.10';
my $classname = 'Frew';
my $foo = bless({ foo => 'bar' }, $classname);
no strict;
*{"$classname\::INC"} = sub {
use strict;
my $data = qq[
package $classname
warn 'test';
sub foo {
print "test?";
}
];
open my $fh, '<', \$data;
return $fh;
};
use strict;
unshift @INC, $foo;
require $foo;
use Data::Dumper;
warn Dumper(\@INC);
$classname->foo;
我收到以下错误(取决于我的需求行是否被注释掉):
有要求:
Recursive call to Perl_load_module in PerlIO_find_layer at crazy.pl line 16.
BEGIN failed--compilation aborted.
无:
$VAR1 = [
bless( {
'foo' => 'bar'
}, 'Frew' ),
'C:/usr/site/lib',
'C:/usr/lib',
'.'
];
Can't locate object method "foo" via package "Frew" at crazy.pl line 24.
任何知道某些黑魔法的巫师:请回答! 我想更多地了解这个奥秘:-)
还要注意:我知道我可以使用Moose和其他更轻的辅助模块来做这种事情,我主要是想学习,所以使用这样一个模块的建议不会得到我的投票:-)
更新 :好的,我想我原本不清楚我的问题。 我基本上想要基于外部数据结构生成带有字符串的Perl类(我将操作并插入)。 我想,从我这里的东西(一旦它起作用)到那不应该太难。
这是一个有效的版本:
#!/usr/bin/perl
use strict;
use warnings;
my $class = 'Frew';
{
no strict 'refs';
*{ "${class}::INC" } = sub {
my ($self, $req) = @_;
return unless $req eq $class;
my $data = qq{
package $class;
sub foo { print "test!\n" };
1;
};
open my $fh, '<', \$data;
return $fh;
};
}
my $foo = bless { }, $class;
unshift @INC, $foo;
require $class;
$class->foo;
@INC
钩子获取文件的名称(或传递给require
字符串)作为第二个参数,并在每次有require
或use
时调用它。 因此,您必须检查以确保我们正在尝试加载$classname
并忽略所有其他情况,在这种情况下perl沿着@INC
继续向下。 或者,您可以将钩子放在@INC
的末尾。 这是递归错误的原因。
ETA:恕我直言,更好的方法是简单地动态构建符号表,而不是生成代码作为字符串。 例如:
no strict 'refs';
*{ "${class}::foo" } = sub { print "test!\n" };
*{ "${class}::new" } = sub { return bless { }, $class };
my $foo = $class->new;
$foo->foo;
没有必要use
或require
,也没有弄乱邪恶的@INC
钩子。
我这样做:
use MooseX::Declare;
my $class = class {
has 'foo' => (is => 'ro', isa => 'Str', required => 1);
method bar() {
say "Hello, world; foo is ", $self->foo;
}
};
然后你可以像任何其他元类一样使用$ class:
my $instance = $class->name->new( foo => 'foo bar' );
$instance->foo; # foo-bar
$instance->bar; # Hello, world; foo is foo-bar
等等
如果要在运行时动态生成类,则需要创建正确的元类,实例化它,然后使用元类实例生成实例。 基本OO。 Class :: MOP为您处理所有细节:
my $class = Class::MOP::Class->create_anon_class;
$class->add_method( foo => sub { say "Hello from foo" } );
my $instance = $class->new_object;
...
如果你想自己做,以便浪费时间调试一些东西,也许可以尝试:
sub generate_class_name {
state $i = 0;
return '__ANON__::'. $i++;
}
my $classname = generate_class_name();
eval qq{
package $classname;
sub new { my \$class = shift; bless {} => \$class }
...
};
my $instance = $classname->new;
有关如何执行此操作的简单示例,请阅读Class :: Struct的源代码 。
但是,如果我需要为某些生产代码动态构建类的能力,我会看看jrockway建议的MooseX :: Declare。
Perl类只不过是一个数据结构(通常是一个hashref),它已被保存到一个包中,其中定义了一个或多个类方法。
当然可以在一个文件中定义多个包名称空间; 我不明白为什么在运行时编译的eval
结构中这是不可能的(参见perlfunc
的两个不同的eval
表单)。
#!/usr/bin/perl
use 5.010;
use strict;
use warnings;
use Data::Dumper;
eval q[
package Foo;
sub new {
my ( $class, %args ) = @_;
my $self = bless { %args }, $class;
return $self;
}
1;
];
die $@ if $@;
my $foo = Foo->new(bar => 1, baz => 2) or die;
say Dumper $foo;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.