我看到了How can I redefine Perl class methods?,所以我想通过一个示例更好地了解它的工作方式。 (相关:How do I reference methods? - PerlMonks)
如果我有一个对象$obj
,它是某些具有Class
的Class::method
的实例,则$obj
也具有$obj->method
;我认为在这种情况下,简化的内存布局将类似于以下内容(在LaTeX Table Generator中创建的表;对于下面的表,src here):
...也就是说,在(说)地址0x1000处,我们有$obj->method
,它只是(以某种方式)指向实际的Class::method
定义,即(说)在0x3500处。
假设我在主文件中的某个地方定义了sub
,即somefunc
(忽略表中的前缀$),其定义最终位于地址0x2000。
如果我仅对$obj
实例“monkey-patch” /替换该方法,我希望内存布局看起来像这样(src here):
现在是这样的-在下面的示例中(东西的名称比上表中的唯一),我实际上想用在主文件Demo::My::Functions::test_me
中定义的方法替换整个类方法repl_test_me
。我真的不知道对地址有什么期望,所以我尝试使用%p
的printf
说明符来显示我认为在方法修补之前和之后的地址。代码输出如下:
$ perl cltest.pl
Starting Demo::Main...
Orig test_me!
1: DMFo: 8e63dc0 DMFo->test_me 8e916f8 DMF::test_me 8e916e8 repl_test_me 8e91748
Orig test_me!
-- CODE --
Subroutine Demo::My::Functions::test_me redefined at cltest.pl line 59.
Repl test_me!
Repl test_me!
2: DMFo: 8e63dc0 DMFo->test_me 8e916f8 DMF::test_me 8dfb708 repl_test_me 8dfb6c8
奇怪的是,即使是在主文件
repl_test_me
中定义的函数也更改了地址-不应该吗?因此,我显然没有打印我认为是函数地址的内容-而且我认为,每个调用有两个打印输出,而我应该只有一个打印输出,这一事实也得到了证实。
如何更改下面的代码,以便打印出可以帮助我确认补丁/重载按预期发生的地址?
这是代码
cltest.pl
:use v5.10.1;
package Demo::My::Functions;
$INC{'Demo/My/Functions.pm'} = 1;
use warnings;
use strict;
use base 'Class::Accessor';
__PACKAGE__->mk_accessors(qw(test_me));
sub test_me {
my $self = shift;
print("Orig test_me!\n");
return 1;
}
sub test_also_me {
my $self = shift;
print("Orig test_also_me!\n");
return 1;
}
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}
############################################################
package Demo::Main;
use warnings;
use strict;
print("Starting Demo::Main...\n");
my $DMFA = Demo::My::Functions->new();
sub repl_test_me {
my $self = shift;
print("Repl test_me!\n");
return 1;
}
# note: \&{$DMFA->test_me} calls!
printf("1: DMFo: %p DMFo->test_me %p DMF::test_me %p repl_test_me %p\n",
$DMFA, \&{$DMFA->test_me}, \&{'Demo::My::Functions::test_me'}, \&repl_test_me
);
print("-- " . ref(\&{$DMFA->test_me}) . " --\n");
{
no strict 'refs';
#~ *Demo::My::Functions::test_me = sub {my $self = shift; print("ReplIN test_me!\n"); return 1; }; # OK
#~ *Demo::My::Functions::test_me = *repl_test_me; # overloads
*Demo::My::Functions::test_me = \&repl_test_me; # overloads
};
# test it:
$DMFA->test_me();
# output addr again:
printf("2: DMFo: %p DMFo->test_me %p DMF::test_me %p repl_test_me %p\n",
$DMFA, \&{$DMFA->test_me}, \&{'Demo::My::Functions::test_me'}, \&repl_test_me
);
最佳答案
对象没有方法。类。一个对象只是一个变量,带有与之关联的包。
$ perl -MDevel::Peek -E'$o={}; Dump($o); bless($o); Dump($o); say \%main::'
SV = IV(0x26c2360) at 0x26c2370
REFCNT = 1
FLAGS = (ROK)
RV = 0x269a978
SV = PVHV(0x26a0400) at 0x269a978
REFCNT = 1
FLAGS = (SHAREKEYS)
ARRAY = 0x0
KEYS = 0
FILL = 0
MAX = 7
SV = IV(0x26c2360) at 0x26c2370
REFCNT = 1
FLAGS = (ROK)
RV = 0x269a978
SV = PVHV(0x26a0400) at 0x269a978
REFCNT = 1
FLAGS = (OBJECT,SHAREKEYS)
STASH = 0x269a7f8 "main"
ARRAY = 0x0
KEYS = 0
FILL = 0
MAX = 7
HASH(0x269a7f8) # Address of the main package.
所以真正的布局是
+---------+
| |
| v
+-----------+ | +-----------+
| Reference | | | 0x269a7f8 |
| 0x26c2370 | | | Package |
+-----------+ | +-----------+
| | |
References | Contains
| | |
v | v
+-----------+ | +-----------+
| Hash | | | 0xXXXXXXX |
| 0x269a978 | | | Method |
+-----------+ | +-----------+
| |
Blessed into |
| |
+---------+
更改程序包会影响使用该程序包的所有对象。
$ perl -E'
sub f { "a" }
my $o = bless({});
say join " ", \&f, $o->can("f"), $o->f;
*f = sub { "b" };
say join " ", \&f, $o->can("f"), $o->f;
'
CODE(0x311c680) CODE(0x311c680) a
CODE(0x3126f60) CODE(0x3126f60) b
关于perl - 打印Perl对象方法的地址? (用于重新定义Perl类方法),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27206371/