I'm writing a subclass that encapsulates multiple objects of the parent class so I can call functions sort-of like a vector, something like this:
package OriginalClass;
sub new { return bless {bar => 123}, 'OriginalClass' }
sub foo { return shift->{bar}; }
1;
package NewClass;
use parent OriginalClass;
# Return a blessed arrayref of "OriginalClass" objects.
# new() would be called NewClass->new(OriginalClass->new(), ...)
sub new {
my $class = shift;
return bless \@_, 'NewClass';
}
# Vectorized foo(), returns a list of SUPER::foo() results:
sub foo
{
my $self = shift;
my @ret;
push @ret, $_->SUPER::foo() foreach @$self;
return @ret;
}
1;
I don't want to write a new vectorized function in NewClass
for each function in OriginalClass
, particularly for when OriginalClass
adds new functions to be maintained (vectorized) in NewClass
.
Question:
As I understand AUTOLOAD
is slow, so is there a way to vectorize calls OriginalClass
via something like NewClass
without AUTOLOAD
?
CodePudding user response:
As I understand
AUTOLOAD
is slow
If AUTOLOAD
generates the missing sub, then only the first call is "slow" since subsequent calls of the same method don't result in AUTOLOAD
being called at all.
package NewClass;
use strict;
use warnings;
sub new {
my $class = shift;
return bless( \@_, $class );
}
sub AUTOLOAD {
my $method_name = our $AUTOLOAD =~ s/^.*:://sr;
my $method = sub {
my $self = shift;
return map { $_->$method_name( @_ ) } @$self;
};
{
no strict 'refs';
*$method_name = $method;
}
goto &$method;
}
1
Note that I didn't use parent
and SUPER::
. This isn't an inheritance relationship. And it would prevent AUTOLOAD
from getting called since AUTOLOAD
is only called when a method doesn't exist.
You can use Sub::Name to "name the sub" for better diagnostics.
use Sub::Name qw( subname );
my $method = subname $method_name => sub { ... };
But yes, AUTOLOAD
can be avoided here, as long as you can get a list of the method names in advance.
package NewClass;
use strict;
use warnings;
sub new {
my $class = shift;
return bless( \@_, $class );
}
for my $method_name (qw( foo ... )) {
my $method = sub {
my $self = shift;
return map { $_->$method_name( @_ ) } @$self;
};
no strict 'refs';
*$method_name = $method;
}
1
The above uses a hardcoded list, but more dynamic solutions are possible. For example, the list could be obtained from inspecting the contents of the OriginalClass namespace for subs (filtering out new
and anything else inappropriate such as names starting with _
).