Perl v5.26x属性,如装饰器/覆盖层,Subs预处理和后处理

wqlqzqxt  于 2023-03-30  发布在  Perl
关注(0)|答案(3)|浏览(136)

我正在尝试理解subs的Perl属性。例如,我期望得到作为输出:

before
middle
after
125

但是我有:

before
middle
after
middle
124

如何使用属性作为主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 ) ) ;

给管理员的。拜托,别抽烟了!

d7v8vwbk

d7v8vwbk1#

您忽略的关键信息是decorator在编译时被调用一次。
decorator将需要用一个包围真实&c的子来替换&c,以获得所需的结果。

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;
}

受试:

sub f :Wrap(
    sub { $_[0], $_[1]+50 },
    sub { $_[0]+7000 },
) {
    $_[1]+600
}

say __PACKAGE__->f( 4 );  # 7654

注意:这使用了OP自己的答案中的调用约定,这与OP问题中的调用约定不同。

plicqrtu

plicqrtu2#

以下是怀疑论者的解决方案。方法的输入和输出过滤器分别:

use Attribute::Handlers ;
use JSON ;

my $json = JSON->new( )->utf8( 0 ) ;

sub json_input:ATTR {
    my ( undef( ) , $symbol, $referent , undef( ) , $data ) = @_ ;

    $data ||= [ 0 ] ;

    *$symbol = sub {
        return $referent->( @_ ) unless @_ > 1 ;

        my ( $self , @args ) = @_ ;
        my @idxs = keys ( %{
            { +
                map {
                    do {
                        if ( $_ >= 0 ) {
                            $_
                        } else {
                            @args + $_
                        }
                    } => undef( ) ,
                } grep {
                    ( abs( $_ ) >=0 ) && ( abs( $_ ) < @args )
                } @$data
            }
        } ) ;

        $_ = $json->decode( $_ ) foreach grep { defined( $_ ) } @args[ @idxs ] ;

        $referent->( $self , @args )
    }
}
sub json_output:ATTR {
    my ( undef( ) , $symbol, $referent ) = @_ ;

    *$symbol = sub {
        my @data = $referent->( @_ ) ;

        $json->encode( @data > 1 ? \@data : @data )
    }
}
sub test
    :json_input( 0 , 2 , 10 ) # позиции аргументов, которые нужно раскодировать перед применением
    :json_output
{
    my ( $self , $data0 , $data1 , $data2 ) = @_ ;

    $data0->{ 'a' } ,
    $data2->{ 'x' }
}

print( __PACKAGE__->test( << '.' ,  << '.' ,  << '.' ) ) ; # many string arguments
{
    "a" : { "b" : "d" } ,
    "c" : "d"
}
.
{
    "d" : { "e" : "r" } ,
    "c" : "d"
}
.
{
    "x" : { "e" : "r" } ,
    "c" : "d"
}
.

+1

看一下sub test:json_input( 0 , 2 ):json_output

mbjcgjjk

mbjcgjjk3#

... the prototype is wrong and it never would to work... Let's try.

这是画布或划痕。这个想法。
我现在接受的版本:

sub decorator( $\*&\@\@$$$ ):ATTR {
    my ( undef( ) , $symbol, $referent , undef( ) , $data ) = @_ ;
    my ( $before , $after ) = @$data ;

    *$symbol = sub {
        $after->( $referent->( $before->( @_ ) ) )
    }
}

相关问题