5

だから私は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;

次のエラーが発生します(require行がコメントアウトされているかどうかによって異なります)。

必要に応じて:

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クラスを生成したいと考えています。私がここに持っているものから(それが機能したら)それへ行くのはそれほど難しいことではないと思います。

4

4 に答える 4

10

動作するバージョンは次のとおりです。

#!/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;

フックは、2番目の引数としてファイルの名前(またはに渡される文字列)を取得し@INC、またはが存在するたびに呼び出されます。したがって、他のすべてのケースをロードして無視しようとしていることを確認する必要があります。この場合、perlはに沿って続行されます。または、の末尾にフックを配置することもできます。これが再帰エラーの原因でした。requirerequireuse$classname@INC@INC

ETA:IMHO、これを実現するためのはるかに優れた方法は、コードを文字列として生成するのではなく、シンボルテーブルを動的に構築することです。例えば:

no strict 'refs';
*{ "${class}::foo" } = sub { print "test!\n" };
*{ "${class}::new" } = sub { return bless { }, $class };

my $foo = $class->new;
$foo->foo;

いいえuse、またはrequire必要ではなく、邪悪な@INCフックをいじることもありません。

于 2009-07-14T22:31:02.367 に答える
6

私はこれをします:

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;
于 2009-07-15T02:43:03.927 に答える
0

これを行う方法の簡単な例については、Class::Structのソースをお読みください

ただし、一部の製品コードのクラスを動的に構築する機能が必要な場合は、jrockwayによって提案されているように、MooseX::Declareを確認します。

于 2009-07-15T03:59:17.607 に答える
-3

Perlクラスは、1つ以上のクラスメソッドが定義されているパッケージに組み込まれたデータ構造(通常はhashref)にすぎません。

1つのファイルに複数のパッケージ名前空間を定義することは確かに可能です。eval実行時にコンパイルされるコンストラクトでこれが不可能な理由がわかりません( perlfunc2つの異なる 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;
于 2009-07-14T22:17:27.260 に答える