Dispatching sub in $var to SUPER without stringy eval

M

Matthew Braid

Hi all,

I've got an object-package which inherits from another. Simple enough.

Both use AUTOLOAD to catch accessor calls (see example below), the
'child' package using eval to dispatch anything it doesn't override to
its parent.

This works, but only if I use the 'stringy' version of eval (eg eval
"This is slow").

Is there an alternate way I can call a class' SUPER with a variable
function call? (The code is probably clearer than my attempt to explain
it :) )

TIA,
MB

----EXAMPLE CODE----

package Foo;
sub new {
my $class = shift;
my $self = bless({}, (ref($class) or $class or __PACKAGE__));
$self->{bar} = "BAR!";
return $self;
}

sub AUTOLOAD {
my $self = shift;
my $var = $AUTOLOAD;
$var =~ s/^.*:://;
return if $var eq 'DESTROY';
if (exists $self->{$var}) {
print "AUTOLOADED $var - $self->{$var}\n";
} else {
die "Unknown accessor $AUTOLOAD";
}
}

package Baz;

use base qw/Foo/;

sub AUTOLOAD {
my $self = shift;
my $var = $AUTOLOAD;
$var =~ s/^.*:://;
return if $var eq 'DESTROY';
if ($var eq 'bar') {
# Add to parent's behaviour
print "BAZ AUTOLOADED $var\n";
}
my $ret;
# EEK - String eval
eval "\$ret = \$self->SUPER::$var(\@_)";
# Unfortunately this:
# eval {$ret = $self->SUPER::$var(@_)};
# does not even compile.
die $@ if $@;
return $ret;
}

package main;

my $baz = Baz->new;
$baz->bar;

----END EXAMPLE CODE----
 
B

Ben Morrow

Matthew Braid said:
Hi all,

I've got an object-package which inherits from another. Simple enough.

Both use AUTOLOAD to catch accessor calls (see example below), the
'child' package using eval to dispatch anything it doesn't override to
its parent.

This works, but only if I use the 'stringy' version of eval (eg eval
"This is slow").

And insecure. In almost every circumstance, string eval is Wrong... :).
Is there an alternate way I can call a class' SUPER with a variable
function call? (The code is probably clearer than my attempt to explain
it :) )

TIA,
MB

----EXAMPLE CODE----

package Foo;
package Baz;

use base qw/Foo/;

sub AUTOLOAD {
my $self = shift;
my $var = $AUTOLOAD;
$var =~ s/^.*:://;
return if $var eq 'DESTROY';
if ($var eq 'bar') {
# Add to parent's behaviour
print "BAZ AUTOLOADED $var\n";
}
my $ret;
# EEK - String eval
eval "\$ret = \$self->SUPER::$var(\@_)";
# Unfortunately this:
# eval {$ret = $self->SUPER::$var(@_)};
# does not even compile.

$var = "SUPER::$var";
eval { $ret = $self->$var(@_) };
die $@ if $@;
return $ret;
}

A few more comments:

1. Have you considered using the 'fields' pragma?
2. It is usual in AUTOLOAD subs to call the sub with goto &sub so as
not to leave an extra entry in the call stack.
3. You are not considering the context in which you are called. See
'wantarray' in perlfunc.
4. What is the point of the eval {} ?

Ben
 
M

Matthew Braid

Ben said:
And insecure. In almost every circumstance, string eval is Wrong... :).






$var = "SUPER::$var";
eval { $ret = $self->$var(@_) };




A few more comments:

1. Have you considered using the 'fields' pragma?
2. It is usual in AUTOLOAD subs to call the sub with goto &sub so as
not to leave an extra entry in the call stack.
3. You are not considering the context in which you are called. See
'wantarray' in perlfunc.
4. What is the point of the eval {} ?

Ben

D'oh. Just needed to move the quotes around :) Thanks.

1. Haven't come across 'fields' yet - will look into it.
2. I make the AUTOLOAD this way so I don't _have_ to make a sub. In the
case of the dispatching call (ie where Baz overrode Foo) I could do so,
but I've often had trouble mixing OO with goto&. The docs don't mention
it at all, so I'm never quite sure what I'm supposed to put - goto
&$self->SUPER::whatever?
3. Good point. I'm pretty sure all the pseudo-accessors in my real code
always return scalars (straight or refs - I hate mixing scalars and
lists, makes checking for failure with a return undef a pain), but not a
bad idea to have it anyway.
4. I'm going to have to look at this further. Some of this is old code
that I inherited and for the life of me I can't tell you why the eval is
there. I take it that the die results would look the same either way?

MB
 
B

Ben Morrow

Matthew Braid said:
2. I make the AUTOLOAD this way so I don't _have_ to make a sub. In the
case of the dispatching call (ie where Baz overrode Foo) I could do so,
but I've often had trouble mixing OO with goto&. The docs don't mention
it at all, so I'm never quite sure what I'm supposed to put - goto
&$self->SUPER::whatever?

No, you have to make sure that you *don't* shift the object off @_ and
then call

goto &SUPER::method;

.. I have to confess that I'm not entirely sure what this does to
method dispatch... so it's possible that it won't work if &method is
defined in a super-super-class. :)
4. I'm going to have to look at this further. Some of this is old code
that I inherited and for the life of me I can't tell you why the eval is
there. I take it that the die results would look the same either way?

Yes... I guess someone may have been thinking to catch the fact that
the method didn't exist, and never got round tuit... UNIVERSAL::can is
a better choice for that, though.

Ben
 
M

Matthew Braid

Ben said:
No, you have to make sure that you *don't* shift the object off @_ and
then call

goto &SUPER::method;

. I have to confess that I'm not entirely sure what this does to
method dispatch... so it's possible that it won't work if &method is
defined in a super-super-class. :)

It looks like goto&, OO and AUTOLOAD don't mix well. The code at the end
of this message resulted in:

TEST2: BAZ
BAZ ERROR: Goto undefined subroutine &SUPER::baz at test.pl line 46.

BLING ERROR: Goto undefined subroutine &SUPER::bling at test.pl line 46.

ULTRATEST: GRUMBLE!
which means goto& worked for when the function is defined in one of the
superclasses (SUPER::new worked as did SUPER::grumble), but not when the
function is emulated by AUTOLOAD (SUPER::baz failed) or when its defined
in a super-super class (SUPER::bling failed).
Yes... I guess someone may have been thinking to catch the fact that
the method didn't exist, and never got round tuit... UNIVERSAL::can is
a better choice for that, though.

I'll look into UNIVERSAL::can. In the mean time I'll just get rid of the
eval entirely, save myself some opcodes :)

MB
 
M

Matthew Braid

Matthew said:
It looks like goto&, OO and AUTOLOAD don't mix well. The code at the end
of this message resulted in:

TEST2: BAZ
BAZ ERROR: Goto undefined subroutine &SUPER::baz at test.pl line 46.

BLING ERROR: Goto undefined subroutine &SUPER::bling at test.pl line 46.

ULTRATEST: GRUMBLE!
which means goto& worked for when the function is defined in one of the
superclasses (SUPER::new worked as did SUPER::grumble), but not when the
function is emulated by AUTOLOAD (SUPER::baz failed) or when its defined
in a super-super class (SUPER::bling failed).



I'll look into UNIVERSAL::can. In the mean time I'll just get rid of the
eval entirely, save myself some opcodes :)

MB
Whoops - here's the code:

package UltraTest;

sub grumble {
print "ULTRATEST: GRUMBLE!\n";
}

package SuperTest;

sub bling {
print "SUPERTEST: BLING!\n";
}

package Test;

use base qw/SuperTest/;

sub new {
my $class = shift;
return bless({}, (ref($class) or $class or __PACKAGE__));
}

sub AUTOLOAD {
my $self = shift;
my $var = $AUTOLOAD;
$var =~ s/^.*:://;
if ($var eq 'baz') {
print "TEST: BAZ\n";
} else {
die "TEST: FAIL $var!\n";
}
}

package Test2;

use base qw/Test UltraTest/;

sub AUTOLOAD {
my $var = $AUTOLOAD;
$var =~ s/^.*:://;
if ($var eq 'baz') {
print "TEST2: BAZ\n";
}
$var = "SUPER::$var";
goto &$var;
}

package main;

my $test = Test2->new;
eval {$test->baz};
print "BAZ ERROR: $@\n" if $@;
eval {$test->bling};
print "BLING ERROR: $@\n" if $@;
eval {$test->grumble};
print "GRUMBLE ERROR: $@\n" if $@;
 
U

Uri Guttman

MB> It looks like goto&, OO and AUTOLOAD don't mix well. The code at the
MB> end of this message resulted in:

huh? they mix perfectly well together. you just need to know the correct
recipes.


MB> TEST2: BAZ
MB> BAZ ERROR: Goto undefined subroutine &SUPER::baz at test.pl line 46.

MB> BLING ERROR: Goto undefined subroutine &SUPER::bling at test.pl line 46.

SUPER is not a real sub but a pseudo method. so you have to call it via
a method call. magic goto requires a real sub as its argument.

MB> ULTRATEST: GRUMBLE!
MB> which means goto& worked for when the function is defined in one of
MB> the superclasses (SUPER::new worked as did SUPER::grumble), but not
MB> when the function is emulated by AUTOLOAD (SUPER::baz failed) or when
MB> its defined in a super-super class (SUPER::bling failed).

all SUPER does is allow you to call a method on the object but start the
method search in a different (the parent of the current class IIRC)
place. it does nothing different regarding AUTOLOAD. if an AUTOLOAD in
the parent class's @ISA tree is found, it will be called.

MB> I'll look into UNIVERSAL::can. In the mean time I'll just get rid of
MB> the eval entirely, save myself some opcodes :)

don't mung UNIVERSAL as it will be visible to every other module
forever. not a smart thing.

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top