perl - 打印 Perl 对象方法的地址? (用于重新定义 Perl 类方法)

标签 perl reference memory-address

我看到了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):

table1

... 也就是说,在(比方说)地址 0x1000 我们有 $obj->method ,这只会(以某种方式)指向实际的 Class::method定义,即(比方说)0x3500。

假设我有一个 sub在主文件的某处定义,somefunc (忽略表中带前缀的 $),其定义在地址 0x2000 处结束。

如果我“monkey-patch”/replace 方法只针对 $obj例如,我希望内存布局看起来像这样(src here):

table2

现在事情是这样的——在下面的例子中(东西的命名比上面表格中的更独特),我实际上想替换整个类方法 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/

相关文章:

c++ - 在运行时区分指针和引用 ANSI C++

c++ - 为什么在返回右值引用时给出 C++ 编译器警告?

c - 32位地址位置混淆...(C语言编程)

c - c中的内存地址是正值还是负值?

Perl:使用 glob 对具有用户定义长度的数组中的值进行排列

c++ - 制作变量列表而不复制它们

mysql - DBI - Perl - 记录 MySQL 警告

c - 写入 double 浮点时处理ecos中未对齐的写入

Perl “do { … } if …” 作为表达式

html - 有趣且延迟的 WebSocket