Home > Blockchain >  In Perl, can you subclass and hook all parent-class functions without `AUTOLOAD`?
In Perl, can you subclass and hook all parent-class functions without `AUTOLOAD`?

Time:10-29

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 _).

  • Related