I'm trying to understand Perl attributes for subs. For example, I expected to got as output:
before
middle
after
125
But, I have:
before
middle
after
middle
124
How to use attributes as overlays for main sub?
use Attribute::Handlers ;
use Data::Dumper ;
sub decorator( $\*&\@\@$$$ ):ATTR {
my ( $package , $symbol, $referent, $attr , $data, $phase, $filename, $linenum ) = @_ ;
my ( $before , $after ) = @$data ;
# before processing
$before->( @_ ) ;
# decorated sub
my $result = $referent->( ) ;
# after processing
$after->( $result )
}
sub c( $ ):decorator(
# before
sub( $\*&\@\@$$$ ) {
warn 'before' ;
} ,
# after
sub( $ ) {
warn 'after' ;
shift( @_ ) + 1
}
) {
warn 'middle' ;
splice( @_ , 1 ) + 1
}
print( __PACKAGE__->c( 123 ) ) ;
For admins. Please, stop moking!
The key piece of information you're missing is that decorator
is called once at compile time.
decorator
will need to replace &c
with a sub wrapping the true &c
to get the desired result.
use Attribute::Handlers;
use Sub::Name qw( subname );
sub Wrap :ATTR(CODE) {
my ( $pkg, $sym, $old_sub, $attr, $data, $phase, $fn, $ln ) = @_;
my ( $before, $after ) = @$data;
my $name = *{ $sym }{ NAME };
my $new_sub = subname "wrapped_$name", sub {
wantarray
? $after->( $old_sub->( $before->( @_ ) ) )
: $after->( scalar( $old_sub->( $before->( @_ ) ) ) )
};
no warnings qw( redefine );
*$sym = $new_sub;
}
Tested:
sub f :Wrap(
sub { $_[0], $_[1]+50 },
sub { $_[0]+7000 },
) {
$_[1]+600
}
say __PACKAGE__->f( 4 ); # 7654
Note: This uses the calling convention from the OP's own answer, which differs from the calling convention in the the OP's question.