可以拦截Perl方法调用吗?

可以拦截Perl方法调用吗?,perl,Perl,您能在Perl中截取一个方法调用,对参数执行一些操作,然后执行它吗?这看起来像是一个任务!Moose是Perl的一个对象系统,它可以做到这一点,还有更多。在解释方面,Perl将比我做得更好,但您可能需要的是一个,特别是在之前的简单地描述一下,Perl具有修改符号表的能力。通过方法所属的包的符号表调用子例程(方法)。如果修改符号表(这并不被认为是非常脏的),可以用调用指定的其他方法替换大多数方法调用。这表明了以下方法: # The subroutine we'll interrupt calls

您能在Perl中截取一个方法调用,对参数执行一些操作,然后执行它吗?

这看起来像是一个任务!Moose是Perl的一个对象系统,它可以做到这一点,还有更多。在解释方面,Perl将比我做得更好,但您可能需要的是一个,特别是在之前的

简单地描述一下,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中执行aspectation,但我希望这可以演示这种方法。

是的,您可以拦截perl子例程调用。我有整整一章是关于这类事情的。查看模块,该模块允许您在不查看所有详细信息的情况下进行操作。Perl的方法只是子例程

您还可以创建一个子类并重写要捕获的方法。这是一种稍微好一点的方法,因为这是面向对象编程希望您使用的方法。然而,有时人们编写的代码不允许您正确地执行此操作。这方面的内容也更多

是的

你需要三件事:

调用的参数位于
@
中,这只是另一个动态作用域变量

然后,
goto
支持一个引用子参数,该参数保留当前的
@
,但进行另一个(尾部)函数调用

最后,可以使用
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
3 / 1 = 3
如果要修改
*foo
而不使用其名称,并且不想使用
eval
,则可以通过操纵
%::
来修改它,例如:

$::{"foo"} = sub { (@_)=($_[0], 1); goto $oldfoo; };
doit();
现在我们得到:

4 / 3 = 1.33333333333333
3 / 1 = 3

你可以,Pavel描述了一个很好的方法,但是你可能应该详细说明为什么你想这样做

如果您正在寻找拦截对任意子例程的调用的高级方法,那么摆弄符号表对您来说是可行的,但是如果您想向可能导出到当前工作的名称空间的函数添加功能,那么您可能需要知道调用其他名称空间中存在的函数的方法

例如,Data::Dumper通常将函数“Dumper”导出到调用命名空间,但您可以重写或禁用该函数,并提供自己的Dumper函数,该函数随后通过完全限定名调用原始名称

e、 g


同样,这是一种替代解决方案,根据原始问题可能更合适。使用符号表可以获得很多乐趣,但如果您不需要它,可能会导致代码维护困难。

Moose是用于整个应用程序的东西,不是针对性的问题。但这取决于问题是什么,提问者不会陈述任何细节。可能是有人在评估创建新应用程序时要采取的方法,在这种情况下,打开Moose将是一个适当且有益的响应。我们刚才也说过同样的话。我没说不要用驼鹿,但我也没说要用它。我看到你用了“aop”标签。为什么不询问您想要使用的特定AOP技术呢?(您问题的答案可能有助于您实现AOP系统,但为什么其他人已经实现了AOP系统呢?)我原以为这可以通过方法属性来实现,但现在我找不到合适的perldoc页面(perldoc属性不是我想到的页面)。我记得有一个很好的例子,其中涉及到包装各种方法来添加日志…请参阅这个相关的SO问题以获取更多信息:在这种情况下,您应该向转储程序提供一个空的导入列表,以便不导入任何内容。在这种情况下,顺序也很重要。您必须先导入,然后重写。最后一个子例程定义获胜。最后,在警告下,执行此操作将出现“重定义”错误。当然,为了示例起见,请尽量保持简单。直截了当比解释每一个无关紧要的细节更有价值(而且对响应者来说更快)。无论如何,导入和警告的概念最好作为其他问题来解释。