Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Perl > Perl Misc > Passing function references in the constructor

Reply
Thread Tools

Passing function references in the constructor

 
 
koszalekopalek@interia.pl
Guest
Posts: n/a
 
      06-24-2007
Hello,

I would like to create a number of objects of the same class (mypack)
and specify a reference to a function in the constructor. I would like
to
let the module user either use a predefined function (e.g. test1)
provided in the class or let him write his own function and provide
a reference to it in the constructor.

The example below does just that.

I create object $obj1 of class mypack and use a predefined sub test1.
Then I create $obj2 and pass a reference to a custom function
customtest.

The problem is that the two functions
$obj1->riddle (1, 2, 3);
and
$obj2->riddle (1, 2, 3);
receive different arguments depending on where they come from.
The first argument passed to the function test1 is the name of the
class (and then comes the number, i.e. 1). If it is customtest, it
does
not get the name of the class.


This is the output of the attached program:
---- test1 args ----
mypack
1
---- customtest args ----
1
2

What would be the most elegant way to tackle this problem? I'd
rather
not shift arguments in test1 or do anything like that. Maybe there
is
a better way to pass function references in the constructor?


#!/usr/bin/perl

{
package mypack;

sub new {
my $class = shift;
$self = { @_ };
bless $self, $class;
return $self;
};

sub riddle {
my $self = shift;
my $testref = \&test;
my $testref = $self->{fun};
&$testref (@_);
};


sub test1 {
print "---- test1 args ----\n";
print @_[0], "\n";
print @_[1], "\n";
};



};


sub customtest {
print "---- customtest args ----\n";
print @_[0], "\n";
print @_[1], "\n";
}


my $obj1 = mypack->new(
fun => sub { mypack->test1 (@_) }
);
$obj1->riddle (1, 2, 3);


my $obj2 = mypack->new(
fun => \&customtest
);
$obj2->riddle (1, 2, 3);

 
Reply With Quote
 
 
 
 
Michele Dondi
Guest
Posts: n/a
 
      06-24-2007
On Sun, 24 Jun 2007 13:52:35 -0700, http://www.velocityreviews.com/forums/(E-Mail Removed) wrote:

>I would like to create a number of objects of the same class (mypack)
>and specify a reference to a function in the constructor. I would like
>to
>let the module user either use a predefined function (e.g. test1)
>provided in the class or let him write his own function and provide
>a reference to it in the constructor.


So far, so fine: just use something like

my ($class, $code)=@_;
# ...
$code=\&test1 unless 'CODE' eq ref $code;

>The problem is that the two functions
> $obj1->riddle (1, 2, 3);
>and
> $obj2->riddle (1, 2, 3);


These are not functions, but methods.

>receive different arguments depending on where they come from.


?!?

>The first argument passed to the function test1 is the name of the
>class (and then comes the number, i.e. 1). If it is customtest, it
>does


It depends on how you're calling it. Do you expect it to be called as
a plain sub or as a method? Just take the decision first, make that an
API and stick with it.

>What would be the most elegant way to tackle this problem? I'd
>rather
>not shift arguments in test1 or do anything like that. Maybe there


Why not? It just depends, again, on how you intend to call it.

> sub new {
> my $class = shift;
> $self = { @_ };
> bless $self, $class;
> return $self;
> };


The constructor doesn't seem to handle a subref passed to it in any
particular way. Hence no sign of the defaulting to test1() which you
hinted to.

>
> sub riddle {
> my $self = shift;
> my $testref = \&test;
> my $testref = $self->{fun};


Huh?!? Under warnings this will also issue a... well, warning! Why the
double assignment anyway?

> &$testref (@_);


Whatever, if $testref contains a subref (but then you should really
check somewhere) and if you call it like that, or as in

$testref->(@_); # preferred!

then you have a normal sub call, i.e. no class name or object passed
in as the first argument. Alternatively, you can do:

$self->$testref(@_);

and then it will be called like a method on $self. Whether you want
this, or the other way round as I said is something you must choose in
advance, and then stay consistent.

>my $obj1 = mypack->new(
> fun => sub { mypack->test1 (@_) }
>);


Huh?!? You're passing in an anonymous sun which *in its body* (hence
the problem has NOTHING to do with "passing function references in the
constructor"!) will call your package's test1() as a *method*. You can
use the fully referenced name instead, if you really want to do so:

fun => sub { mypack::test1 (@_) }

But you probably meant just

fun => \&mypack::test1

All in all, the simplest variation of your original code (also fixing
some stylistic issues, what's with all those semicolons?) that still
does no (good) checks, etc. is:

#!/usr/bin/perl

use warnings;
use strict;

{
package Mypack; # all lowercase ones are reserved for pragmatas

sub new {
my $class = shift;
bless { @_ }, $class;
}

sub riddle {
my $self = shift;
($self->{fun} || \&test1)->(@_);
}

sub test1 { print "test1 args: [@_]\n" }
}

my $obj1 = Mypack->new; # defaulting to test1
$obj1->riddle(1, 2, 3);

my $obj2 = Mypack->new( fun => sub { print "customtest args: [@_]\n"
} );
$obj2->riddle(1, 2, 3);

__END__


Alternatively, make riddle() into

sub riddle {
my $self = shift;
my $code = $self->{fun} || \&test1;
$self->$code(@_);
}

And see what happens. For the last time: just choose what you like
best.

After much thinking... I suspect smell of XY problem here. See e.g.
<http://perlmonks.org/?node=XY+problem>.


Michele
--
{$_=pack'B8'x25,unpack'A8'x32,$a^=sub{pop^pop}->(map substr
(($a||=join'',map--$|x$_,(unpack'w',unpack'u','G^<R<Y]*YB='
..'KYU;*EVH[.FHF2W+#"\Z*5TI/ER<Z`S(G.DZZ9OX0Z')=~/./g)x2,$_,
256),7,249);s/[^\w,]/ /g;$ \=/^J/?$/:"\r";print,redo}#JAPH,
 
Reply With Quote
 
 
 
 
Mumia W.
Guest
Posts: n/a
 
      06-25-2007
On 06/24/2007 03:52 PM, (E-Mail Removed) wrote:
> Hello,
>
> I would like to create a number of objects of the same class (mypack)
> and specify a reference to a function in the constructor. I would like
> to
> let the module user either use a predefined function (e.g. test1)
> provided in the class or let him write his own function and provide
> a reference to it in the constructor.
>
> The example below does just that.
>
> I create object $obj1 of class mypack and use a predefined sub test1.
> Then I create $obj2 and pass a reference to a custom function
> customtest.
>
> The problem is that the two functions
> $obj1->riddle (1, 2, 3);
> and
> $obj2->riddle (1, 2, 3);
> receive different arguments depending on where they come from.
> The first argument passed to the function test1 is the name of the
> class (and then comes the number, i.e. 1). If it is customtest, it
> does
> not get the name of the class.
>
>
> This is the output of the attached program:
> ---- test1 args ----
> mypack
> 1
> ---- customtest args ----
> 1
> 2
>
> What would be the most elegant way to tackle this problem? I'd
> rather
> not shift arguments in test1 or do anything like that. Maybe there
> is
> a better way to pass function references in the constructor?
>
>
> #!/usr/bin/perl
>


These lines are missing:

use strict;
use warnings;

Modifying your program to work under these conditions will help you
catch many errors.

> {
> package mypack;
>
> sub new {
> my $class = shift;
> $self = { @_ };
> bless $self, $class;
> return $self;
> };
>
> sub riddle {
> my $self = shift;
> my $testref = \&test;
> my $testref = $self->{fun};
> &$testref (@_);
> };
>
>
> sub test1 {
> print "---- test1 args ----\n";
> print @_[0], "\n";
> print @_[1], "\n";
> };
>


Since you don't do "my $self = shift;", test is not expected to be a
method, but a normal function--which is okay.

>
>
> };
>
>
> sub customtest {
> print "---- customtest args ----\n";
> print @_[0], "\n";
> print @_[1], "\n";
> }
>
>
> my $obj1 = mypack->new(
> fun => sub { mypack->test1 (@_) }
> );


But here, you invoke 'test' as a class method of the 'mypack' package.
Change that line to the following, and you'll get better results:

fun => sub { test1(@_) }

Or try this:

fun => \&mypack::test1


> $obj1->riddle (1, 2, 3);
>
>
> my $obj2 = mypack->new(
> fun => \&customtest
> );
> $obj2->riddle (1, 2, 3);
>


There are several more problems with your program. Let strictures (use
strict) and warnings (use warnings) tell you about them.

Just by fixing the problems reported by "use strict;" and "use
warnings;", I was able to create an improved version of your program:

#!/usr/bin/perl
use strict;
use warnings;

package Mypack;

sub new {
my $class = shift;
my $self = { @_ };
bless $self, $class;
return $self;
}

sub riddle {
my $self = shift;
my $testref = \&test1;
if ($self->{fun}) { $testref = $self->{fun}; }
&$testref (@_);
}

sub test1 {
print "---- test1 args ----\n";
print $_[0], "\n";
print $_[1], "\n";
}


package main;

sub customtest {
print "---- customtest args ----\n";
print $_[0], "\n";
print $_[1], "\n";
}

my $obj1 = Mypack->new();

my $obj2 = Mypack->new(
fun => \&customtest
);

$obj1->riddle(1, 2, 3);
$obj2->riddle(1, 2, 3);

 
Reply With Quote
 
koszalekopalek@interia.pl
Guest
Posts: n/a
 
      06-26-2007
On Jun 25, 12:46 am, Michele Dondi <(E-Mail Removed)> wrote:

(...)
> But you probably meant just
>
> fun => \&mypack::test1



Thanks to everybody that answered.

Now I understand better what's going on.
I think replacing
fun => sub { mypack::test1 (@_) }
with
fun => \&mypack::test1
is what I was looking for. (I tried that with
the arrow syntax
fun \&mypack->test1
but that gave me the following error:
Backslash found where operator expected at a.pl line 46, near "fun \"

I always (well, almost) use strict - the example I
posted was a chopped down version of what I was
trying to achieve.


> After much thinking... I suspect smell of XY problem here. See e.g.
> <http://perlmonks.org/?node=XY+problem>.


Well, that may be true - I think I have some problems
figuring out what should be enclosed in the class and
what should not.

Thanks again,

K.O.


>
> Michele
> --
> {$_=pack'B8'x25,unpack'A8'x32,$a^=sub{pop^pop}->(map substr
> (($a||=join'',map--$|x$_,(unpack'w',unpack'u','G^<R<Y]*YB='
> .'KYU;*EVH[.FHF2W+#"\Z*5TI/ER<Z`S(G.DZZ9OX0Z')=~/./g)x2,$_,
> 256),7,249);s/[^\w,]/ /g;$ \=/^J/?$/:"\r";print,redo}#JAPH,



 
Reply With Quote
 
Michele Dondi
Guest
Posts: n/a
 
      06-26-2007
On Mon, 25 Jun 2007 22:51:47 -0700, (E-Mail Removed) wrote:

>I think replacing
> fun => sub { mypack::test1 (@_) }
>with
> fun => \&mypack::test1


In fact with that bacslash in front, &mypack::test1 is fundamentally
nothing but a name, which means you're taking a reference to the sub
it refers to.

>is what I was looking for. (I tried that with
>the arrow syntax
> fun \&mypack->test1
>but that gave me the following error:
> Backslash found where operator expected at a.pl line 46, near "fun \"


Missing C< => >?


Michele
--
{$_=pack'B8'x25,unpack'A8'x32,$a^=sub{pop^pop}->(map substr
(($a||=join'',map--$|x$_,(unpack'w',unpack'u','G^<R<Y]*YB='
..'KYU;*EVH[.FHF2W+#"\Z*5TI/ER<Z`S(G.DZZ9OX0Z')=~/./g)x2,$_,
256),7,249);s/[^\w,]/ /g;$ \=/^J/?$/:"\r";print,redo}#JAPH,
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
A constructor calling another constructor (default constructor)? Generic Usenet Account C++ 10 11-28-2007 04:12 AM
trouble passing parameters of a subclass constructor through to it's superclass constructor ingoweiss Javascript 4 05-12-2006 07:43 AM
Difference between bin and obj directories and difference between project references and dll references jakk ASP .Net 4 03-22-2005 09:23 PM
Passing to a function -- object and method names (or references) Midas Python 2 01-02-2004 08:17 PM
Pointers and References (and References to Pointers) Roger Leigh C++ 8 11-17-2003 10:14 AM



Advertisments