Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modern Perl: how to implement Redispatching Methods in AUTOLOAD()?

Tags:

oop

perl

Feeling weak in OO topic i try to get improved with Modern Perl book. About asked topic i found in book following example:

package Proxy::Log; 

sub new
{
    my ($class, $proxied) = @_;
    bless \$class, $proxied;
}

sub AUTOLOAD
{
    my ($name) = our $AUTOLOAD =~ /::(\w+)$/;
    Log::method_call( $name, @_ );
    my $self = shift;
    return $$self->$name( @_ );
}

Is this code just a scratch or working example?

I don't uderstand, how could i use it, where and what should it log and how should i create an object (what should get to $proxied)?

I added just few lines to test it, but got no AUTOLOAD-functionality:

package main;

my $tst = Proxy::Log->new();
say $tst->AnyKindOfSub();

I hope you could lead me to some working code with it. I thougt i got idea, how closures and AUTOLOAD works, but i am here a little jammed.

like image 217
w.k Avatar asked Dec 16 '22 16:12

w.k


2 Answers

As bvr noticed, you have flipped your arguments to bless in the constructor. So while that is the immediate problem with your code, an important consideration when writing redispatching methods is to use the goto &sub syntax to erase the stack frame of the AUTOLOAD call:

sub AUTOLOAD
{
    my ($name) = our $AUTOLOAD =~ /::(\w+)$/;
    Log::method_call( $name, @_ );
    my $self = shift;
  # return $$self->$name( @_ );   # instead of this, use the following:
    unshift @_, $$self;           # setup argument list
    goto &{ $$self->can($name) }  # jump to method
}

If the redispatched method uses the caller builtin for anything (installing methods, localizing variables, Carp error reporting...), then this technique will keep caller working properly. Using the original return $$self->$name(@_) line would always report that caller was the last line of the AUTOLOAD sub, which in turn could be the source of hard to find bugs.

If you want to improve the error reporting a bit, you could write the last line as:

 goto &{ $$self->can($name) or Carp::croak "no method '$name' on $$self" };

Which assumes that the Carp package has been loaded.

like image 165
Eric Strom Avatar answered May 21 '23 14:05

Eric Strom


I think the example have switched bless parameters in new of Proxy::Log. It probably should be:

bless \$proxied, $class;

Find below a functional example, as it was probably intended. The proxy class writes the log and then re-dispatch call to target object (Another class in example below).

package Proxy::Log;

sub new {
    my ($class, $proxied) = @_;
    bless \$proxied, $class;
}

sub AUTOLOAD {
    my ($name) = our $AUTOLOAD =~ /::(\w+)$/;
    warn "$name: @_";
    my $self = shift;
    return $$self->$name( @_ );
}

package Another;

sub new { 
    bless {}, $_[0];
} 

sub AnyKindOfSub {
    warn "Target called\n";
    return "Hello";
};


package main;

my $tst = Proxy::Log->new(Another->new);
say $tst->AnyKindOfSub();
like image 23
bvr Avatar answered May 21 '23 13:05

bvr