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

Hi, I'm having problems with getting a method in my object to dispatch to other methods within the object.
Here's an example without an object, showing what I'm doing:
sub first { print "This is first\n"; } sub second { print "This is second\n"; } sub dispatch { no strict 'refs'; my $command = shift; print "In dispatch, command = $command\n"; my %dispatch = ( 'a'=>'first', 'b'=>'second' ); &{$dispatch{$command}}(); } dispatch('a'); dispatch('b');

The output for this is:
In dispatch, command = a
This is first
In dispatch, command = b
This is second

Here I use the same concept with objectified code:
package FOO; sub first { print "This is first\n"; } sub second { print "This is second\n"; } sub dispatch { my $self = shift; no strict 'refs'; my $command = shift; print "In dispatch, command = $command\n"; my %dispatch = ( 'a'=>'first', 'b'=>'second' ); &{$self->{$dispatch{$command}}}(); } sub new { my $class = shift; my $self = {}; bless $self, $class; return $self; } package ::; my $foo = new FOO(); $foo->dispatch('a'); $foo->dispatch('b');

But now, it fails with the following error:
Undefined subroutine &main:: called at ./dispatch2.pl line 15.

My best guess is that perl is taking the curly braces after the $self-> and interpreting that to mean that to mean everything inside refers to a key in the blessed hash, instead of evaluating everything and recognizing it as a function call. Any suggestions on how to get around this? -Dan

Replies are listed 'Best First'.
Re (tilly) 1: Dispatching Method Objects
by tilly (Archbishop) on Oct 03, 2001 at 21:23 UTC
    Try the following in your objectified dispatch code:
    sub dispatch { my $self = shift; my $meth = { a=>"first", b=>"second" }->{shift(@_)}; $self->$meth(); }
    (You can add error checks, etc.)

    BTW if you can, it makes more sense to drop your dispatch all together and let client code look for the methods by name. And then you can put error checks in an AUTOLOAD.

Re: Dispatching Method Objects
by gbarr (Monk) on Oct 03, 2001 at 21:25 UTC
    You are right that the error is the $self-> in the sub call. Lets see what it's doing

    &{ # call the result of the block as a sub $self->{ # lookup in hash $self the result of $dispatch{$command} # lookup $command in %dispatch } }(); # call the sub with no arguments

    I am guessing you want to call the sub, passing $self, which should be

    &{$dispatch{$command}}($self);
Re: Dispatching Method Objects
by Masem (Monsignor) on Oct 03, 2001 at 21:17 UTC
    How about using an AUTOLOAD method? When you pass your object a function it doesn't explicitly know about, AUTOLOAD is invoked; at that point, if the 'function name' is on your dispatch table, you can then call the appropriate dispatch function directly.

    Note that you also could use coderefs as opposed to just strings as to avoid problems with strict 'refs' and the like. (eg: %dispatch = ( a => \&first, b => \&second );).

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    It's not what you know, but knowing how to find it if you don't know that's important

Re: Dispatching Method Objects
by Fletch (Bishop) on Oct 03, 2001 at 21:24 UTC
    ... my $method = $dispatch{ $command }; $self->$method( ); ...

    Or you can be more bullet proof.

    my $method = $self->can( $dispatch( $command ) ); unless( defined $method ) { warn "I don't understand `$command'\n"; return; } $self->$method( );
Re: Dispatching Method Objects
by suaveant (Parson) on Oct 03, 2001 at 21:27 UTC
    Well, that is because in your object you now have a package, and what once worked in your main code, is now bundled away in FOO::...

    I would suggest something along the following..

    package FOO; sub first { print "This is first\n"; } sub second { print "This is second\n"; } sub dispatch { my $self = shift; no strict 'refs'; my $command = shift; print "In dispatch, command = $command\n"; &{$self->{dispatch}{$command}}(); } sub new { my $class = shift; my $self = {}; bless $self, $class; $self->{dispatch} = { 'a'=> $self->can('first'), 'b'=> $self->can('s +econd') }; return $self; } package ::; my $foo = new FOO(); $foo->dispatch('a'); $foo->dispatch('b');
    I took references to the methods using can (since I cannot think off the top of my head how to do it otherwise...). It is MUCH better to use code refs than do symbol table lookups on strings... but to do a successful symbol table lookup you would do
    $self->$command();
    I also move the definition of your dispatch table into the new method, since you should only have to generate this once....

                    - Ant
                    - Some of my best work - Fish Dinner

Re: Dispatching Method Objects
by runrig (Abbot) on Oct 03, 2001 at 21:22 UTC
    Use coderefs:
    my %dispatch = ('a'=>\&first, 'b'=>\&second); sub dispatch { my $self = shift; my $command = shift; $dispatch{$command}->($self, @_); }
    Note this removes the OO'ness of it, i.e. no inheritance.
    Update:Realized this and was updating just as tilly was replying :)

    Possibly another option is to prod Damian into finishing Class::Delegation :-)

      Whether or not to use code refs depends on what you are doing. If you use code refs your dispatch will ignore inheritance. If you the dynamic method lookup that I posted, your dispatch will allow subclasses to usefully override the method that you are dispatching to.