Cancel area change code in Perl

It often seems to me that it is useful to be able to schedule code execution when you exit the current area. In my previous life in TCL, a friend created a function that we called defer.

He included code like: set fp [open "x"] defer ("close $ fp");

which is called when leaving the current area. The main advantage is that it is always called no matter how / where I leave the scope.

So, I implemented something similar in Perl, but it seemed to be simpler. Comments are welcome.

How I did it in Perl:

  • create a global, bound variable that contains an array of routines to be executed.
  • whenever I want to assign fn to be called upon exit, I use local to modify the array. when I leave the current scope, Perl changes the global to the previous value because the global is bound, I know when this change in value occurs and can cause subtitles in the list.

Actual code below.

Is there a better way to do this? It seems that this will usually be a necessary opportunity.

use strict;

package tiescalar;

sub TIESCALAR {
    my $class = shift;

    my $self = {};
    bless $self, $class;
    return $self;
}

sub FETCH {
    my $self = shift;
    return $self->{VAL};
}

sub STORE {
    my $self = shift;
    my $value = shift;

    if (defined($self->{VAL}) && defined($value)) {
    foreach my $s (@{$self->{VAL}}) { &$s; }
    }
    $self->{VAL} = $value;
}

1;

package main;

our $h;
tie($h, 'tiescalar');
$h = [];
printf "1\n";
printf "2\n";

sub main { 
    printf "3\n";
    local $h = [sub{printf "9\n"}];
    push(@$h, sub {printf "10\n";});
    printf "4\n";
    { 
    local $h = [sub {printf "8\n"; }];
    mysub();
    printf "7\n";
    return;
    }
}

sub mysub {
    local $h = [sub {printf "6\n"; }];
    print "5\n";
}

main();

printf "11\n";
+5
source share
5 answers

, , ( ). DESTROY , , , , :

#!/usr/bin/perl

use strict;
use warnings;

for my $i (1 .. 5) {
    my $defer = Defer::Sub->new(sub { print "end\n" });
    print "start\n$i\n";
}

package Defer::Sub;

use Carp;

sub new {
    my $class = shift;
    croak "$class requires a function to call\n" unless @_;
    my $self  = {
        func => shift,
    };
    return bless $self, $class;
}

sub DESTROY { 
    my $self = shift;
    $self->{func}();
}

ETA: , Scope:: OnExit - .

+4

, , , . local.

{
my $defer = Scope::OnExit->new( @subs );
$defer->push( $other_sub ); # and pop, shift, etc

...
}

, - DESTROY.

, , , , , , VAL :

sub TIESCALAR { bless { VAL => [] }, $_[0] }

sub STORE {
    my( $self, $value )  = @_;

    carp "Can only store array references!" unless ref $value eq ref [];

    foreach { @$value } {
        carp "There should only be code refs in the array"
            unless ref $_ eq ref sub {}
        }

    foreach ( @{ $self->{VAL}} ) { $_->() }


    $self->{VAL} = $value;
    }
+4

B::Hooks::EndOfScope

, :

   use B::Hooks::EndOfScope; 

   sub foo {
      on_scope_end { 
               $codehere;
      };
      $morecode
      return 1; # scope end code executes.
   }

   foo();
+3

, - Scope:: Guard, . .

.

+1

,

sub OnLeavingScope::DESTROY { ${$_[0]}->() }

:

{
    ...
    my $onleavingscope = bless \sub { ... }, 'OnLeavingScope';
    my $onleavingscope2 = bless \\&whatever, 'OnLeavingScope';
    ...
}

(An additional link sub link level is only necessary for optimization optimization (which may be a mistake) when using an anonymous subdirectory without closing.)

+1
source

All Articles