Gregory Nisbet Gregory Nisbet - 3 years ago 140
Perl Question

Perl calling subroutine reference with explicit additional scope as cleanly as possible

I'd like to be able to write something like the following...

x => 47,
}, sub {
printf "$x\n";
printf "$y\n";

is bound in the environment containing the expression (either lexically or dynamically depending on the symbol).

I've found a way to do it, but it requires
no strict "vars"
to be in effect in the expression containing
and the implementation of
to create local bindings before transferring control to the callback.

Is there a way to avoid either requiring
no strict "vars"
at the call site or refer to and change the value of a
variable without resorting to eval?

For completeness, the code snippet below implements
and prints
and then

#!/usr/bin/env perl
use strict;
use warnings;

sub call_with_scope {
my ($env, $func) = @_;
my %property;
my @preamble;
foreach my $k (keys %$env) {
$property{$k} = $env->{$k};
# deliberately omitted: logic to ensure that ${$k} is a well-formed variable
push @preamble, "local \$$k = \$property{'$k'};";
# force scalar context
do {
my $str = join('', 'no strict "vars";', @preamble, '$_[1]->();');
return scalar(eval($str));

do {
no strict 'vars';
local $x;
my $y = 48;
x => 47,
sub {
printf "$x\n";
printf "$y\n";

Answer Source

I'm trying to write something kind of like Test::LectroTest ... except that instead of using a source filter and comments like in Property { ##[ x <- Int, y <- Int ]## <body> } ... I want to write something like Property({x => gen_int, y => gen_int}, sub { <body> }) where $x and $y inside body get their values when an "instantiation" of a property test is performed.

You can do this by defining $x and $y as globals in the caller's package.

no strict 'refs';
my $caller = caller;
for my $var (keys %$properties) {
    *{$caller.'::'.$var} = $properties->{$var};

But this can't be easily localized. And polluting the caller's namespace with globals potentially leads to mysterious data leaking between tests. In general, use as little magic as possible in a test library; the user will have enough of their own weird magic to debug.

Instead, provide a function which returns the properties. For example, p.

package LectroTest;

use Exporter qw(import);
our @EXPORT = qw(test p);
our $P;

sub test {
    my($props, $test) = @_;

    local $P = $props;

sub p {
    return $P;

And the test looks like:

use LectroTest;

    { x => 42 }, sub { print p->{x} }
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download