2009-09-01 20 views
14

Perlでメソッド呼び出しを傍受し、引数で何かを実行してから実行できますか?Perlのメソッド呼び出しをインターセプトできますか?

+1

「aop」というタグを使用しています。あなたが使用したい特定のAOPテクニックについて質問してみませんか? (あなたの質問に対する答えは、AOPシステムを実装するのに役立つかもしれませんが、他の誰かがすでにそれをしているときにどうしますか?) – jrockway

+0

これはメソッド属性で行うことができると思っていましたが、今は適切なperldocページ私が考えていたページではありません)。私はロギングを追加するためのさまざまなメソッドをラップすることを含む素晴らしい例を思い出してください... – Ether

+0

追加情報については、この関連する質問を参照してください:http://stackoverflow.com/questions/635212/how-i-default-perl-class-methods – draegtun

答えて

14

はい、Perlサブルーチンコールを代行受信できます。私はMastering Perlにそのようなことに関する章全体を持っています。 Hook::LexWrapモジュールをチェックしてください。このモジュールを使用すると、すべての詳細を調べなくても実行できます。 Perlのメソッドは単なるサブルーチンです。

サブクラスを作成し、キャッチするメソッドをオーバーライドすることもできます。これは、オブジェクト指向プログラミングがあなたが望んでいる方法であるため、これを行うためのやや良い方法です。しかし、時々人々はこれを適切に行うことを許さないコードを書くことがあります。 Mastering Perlにそれに関する詳細があります。

6

これはMooseの仕事のようです。 MooseはPerlのためのオブジェクトシステムであり、これを行うことができます。 docsは私ができるよりはるかに良い説明をしますが、おそらくMethod Modifier、具体的にはbeforeです。

+1

ムースは、アプリケーション全体で使用するものであり、対象となる問題ではありません。 –

+3

しかし、それは問題が何であるかによって異なり、質問の質問者は詳細を述べていません。新しいアプリケーションを作成するときに取るアプローチを評価している人がいるかもしれません。その場合、Mooseを有効にすることは適切で有益な応答になります。 – jsoverson

+1

同じことを言っただけです。私はムーゼを使わないと言っていませんでしたが、私はそれを使用すると言っていませんでした。 –

7

簡単に説明すると、Perlはシンボルテーブルを変更する適性があります。メソッドの属するパッケージのシンボルテーブルを介してサブルーチン(メソッド)を呼び出します。シンボルテーブルを変更した場合(これは非常に汚いとはみなされません)、ほとんどのメソッド呼び出しを、指定した他のメソッドを呼び出すことで置き換えることができます。

# The subroutine we'll interrupt calls to 
sub call_me 
{ 
    print shift,"\n"; 
} 

# Intercepting factory 
sub aspectate 
{ 
    my $callee = shift; 
    my $value = shift; 
    return sub { $callee->($value + shift); }; 
} 
my $aspectated_call_me = aspectate \&call_me, 100; 

# Rewrite symbol table of main package (lasts to the end of the block). 
# Replace "main" with the name of the package (class) you're intercepting 
local *main::call_me = $aspectated_call_me; 

# Voila! Prints 105! 
call_me(5); 

また、これは、誰かがサブルーチンのリファレンスを取り、参照を経由して、それを呼び出した後、あなたは、もはやこのような呼び出しに影響を与えることはできない、ということを示しています。これは、アプローチを示しています。

私は確かに、perlでアスペクトを行うためのフレームワークがあると確信していますが、これはアプローチを示しています。

3

はい。ちょうど別の動的スコープの変数である@_にあるコールに

引数:

は、次の3つのものを必要としています。

次に、gotoは、現在の@_を保持するが、別の(テール)関数呼び出しを行うreference-sub引数をサポートしています。

最後に、localを使用して、レキシカルスコープのグローバル変数を作成し、シンボルテーブルを%::に埋め込みます。

だからあなたが持っている:

sub foo { 
    my($x,$y)=(@_); 
    print "$x/$y = " . ((0.0+$x)/$y)."\n"; 
} 
sub doit { 
    foo(3,4); 
} 
doit(); 

もちろん出力した:

3/4 = 0.75 

我々はlocalを使用してFOOを交換して行くことができます:

my $oldfoo = \&foo; 
local *foo = sub { (@_)=($_[1], $_[0]); goto $oldfoo; }; 
doit(); 

そして今、我々が得ます:

4/3 = 1.33333333333333 

あなたは、その名前を使用せずに*fooを変更したい、とあなたは、あなたが例えば、%::を操作して、それを修正することができ、evalを使いたくなかった場合:

$::{"foo"} = sub { (@_)=($_[0], 1); goto $oldfoo; }; 
doit(); 

そして今、我々が得る:

3/1 = 3 
5

パベルはそれを行う良い方法を説明していますが、なぜこれを最初にやりたいのかについて詳しく説明する必要があります。

任意のサブルーチンへの呼び出しをインターセプトする高度な方法を探している場合は、シンボルテーブルを使った操作がうまくいくはずですが、現在作業している名前空間にエクスポートされた関数に機能を追加するには、他の名前空間に存在する関数を呼び出す方法を知る必要があります。

Data :: Dumperは、通常、関数 'Dumper'を呼び出し元の名前空間にエクスポートしますが、その関数をオーバーライドまたは無効にして独自のDumper関数を提供し、完全修飾名で元の関数を呼び出します。

use Data::Dumper; 

sub Dumper { 
    warn 'Dumping variables'; 
    print Data::Dumper::Dumper(@_); 
} 

my $foo = { 
    bar => 'barval', 
}; 

Dumper($foo); 

また、これは元の問題によっては、より適切な代替ソリューションです。シンボルテーブルを使って遊ぶときには楽しいことがたくさんありますが、余計なことがあり、必要がない場合はコードを維持するのが難しくなります。

+1

この場合、何もインポートしないようにダンプに空のインポートリストを与えるべきです。また、この場合シーケンスが重要です。最初にインポートしてからオーバーライドする必要があります。最後のサブルーチン定義が勝つ。最後に、警告の下で、これを行う '再定義'エラーが発生します。 –

+0

間違いなく、単に例のために単純にしておきます。直接的であれば、すべての接線の詳細を説明するよりも、より価値があります(レスポンダにとってはより早い)。インポートと警告の概念は、とにかく他の質問として最もよく説明されます。 – jsoverson

関連する問題