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

时间:2014-11-29 19:49:01

标签: perl reference memory-address

我看到How can I redefine Perl class methods?,所以我想通过一个例子更好地理解它是如何工作的。 (相关:How do I reference methods? - PerlMonks

如果我有一个对象$obj,该对象是某个Class的{​​{1}}实例,则Class::method也有$obj;我假设在这种情况下简化的内存布局看起来像这样(LaTeX Table Generator中的表格;对于下面的src here):

table1

...也就是说,在(比方说)地址0x1000,我们有$obj->method,它会简单地(某种程度上)指向实际的$obj->method定义,即(例如)0x3500。 / p>

假设我在主文件中Class::method定义了一个sub(忽略表中的前缀$),其定义最终在地址0x2000处。

如果我“猴子补丁”/仅替换somefunc实例的方法,我希望内存布局看起来像这样(src here):

table2

现在问题是 - 在下面的示例中(stuff的命名比上面的表更加独特),我实际上想用主文件{{1中定义的方法替换整个类方法$obj }}。我真的不知道对地址有什么期望,所以我试图使用Demo::My::Functions::test_me的{​​{1}}说明符来显示我认为在方法修补之前和之后的地址。代码输出:

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

2 个答案:

答案 0 :(得分:8)

对象没有方法;上课。对象只是一个带有与之关联的包的变量。

$ 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

答案 1 :(得分:2)

  

我显然没有打印我认为的功能地址

右。相反,您将{{}} printf("%p")保存对子例程的引用的(临时和未命名的)变量的地址。 (在C中,你打印了指针的地址,而不是它的内容。)


  

[我怎么能]我打印出[潜艇]的地址?

\&a_sub会返回您感兴趣的内容。要检查它,只需打印它,因为它很好地字符串化(和编号)并显示地址:

use feature qw(say);

sub f {}
my $f_ref = \&f;
say "reference: $f_ref"; # reference: CODE(0x1234)

  

每次通话我有两个打印输出,而我应该只有一个[。]

是。当你这样说:

\&{$object->method}

您正在调用方法(这是您的打印输出之一),然后在符号表中引用其字符串化返回值的CODE槽。在您的情况下,这是对不存在的名为1的子例程的引用,这是您的子回归。 (也就是说,好像你说的是my $coderef = \&{1}。)我很惊讶,这会过去strictwarnings而没有评论,因为它和&# #39;值得。