Add a new method for an existing object in perl

I have this perl object. After the object was created, I try to add a new method to the object in the loader method, which can then be called later.

I tried a whole bunch of things that didn't work. Examples include:

sub loader { my ($self) = @_; sub add_me { my ($self, $rec) = @_ warn "yayyyyyy"; return $rec; } #here are the things I've tried that dont work: # &{$self->{add_me}} = \&add_me; # \&{$self->{add_me}} = \&add_me; # assuming the class definition is in Holder::Class try to add it to symblol table # *{Holder::Class::add_me} = \&add_me; } 

EDIT:

The reason I need to do this is to add a hook to my code in which the user of my software will be able to enter their own sub to edit the data structure as it will be.

To do this, they will be able to edit an additional file that will contain only one sub element and get the transferred data structure, for example:

 sub inject_a_sub { my ($self, $rec) = @_; #do stuff to $rec return $rec; } 

then inside my original object after creating it, I check if the above file exists, and if so, read its contents and evaluate it. Finally, I want to make the eval'd code, which is just a sub method of my object. More precisely, my object already inherits a method called do_something , and I want sub to read in with eval to override the do_something method, which is inherited, so that when sub is called from an external file, it is executed.

his strange problem: /

and it hurts me: (

Obi Wan Kenobi you are my only hope!

Hooray!

+5
oop perl
source share
1 answer

If you just want to attach functionality to a specific object and do not need inheritance, you can save the ref code in the object and call it.

 # Store the code in the object, putting it in its own # nested hash to reduce the chance of collisions. $obj->{__actions}{something} = sub { ... }; # Run the code my @stuff = $obj->{__actions}{something}->(@args); 

The problem is that you need to check that $obj->{__actions}{something} contains a link to the code. I would suggest including a method around this procedure.

 sub add_action { my($self, $action, $code) = @_; $self->{__actions}{$action} = $code; return; } sub take_action { my($self, $action, $args) = @_; my $code = $self->{__actions}{$action}; return if !$code or ref $code ne 'CODE'; return $code->(@$args); } $obj->add_action( "something", sub { ... } ); $obj->take_action( "something", \@args ); 

If you already know the name of the class into which you want to enter the method, write the routine as usual, but use the full name.

 sub Some::Class::new_method { my $self = shift; ... } 

Note that any global variables inside this routine will be in the surrounding package, not in Some :: Class. If you want constant variables to use state inside the routine or my outside the routine.


If you do not know the name at compile time, you will need to enter the subroutine in the symbol table so that you are close to the last.

 sub inject_method { my($object, $method_name, $code_ref) = @_; # Get the class of the object my $class = ref $object; { # We need to use symbolic references. no strict 'refs'; # Shove the code reference into the class' symbol table. *{$class.'::'.$method_name} = $code_ref; } return; } inject_method($obj, "new_method", sub { ... }); 

Methods in Perl are associated with a class, not an object. To assign a method to a single object, you must put this object in your class. As above, but you need to create a subclass for each instance.

 my $instance_class = "_SPECIAL_INSTANCE_CLASS_"; my $instance_class_increment = "AAAAAAAAAAAAAAAAAA"; sub inject_method_into_instance { my($object, $method_name, $code_ref) = @_; # Get the class of the object my $old_class = ref $object; # Get the special instance class and increment it. # Yes, incrementing works on strings. my $new_class = $instance_class . '::' . $instance_class_increment++; { # We need to use symbolic references. no strict 'refs'; # Create its own subclass @{$new_class.'::ISA'} = ($old_class); # Shove the code reference into the class' symbol table. *{$new_class.'::'.$method_name} = $code_ref; # Rebless the object to its own subclass bless $object, $new_class; } return; } 

I left the code to check if this instance does not have this method, checking if its class matches /^${instance_class}::/ . I leave this as an exercise for you. Creating a new class for each object is not cheap and will cost memory.


There are good reasons for this, but they are exceptional. You really have to really wonder if you should do this monkey correction . Generally, distance action should be avoided.

Can you accomplish the same thing using a subclass, delegation, or role?

Perl OO systems already exist that will do this for you and more. You must use it. Moose , Moo (via Role :: Tiny ), and Mouse can add roles to the instance.

+9
source share

All Articles