Re: replace object methods at runtime
by pc88mxer (Vicar) on Jun 24, 2008 at 13:18 UTC
|
Does it exist symbol table per object(instance) or only per class/package?
A symbol table only exists per class/package.
You might be interested in Object::Prototype which implements a prototype object model like Javascript has. That will allow you to implement methods on a per object basis.
| [reply] |
|
|
Thanks, sounds interesting this prototype objects. Quick view of the source reveals that in this manner can be defined new methods but I doubt that could be replaced already existing ones. The solution is to make some kind of dispatch table indexed by id of the object and method and than create subs in AUTOLOAD. But I really doubt that the new method could shadow the already existing method.
For now I think to just clean the double dispatch solution that I already have.
| [reply] |
|
|
If you don't need something as heavy-duty as Class::Prototyped, you can use Class::Unique, which gives every instance a unique subclass whose symbol table you can tweak at will.
| [reply] |
Re: replace object methods at runtime
by salva (Canon) on Jun 24, 2008 at 13:30 UTC
|
But if I could skip this level of indirection I will be happy.
Well, you can hide it using AUTOLOAD
# untested
sub AUTOLOAD {
if (my ($pkg, $name) = $AUTOLOAD =~ /^((?:.*::)?)(.*)$/) {
$name =~ /^_default_(.*)/ and croak "method $1 not found";
my $sub = sub {
my $method = $_[0]->{m}{$name};
goto &$method if defined $method;
my $default = "_default_$name"
shift->$default(@_);
}
no strict 'refs';
*$AUTOLOAD = $sub;
goto &$sub;
}
croak "bad method name $AUTOLOAD";
}
and then declare the default methods for your objects as:
sub _default_foo { ... }
sub _default_bar { ... }
...
| [reply] [d/l] [select] |
|
|
Thank you for all the suggestions and especially to salva - I have taken the AUTOLOAD road (there is a rhyme here :)
Thanks also to Narveson. Basically the AUTOLOAD makes you the wrapper functions when they are needed. It is really good because I don't want to hand write them by hand.
I know that one day I have to go for Moose. May be sooner than I expected.
Thanks again
| [reply] |
Re: replace object methods at runtime
by Narveson (Chaplain) on Jun 24, 2008 at 14:12 UTC
|
In place of
$self->{m}->($self,@args);
would you like $self->m(@args)?
All you have to do is cover up the syntax you dislike with a wrapper, like so:
package foo;
# dispatch table
my %m_implementation_for;
# wrapper
sub m {
my ($self, @args) = @_;
my $implementation = $m_implementation_for{$self};
$implementation->($self, @args);
}
Afterthoughts: or more tersely,
sub m {
my ($self, @args) = @_;
$m_implementation_for{$self}($self, @args);
}
To be still terser, replace the implementations with closures:
my %m_closure_for;
# after the implementations have been defined
foreach my $object (keys %m_implementation_for) {
$m_closure_for{$object} = sub {
$m_implementation_for{$object}($object, @_);
};
}
# terser wrapper
sub m {
my ($self, @args) = @_;
$m_closure_for{$self}(@args);
}
| [reply] [d/l] [select] |
Re: replace object methods at runtime
by dragonchild (Archbishop) on Jun 24, 2008 at 15:18 UTC
|
You're looking for Class::MOP and, generally, Moose.
My criteria for good software:
- Does it work?
- Can someone else come in, make a change, and be reasonably certain no bugs were introduced?
| [reply] |
Re: replace object methods at runtime
by kyle (Abbot) on Jun 24, 2008 at 16:38 UTC
|
| [reply] |
Re: replace object methods at runtime
by DrHyde (Prior) on Jun 25, 2008 at 10:17 UTC
|
You can't replace methods per-object, only per class or package. So instead, how about replacing the object's class! Let's assume that you want to replace the foo method in $object ...
use Scalar::Util qw(refaddr);
my $newclass = q[
package $CLASS::FakeClass$OBJID;
use base qw($CLASS);
sub foo {
my $self = shift;
... blah blah blah ...
}
];
$newclass =~ s/\$CLASS/ref($object)/eg;
$newclass =~ s/\$OBJID/refaddr($object}/eg;
eval $newclass;
bless $object, ref($object)."::FakeClass".refaddr($object);
That creates a new class just for that object (the one-to-one class-to-object correspondence is guaranteed by using the object's address in memory as part of the class name), which inherits from the class that the object is already an instance of, then reblesses it. Any methods defined in the new class will override those in the original class, and any others will just be passed through to the original class in the usual way.
The one problem with this is that you'll need to be really careful if you serialise and later unserialise objects. In particular, objects are extremely unlikely to be unserialised at the same address in memory, which may lead to later namespace clashes.
Code is untested, but I do something similar quite often, so the principle is correct even if I typed it wrong :-) | [reply] [d/l] [select] |
Re: replace object methods at runtime
by BrowserUk (Patriarch) on Jun 24, 2008 at 15:57 UTC
|
how to replace object methods at runtime.
If you have the time and inclination, I love to see a description of the use you have for this?
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
|
|
Sure ,
A little context first. I am writing SNMP Trap handling framework. I have started with Net-SNMP's snmptrapd with embedded perl interpreter and I have written the first version of the framework and some heavy duty trap handlers for it. Then I realized that it leaks like hell - 1G RAM for 2-3 hours. And it is not my fault because it leaks with empty handlers a lot of memory too
The next step was to write my own trap-daemon based on SNMP_Session.pm and BER.pm. It take me one morning the daemon and the afternoon to port and tune the existing trap-handlers.
So now the architecture is quite configurable and pluggable but is modeled upon the initial implementation. It consists of one central process that at the start loads the configured modules and then fires the appropriate handlers for the received traps.
All the trap handlers inherit from one class "Trap" that makes simple decoding of the trap and manages the live-cycle of the handler. I have decided to spawn new process for each handler and to communicate via pipes with it. In this way I could get lazy forking and initialization of the handlers and to manage situations like the death of some workers. So for now I have code that goes like this:
sub new {
my $type = shift;
my $class = ref $type || $type;
my $self = {
trap => [],
cfg => shift
};
bless $self, $class;
return sub { $self->receiver(@_) };
}
sub _default_receiver {
my ($self,$vars) = @_;
my $fd;
if ( my $pid = open($fd, "|-" )){ # Fork
$fd->autoflush();
$__PACKAGE__::to_die{$pid}=$self;
$self->{receiver} = sub { # Replace receiver in parent
my ($self1,$v)= @_;
store_fd($v,$fd); # Send
};
$self->receiver($vars); # The first trap
}else{
$self->init(); # Init child here
while ( my $v = fd_retrieve( \*STDIN ) ) {
my $r = {};
... do some work here ....
push @{$self->{trap}},$r;
$self->handler();
}
$self->destroy();
}
}
The constructor return a closure that will be run for each received trap. The idea of the "dynamic method rewriting" is that the "receiver"(_default_receiver) will fork new process only the first time and then replace itself with "receiver" (the closure) that just feeds the created worker with data. If this process dies we deleting the "receiver", so the next time it will call the _default_receiver and will fork new process. (SIGCHLD handler not shown in the code).
This version uses the AUTOLOAD approach suggested by salva. I am not coping the code of AUOTLOAD sub that looks quite like the code he suggested
I am open to all kinds of suggestions and advises.
Thanks in advance
| [reply] [d/l] |
|
|
Sure ,
Thank you for indulging me. I've seen this kind of facility requested (or described in other languages), but I've never really seen a non-hypothetical use-case for it. And I'm always more interested in real use cases than hypothetical ones.
That said, without fully understanding the architecture you are describing, it sounds like a complex way of coding a simple boolean condition? Essentially:
sub method {
my( $self, @args ) = @_;
...
if( $self->_pipeExistsAndIsGood ) {
## communicate with pipe
}
else {
## $self->_forkAndStorePipe();
}
}
I can see that 'dynamic method rewriting' is a way to avoid a boolean test, which might provide a small efficiency gain, but trading that for the complexity and 'action at a distance' involved runtime determination of the code behind a method, along with parent & child time forked instances, just seems like a recipe for complex and difficult to trace bugs and failures.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
|
|
|
| [reply] |