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

I've got an object. Within the object there is a method. Within the method there is this line that establishes the handler for a HTML::Parser object:

$parser->handler(start => \&_add_link, 'tagname,attr');

The callback routine, _add_link, is another method within the object and needs access to some of the object's data to work properly. However, the above code doesn't do the trick because \&_add_link calls the object method directly since it doesn't use the arrow operator like so: \$self->_add_link

However, when I tryin using a reference to the method, I get:

Only code or array references allowed as handler at module.pm line 34.

I've tried a few variations to get this to work but I still get an error. Thanks.

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff";
$nysus = $PM . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Proper syntax for using a method as a call back routine?
by BrowserUk (Patriarch) on Jul 08, 2003 at 03:28 UTC

    As soon as you take a reference to a method, you have disassociated it from its instance, and converted it to a function. (which it is anyway but blah, blah ...:)

    The way around this is to generate an anonymous sub that calls the method you want and passes the instance handle ($self), retaining it via the closure mechanism, and pass a reference to that anonymous sub as the callback

    $parser->handler( start => sub { $self->_add_link( @_ ) }, 'tagname,attr' );

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      Great! That did the trick. But it seems the more I learn, the less I seem to know. I'm thoroughly confused. Thanks, though!

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff";
      $nysus = $PM . $MCF;
      Click here if you love Perl Monks

        Think of it this way. When you call a method,

        $object->method( 1, 2, 3 );

        What perl does is

        1. Inspects $object, which is a blessed reference of some kind, and finds out the package that it was blessed into.

          You can see this yourself if you print a blessed reference and you get something like  My::Package=HASH(0x1d154d8). In this case $object is a hashref that was blessed into the package My::Package.

        2. It then looks up the address of the sub named 'method' in the package My::Package, and then calls that address passing the list $object, 1, 2, 3 in @_.

          Hence, the first thing you shift off @_ within the method is $self. Ie. The object upon which the method was called.

        However, when you take the address of a method

        my $codref = \&$object->method;
        and then later invoke it (or have the parser invoke for you),
        $coderef->( 1, 2, 3 );

        Perl no longer recognises that you are invoking a method rather than a function, and therefore doesn't add $object to the stack. Even if it knew that this was a method call, it no longer knows which package (or which instance of that package) was used to derive the address of the method. So it wouldn't know what $object to pass.

        Indirecting the call through a closure simply allows you to retain the instance handle along with the method to be invoked, with the slight penalty of the two step process of getting there.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


Re: Proper syntax for using a method as a call back routine?
by sgifford (Prior) on Jul 08, 2003 at 03:14 UTC
    When I have to do this, I simply create a very small anonymous sub. Something like:
    $parser->handler(sub { $self->add_link(@_) }, 'tagname,attr');
    That code's not tested, but you get the idea.

    Update:Remove bad parameter I forgot to delete.

    Update:Probably should be $self-> instead of $parser->. Fixed.

Re: Proper syntax for using a method as a call back routine?
by dws (Chancellor) on Jul 08, 2003 at 02:59 UTC
    The callback routine, _add_link, is another method within the object and needs access to some of the object's data to work properly.

    Is this something you could profitably use local for?

    E.g.,

    local $instance = $self; $parser->handler(start => \_add_link, 'tagname,attr'); ... sub _add_link { my $self = $instance; ...
    This makes _add_link a sort of spring-loaded method call.

    Update: Forget this, and go with BrowserUk's suggestion below. Using a closure is a superior approach.

      That isn't going to work. As soon as the method containing the the call to $parser->handler() exits, (or the nearest local scope), then the value assigned to $instance will be forgotten as the global $instance reverts to its former value and whne the _add_link() routine is called back by the parser, $instance could have just about any value.


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


        That isn't going to work.

        I assumed that the parser would be called on to do its thing within the scope of the current method. If that's not the case, consider stashing $self into a package variable.