Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Hijacking a method

by Transient (Hermit)
on Jun 17, 2009 at 16:27 UTC ( #772462=perlquestion: print w/replies, xml ) Need Help??

Transient has asked for the wisdom of the Perl Monks concerning the following question:

I'd like to hijack an object's method to do some logging around it. I'm modifying some legacy code, and could simply call the logging functions around each call to the object's method, but I'm lazy. I also don't have (and don't want) explicit access to the object's method code - I don't want to mess up the internals.

Basically,I have this:
$obj->doSomething();
and the end result might be this:
$myFunkyLogMachine->Log("hi!"); $obj->doSomething(); $myFunkyLogMachine->Log("bye!");


My gut reaction is to go with symbol tables, but I'm unsure of how to proceed. I am also wondering how I would reference the outside object (in this case, the $myFunkyLogMachine object) inside the changed sub.

My initial thoughts were something like:
*obj::doSomething2 = \&obj::doSomething; *obj::doSomething = sub { $myFunkyLogMachine->Log("hi!"); $obj->doSome +thing2(@_); $myFunkyLogMachine->Log("bye!"); }


but I have a feeling that isn't anywhere close to correct.

Thanks in advance for any help.

Replies are listed 'Best First'.
Re: Hijacking a method
by lostjimmy (Chaplain) on Jun 17, 2009 at 17:06 UTC
    I can highly recommend Hook::LexWrap. It does precisely what you are asking for.
    package Obj; sub new { my $class = shift; my $id = shift; return bless { id => $id }, $class; } sub method1 { my $self = shift; print "Object $self->{'id'}: Method 1\n"; } sub method2 { my $self = shift; print "Object $self->{'id'}: Method 2\n"; } package main; use Hook::LexWrap; wrap 'Obj::method1', pre => sub { print "PRE method1\n" }, post => sub + { print "POST method1\n\n" }; wrap 'Obj::method2', pre => sub { print "PRE method2\n" }, post => sub + { print "POST method2\n\n" }; my $object1 = new Obj(1); my $object2 = new Obj(2); $object1->method1; $object1->method2; $object2->method1; $object2->method2; ## OUTPUT PRE method1 Object 1: Method 1 POST method1 PRE method2 Object 1: Method 2 POST method2 PRE method1 Object 2: Method 1 POST method1 PRE method2 Object 2: Method 2 POST method2
      Fantastic! That's exactly what I'm looking for! Thanks lostjimmy =)

      FWIW I did actually get this code to work, but I'm not sure how safe/portable/full of holes it is:
      *Obj::myFunkyLogHandler = \$myFunkyLogHandler; *Obj::doSomething2 = \&Obj::doSomething; *Obj::doSomething = sub { $myFunkyLogHandler->Log("Hello there!"); my $ret_val = Obj::doSomething2( @_ ); $myFunkyLogHandler->Log("Bye for now!"); return $ret_val; };
      so I guess I was a little closer to what I wanted than I thought!
        I would guess this method is the most portable since you don't need to install anything from CPAN to get it to work and this works on most (if not all) perls. The sub-class and/or cpan methods are certainly cleaner and more readable, but this is definitely portable.

        -Paul

        Like jettero says, your version is very portable, and should be safe for what you are using it for. It is essentially what LexWrap does, minus some additional features and error checking.

        On the other hand, LexWrap is a pure perl module, so there's really no issue with just including it with your script if you're looking for a clean wrapping module to use.

Re: Hijacking a method
by ELISHEVA (Prior) on Jun 17, 2009 at 17:15 UTC

    CPAN is your friend. There are a number of modules available that automatically prepend or append standard code to a set of functions. There source code should give you some ideas about how to go about this. Better yet, download them and try them out yourself:

    • Sub::WrapPackages wraps all subs in a package with pre/post processing.
    • Attribute::Handlers lets you assign attributes to subs. The attributes can be mapped to routines that prepend or postpend code to a subroutine. Complex and quirky and prone to lots of redundant coding. Perl also has limited built-in support for defining custom subroutine attributes, but it doesn't work well for subroutine definition. See these these threads: Support for attributes:get in Attribute::Handlers? and its follow-up for more information if you are interested. A newer module Sub::Attribute claims to be a slimmed down version of AttributeHandlers. You might want to check that out too.
    • Sub::Override subroutines and syntactic sugar for overriding individual functions
    • Sub::Versive another modules for automatically prepending and appending code to subroutines
    • Sub::Prepend provides simple support for prepending code to existing subs.

    This is just for starters. There are several more: just search CPAN on the word "sub" to see what is out there.

    Best, beth

Re: Hijacking a method
by DStaal (Chaplain) on Jun 17, 2009 at 16:58 UTC

    Probably the 'cleanest' way to do this would be to sub-class the object's class, and a create a method that does the logging and calls the parent's method. Something like this:

    package Sub::Object; use base qw(Orig::Obj); sub doSomething { my $self = shift @_; $myFunkyLogMachine->Log("hi!"); my $value = $self->SUPER::doSomething(@_); $myFunkyLogMachine->Log("hi!"); return $value; }

    Then you would just use Sub::Object in place of Orig::Obj and you'd get the logging.

      I was considering doing something like this, but it would require a lot of changes in the legacy code (script that's calling the object) - and I'm lazy =). I was hoping to just add a change to the beginning of the file (explicitly to the symbol table) and have the results, without going in and changing too much code.

        Not a large number of changes... perl -pi -e 's/Orig::Obj/Sub::Object/g' should do it. ;)

        But it looks like there are simpler ways.

Re: Hijacking a method
by Unforgiven (Hermit) on Jun 17, 2009 at 16:57 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://772462]
Approved by ELISHEVA
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2020-07-03 19:50 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?