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

Hi PerlMonks,

It maybe a simple thing, but I seem to have hit a brick wall.

I have a very simple class and in the constructor, I am creating a hash that is holding references to subs which are members of the same class as well.

see below:
package EOYStats; use strict; use warnings; use Data::Dumper; sub new { my $class = shift; my $this = bless {}, $class; $this->{ACT} = shift; %{ $this->{ACTIVITIES} } = ( 'Sale' => { 'func' => \&total_sold, 'datefield' => "date_sold", }, 'Purchae' => { 'func' => \&total_bought, 'datefield' => "date_bought", }, ); return $this; } sub get_stats { my $this = shift; my $arg = shift; $this->{ACTIVITIES}->{$this->{ACT}}->{'func'}->($arg); } sub total_sold { my $this = shift; my $arg = shift; $this->dosomething(); } sub total_bought { my $this = shift; my $arg = shift; # Do stuff.... } sub dosomething { my $this = shift; print "This is test...\n"; } return 1;
Now, when I call total_sold from another method in this class (get_stats in this case), '$this' is undefined in total_sold and '$this->dosomething' returns an error 'Can't call method "dosomething" on an undefined value at EOYStats.pm line 37.'

There is no real need to have these methods in a hash. I can simply write conditions in the get_stats and call the methods accordingly, but I wanted to try and see if this worked.

so is it really ridiculously bad idea to do this or is it not possible or simply silly thing to do?

Sorry if this is too confusing.

Replies are listed 'Best First'.
Re: Hash of subroutines as member of a class
by tangent (Parson) on Sep 30, 2014 at 01:52 UTC
    Not a bad idea at all.

    The problem is that you are calling the subroutine as a normal function rather than as an object method. Try changing to:

    my $method = $this->{ACTIVITIES}->{$this->{ACT}}->{'func'}; $this->$method($arg);
    Also, it would be easier to assign to ACTIVITIES without the de-reference:
    $this->{ACTIVITIES} = { Sale => {}, .... };
    Within an object, you don't need to take a reference to the subroutine, so you could just have:
    $this->{ACTIVITIES} = { Sale => { func => 'total_sold', datefield => 'date_sold', }, Purchase => { func => 'total_bought', datefield => 'date_bought', }, };
Re: Hash of subroutines as member of a class
by GrandFather (Saint) on Sep 30, 2014 at 02:44 UTC

    There are a couple of tricks I use in this sort of context. Consider:

    use strict; use warnings; sub new { my ($class, $act) = @_; my %activities = ( ACT => $act, ACTIVITIES => { 'Sale' => {datefield => "date_sold",}, 'Purchae' => {datefield => "date_bought",}, } ); my $self = bless \%activities, $class; return $self; } sub get_stats { my ($self, $arg) = @_; my $funcName = "_do$self->{ACT}"; my $func = $self->can($funcName); die "Class " . ref($self) . " doesn't know how to '$funcName'\n" i +f !$func; $self->$func($arg); } sub _doSale { my ($self, $arg) = @_; $self->dosomething($arg); } sub _doPurchae { my ($self, $arg) = @_; ... } sub dosomething { my ($self, $arg) = @_; print "This is test ($arg)...\n"; } my $obj = main->new('Sale'); $obj->get_stats('wibble');

    Prints:

    This is test (wibble)...

    $self->can lets you preflight the call so you have some control over error handling.

    my $funcName = "_do$self->{ACT}"; ensures only expected members can be called.

    Perl is the programming world's equivalent of English
Re: Hash of subroutines as member of a class
by Athanasius (Archbishop) on Sep 30, 2014 at 03:20 UTC

    Hello Hameed,

    As you don’t provide any example usage, I’m in the dark as to what this code is designed to accomplish. Apparently tangent and GrandFather can see the point to this approach, so there must be one; but...

    It seems to me that you’ve come up with a complex and fragile way of simulating inheritance; and one which also has the disadvantage that each object duplicates the same redundant information. For example:

    bless({ ACT => "Purchase", ACTIVITIES => { Purchase => { datefield => "date_bought", func => "total_bought" } +, Sale => { datefield => "date_sold", func => "total_sold" }, }, }, "EOYStats")

    Consider as an alternative the following skeleton code, which uses simple inheritance:

    #! perl use strict; use warnings; #--------------------------------------------------------------------- +--------- package EOYStats; #--------------------------------------------------------------------- +--------- sub new { my ($class) = @_; my $this = bless {}, $class; use Data::Dump; dd $this; return $this; } sub do_something { my ($this, $arg) = @_; print "EOYStats::do_something($arg)\n"; } #--------------------------------------------------------------------- +--------- package EOYStats::Sale; #--------------------------------------------------------------------- +--------- use parent -norequire, 'EOYStats'; sub get_stats { my ($this, $arg) = @_; print "EOYStats::Sale::get_stats()\n"; $this->do_something($arg); } #--------------------------------------------------------------------- +--------- package EOYStats::Purchase; #--------------------------------------------------------------------- +--------- use parent -norequire, 'EOYStats'; sub get_stats { my ($this, $arg) = @_; print "EOYStats::Purchase::get_stats($arg)\n"; $this->do_something($arg); } #--------------------------------------------------------------------- +--------- package main; #--------------------------------------------------------------------- +--------- my $obj = EOYStats::Purchase->new(); $obj->get_stats('abc');

    Output:

    12:59 >perl 1034_SoPW.pl bless({}, "EOYStats::Purchase") EOYStats::Purchase::get_stats(abc) EOYStats::do_something(abc) 13:09 >

    Would this approach work for you? If so, I think you’ll find it simpler and more robust. And if not, an explanation of where it falls short will help to clarify the problem.

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Hi Athanasius,

      I had to quickly whip up a few lines to avoid posting the original script that I was working on. I would have had to cut down so much and then it would be more nonsensical than this one.

      I was trying to call a method, and it failed to pass the object. That whole point of the question was to find out what was wrong with that call. tangentwas able to answer that. Thanks to him.

      You and GrandFather suggested different ways of doing that. Much appreciated.

      I am sure your suggestion, being proper OO is much better.

      Thank you. :)