问题
I saw How can I redefine Perl class methods?, so I wanted to understand better how it works through an example. (Related: How do I reference methods? - PerlMonks)
If I have an object $obj
which is an instance of some Class
which has Class::method
, then $obj
also has $obj->method
; I assume the simplified memory layout in that case would look something like this (tables made in LaTeX Table Generator; for the below one, src here):
![](https://i0.wp.com/i.stack.imgur.com/Z3pOW.jpg)
... that is, at (say) address 0x1000 we have the $obj->method
, which would simply (somehow) point to the actual Class::method
definition, which is (say) at 0x3500.
Let's say then I have a sub
defined somewhere in the main file, somefunc
(ignore the prefixed $ in the tables), whose definition ends up at address 0x2000.
If I "monkey-patch"/replace the method only for the $obj
instance, I'd expect the memory layout to look something like this (src here):
![](https://i0.wp.com/i.stack.imgur.com/fpvoM.jpg)
Now the thing is this - in the example below (stuff is named more uniquely than in above tables), I actually want to replace the entire class method Demo::My::Functions::test_me
with a method defined in the main file repl_test_me
. I don't really know what to expect of addresses, so I'm trying to show what I think are addresses before and after the method patching, using the %p
specifier of printf
. The code outputs this:
$ 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
What is weird here, is that even the function that is defined in the main file, repl_test_me
, changes address - and it shouldn't?!
So, I'm apparently not printing what I think are addresses of functions - and I think it's also confirmed by the fact that I have two printouts per each call, while I should have only one?
How can I change the below code, so I print out the addresses that can help me confirm that the patching/overloading happened as expected?
Here is the code, 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
);
回答1:
Objects don't have methods; classes do. An object is simply a variable with a package associated with it.
$ 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.
So the real layout is
+---------+
| |
| v
+-----------+ | +-----------+
| Reference | | | 0x269a7f8 |
| 0x26c2370 | | | Package |
+-----------+ | +-----------+
| | |
References | Contains
| | |
v | v
+-----------+ | +-----------+
| Hash | | | 0xXXXXXXX |
| 0x269a978 | | | Method |
+-----------+ | +-----------+
| |
Blessed into |
| |
+---------+
Changing the package affects all objects that use that package.
$ 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
回答2:
I'm apparently not printing what I think are addresses of functions
Right. Instead, you're printf("%p")
ing the address of a (temporary and unnamed) variable holding the reference to the subroutine. (In C, you'd have printed the address of the pointer, rather than its contents.)
[How can I] I print out the addresses [of the subs]?
\&a_sub
returns the thing you're interested in. To inspect it, just print it, as it stringifies (and numifies) nicely and will reveal the address:
use feature qw(say);
sub f {}
my $f_ref = \&f;
say "reference: $f_ref"; # reference: CODE(0x1234)
I have two printouts per each call, while I should have only one[.]
Yes. When you say this:
\&{$object->method}
You are invoking method (which is one of your printouts) and then taking a reference to the CODE slot of its stringified return value in the symbol table. In your case, that's a reference to a non-existent subroutine named 1
, which is what your subs return. (That is, it's as if you'd said my $coderef = \&{1}
.) I'm surprised this gets past strict
and warnings
without comment, for what it's worth.
来源:https://stackoverflow.com/questions/27206371/printing-addresses-of-perl-object-methods-for-redefining-perl-class-methods