2

クラス内でメソッドを動的に定義したいと思います。以下のスケルトンよりもやや複雑なトレーサーを作成しています。これも状態を認識していますが、それは私の問題とは関係ありません。sprintf を呼び出し、改行をテキスト \n に置き換える trace メソッドを使用して TraceSlave クラスを作成しました。

基本的に、トレースを次のようにインスタンス化したいと思います。

my @classes = qw(debug token line src match);
my $trace = Tracer->new(\@classes);

そして、動的に定義されたトレースのメソッドを次のように呼び出すことができるはずです。

$trace->debug("hello, world");
$trace->match("matched (%s)(%s)(%s)(%s)(%s)", $1, $2, $3, $4, $5);

したがって、私の Tracer クラスは次のようになります。

package Tracer;
  sub new {
    my $class = shift;
    my $self = {};
    my @traceClasses = @{$_[0]};
    bless $self, $class;
    for (@traceClasses) {
# This next line is wrong, and the core of my question
      $self->$_ = new TraceSlave($_, ...)->trace
    } # for (@traceClasses)
  }

それはコンパイルされないからです。基本的には、TraceSlave のインスタンスの trace メソッドとして、Tracer インスタンスのメソッドを定義したいと考えています。ループで。

AUTOLOAD または eval で実行できますが、それは間違っています。正しい方法は何ですか?

完全を期すために、TraceSlave を次に示します。大丈夫だよ

package TraceSlave;
  sub new {
    my $self = { header => $_[1], states => $_[2], stateRef => $_[3] };
    bless $self, $_[0];
    return $self;
  } # new()

  sub trace {
    my $self = shift;
    my @states = @{$self->{states}};
    if ($states[${$self->{stateRef}}]) { # if trace enabled for this class and state
      my @args;
      for (1..$#_) { ($args[$_-1] = $_[$_]) =~ s/\n/\\n/g; } # Build args for sprintf, and replace \n in args
      print $self->{header}.sprintf($_[0], @args)."\n";
    }
  } # trace()
4

6 に答える 6

2

クラスのランタイム変更を開始するときはいつでも、MOP と Moose を使い始めます。したがって、私がこれを正しく読んでいる場合、次のようなものが必要です

package Tracer;
use strict;
use warnings;
use Moose;
use TraceSlave;

has 'classes' => ( is => 'ro', isa => 'ArrayRef[Str]', required => 1 );

### This is to allow ->new(\@classes) invocation instead of just
### using ->new( classes => \@classes) for invocation
around BUILDARGS => sub {
  my $orig  = shift;
  my $class = shift;

  if ( @_ == 1 && ref $_[0] eq 'ARRAY' ) {
    return $class->$orig( classes => $_[0] );
  }
  else {
    return $class->$orig(@_);
  }
};

sub BUILD {
  my $self = shift;
  for my $class (@{$self->classes}) {
    my $tracer = TraceSlave->new($class, ...);
    $self->meta->add_method( $class => sub { $tracer->trace(@_) } );
  }
}

私はかなり確信していますが、これはボンネットの下で同じことを行い、最終的に文字列の評価になります。私は MOP の内部をまったく掘り下げていません。(また、これが正しいコードであるか、Moose を使用する最善の方法であると 100% 確信しているわけでもないので、購入者は注意してください。:))

于 2013-06-06T17:20:54.643 に答える
1

「トレーサー」の問題の詳細を無視して、特定のパッケージのメソッドを動的に作成できるようにしたいというのは正しいですか? これはどうですか?

sub new {
    my ($class, $trace_classes) = @_;

    # ...

    foreach my $tc (@$trace_classes) {
        no strict 'refs';
        *{"${class}::${tc}"} = sub {
            my $self = shift;
            # ...
        };
    }

    return $self;
}

!でそれを行うのは非常に奇妙に思えますが。newだから多分私は要点を逃した

于 2013-06-10T21:46:14.180 に答える
0

@Unk 上記の Unk の小さなコード スニペットへの回答です。

  foreach my $tc (@classes) { # loop over all trace classes, and create a slave for each class
    my $states = $self->{states}->{$tc} = [];
    $slave = TraceSlave->new( "$tc:"." "x($maxlen-length($tc)), $states, $stateRef );
    no strict 'refs';
    *{"${class}::$tc"} = sub { $slave->trace(@_[1..$#_]); }
  } # foreach my $tc (@classes)
  bless $self, $class;
  return $self;
} # new()

これは確かにずっときれいです。Tracer $self にスレーブを含める必要はもうありません。

ただし、サブ全体を次のように置き換えることができるはずです。

*{"${class}::${tc}"} = $slave->trace;

$slave->trace への参照が必要なため、これはもちろん機能しません。上記のコードはそれを呼び出すだけです。悲しいことに、私はグロブの使用、または実際には左辺値のリファレンスの多くも理解していません。私の弁護では、C のポインターまたは JavaScript の参照を使用して何でもできますが、perl の参照はかなりの食事です。まだまだ勉強中。

トレーサーには多くのプライベート データ、具体的にはどのトレース クラスがどの状態にあるか、そしてもちろん各トレース クラスのヘッダーがあるため、OO は正しいアプローチだと思います。上記の単純な割り当てが機能する場合、ネストされたオブジェクトも正しいです。

これはすべて、VHDL プロジェクトの make システムを作成する作業の一部です。10行のダクトテープを書くのではなく、言語を適切に学ぶ時が来たと思ったので、私はそれをperlでやることを余儀なくされました.

*{"${class}::${tc}"} = \&trace->slave はおそらく機能しないことに気付きました。最初の引数は Tracer インスタンスですか、それとも TraceSlave インスタンスですか? 機能するには TraceSlave インスタンスである必要がありますが、これは Tracer クラスで定義されたメソッドです。もちろん、スレーブを Tracer $self に戻すこともできますが、そうすると事態はさらに複雑になります。

私は今、私が望むことをできるだけ簡単に行うと思います。

于 2013-06-11T08:32:05.057 に答える