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

I know that i have the code for this somewhere, but I seem to have forgotten how to do this.

I have a constructor for one class, and I want to pass the reference to another subroutine to it. Let's say that I have a Collection, and I want to create it with a specified Sorter.

my $collection = Collection->new({ 'sorter' => Sort::mysorter });

Collection->new() has the following:

sub new { my ($self, $options) = @_; . . . $self->{sorter} = /&{$options->{sorter}}; }

Now, $self->{sorter} is a CODEREF.

Then, when you want to invoke the subroutine you do:

$self->{sorter}($mystuffToSort);

I get an "undefined subroutine" error whenever the subroutine lives outside the calling class. Can you help a forgetful old man?

DrSax

Replies are listed 'Best First'.
Re: Passing a reference to a subroutine in a constructor
by tcf22 (Priest) on Aug 22, 2003 at 18:41 UTC
    I think this is what you want(untested)
    my $collection = Collection->new({ 'sorter' => \&Sort::mysorter }); ... sub new { my ($self, $options) = @_; . . . $self->{sorter} = $options->{sorter}; }
    You actually pass the CODEREF into the constuctor, instead of using a soft reference.
      I get the same result when I do that. Is the execution of the sort method itself?
      $self->{sorter}($keys);
      Dr. Sax
        $self->{sorter}->($keys);

        Note the squigglies when de-referencing $self and the round parens when de-referencing the sort routine coderef.

        ------
        We are the carpenters and bricklayers of the Information Age.

        The idea is a little like C++ templates, except not quite so brain-meltingly complicated. -- TheDamian, Exegesis 6

        Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

Re: Passing a reference to a subroutine in a constructor
by bobn (Chaplain) on Aug 22, 2003 at 18:48 UTC

    After making '/' a '\', it works for me:

    my $collection = Collection->new({ 'sorter' => Sort::mysorter }); $collection->doit; package Collection; sub new { my ($class, $options) = @_; $self->{sorter} = \&{$options->{sorter}}; bless {}, $class; } sub doit { print "iam doit\n"; $self->{sorter}->($mystuffToSort); } package Sort; sub mysorter { print "iam mysorter\n"; }

    On the other hand, I'm not sure what you meant by "out of the scope of the calling package".

    All code given here is UNTESTED unless otherwise stated.

    --Bob Niederman, http://bob-n.com
      Your approach only seems to work for me when the package is in the same file. All of our classes/packages live in seperate files. Could that have an impact?

      DrSax

        Just wondering: have you actually -use-d or -require-d the other packages?

        I realize this is equivalent to asking whether the computer is plugged in, but you never know ;-)

        Liz

Re: Passing a reference to a subroutine in a constructor
by CombatSquirrel (Hermit) on Aug 22, 2003 at 18:42 UTC
    I suppose you rather mean
    my $collection = Collection->new({ 'sorter' => \&Sort::mysorter }); sub new { my ($self, $options) = @_; . . . $self->{sorter} = $options->{sorter}; }
    As to my knowledge, this should work (notice the slash - backslash difference).
    Cheers, CombatSquirrel.
      To all who noted the backslash, I have the correct slash direction in my real code. The code I posted was typed in lovingly by hand into the web page since I can't cut and paste between the computer I develop on (Sparcstation) and the one I Monk on (Windows). It doesn't seem to matter whether I use the form:
      my $collection = Collection->new({ 'sorter' => Sort::mysorter }); sub new { my ($self, $options) = @_; . . . $self->{sorter} = \&$options->{sorter}; }
      or...
      my $collection = Collection->new({ 'sorter' => \&Sort::mysorter }); sub new { my ($self, $options) = @_; . . . $self->{sorter} = $options->{sorter}; }
      When I get to :
      $self->sorter($stuffToSort);
      or $self->sorter->($stuffToSort); I get the same results.

      DrSax

        here it is in separate files works fine:

        [bobn@trc2:/home/bobn/misc]# cat subref.pl #!/usr/bin/perl -w use lib q/./; use Collection; use Sort; my $collection = Collection->new({ 'sorter' => \&Sort::mysorter }); $collection->doit('here is something'); [bobn@trc2:/home/bobn/misc]# cat Collection.pm package Collection; my $self = {}; sub new { my ($class, $options) = @_; $self->{sorter} = $options->{sorter}; bless $self, $class; } sub doit { my ($self, $stuffToSort ) = @_; print "in doit: $stuffToSort\n"; $self->{sorter}->($stuffToSort); print "in doit: $stuffToSort\n"; } 1; [bobn@trc2:/home/bobn/misc]# cat Sort.pm package Sort; sub mysorter { print "in mysorter, @_\n"; } 1; [bobn@trc2:/home/bobn/misc]# ./subref.pl in doit: here is something in mysorter, here is something in doit: here is something

        All code given here is UNTESTED unless otherwise stated.

        --Bob Niederman, http://bob-n.com