4

我正在尝试通过使用绑定标量向我们的代码库添加一些功能。

我们有一个指定返回标量的函数。我想我可以通过在返回它们之前绑定这些标量来为系统添加一些功能,但看起来 FETCH 方法在返回之前被调用,这导致返回一个未绑定的标量。

有没有办法解决?

如果可能的话,我真的很想保持子例程的接口(返回标量)完好无损。

use strict;
use warnings;
main();

sub GetThing{
    my $thing;
    tie $thing, 'mything', @_;
    return $thing;
}

sub main {
    my %m;
    $m{pre} = GetThing('Fred');
    print "1\n";
    print $m{pre};
    print "2\n";
    print $m{pre};
    print "3\n";
}


package mything;
require Tie::Scalar;

my @ISA = qw(Tie::StdScalar);

sub TIESCALAR {
    my $class  = shift;
    bless {
        name    => shift || 'noname',
    }, $class;
}

sub FETCH {
    my $self = shift;
    print "ACCESS ALERT!\n";
    return "    NAME: '$self->{name}'\n";
}

期望的输出:

1
ACCESS ALERT!
    NAME: 'Fred'
2
ACCESS ALERT!
    NAME: 'Fred'
3

我可以通过返回一个引用并在每次访问时取消引用来获得所需的输出,但这会破坏我们已建立的界面,并使我们的用户更加困惑。

——巴克

4

4 回答 4

4

正如 DVK 所说, tie 适用于容器,因此对于返回没有用。

为此,您使用重载。一个示例(并非提供所有可能的重载操作;请参阅http://perldoc.perl.org/overload.html#Minimal-set-of-overloaded-operations):

use strict;
use warnings;
main();

sub GetThing{
    my $thing;
    $thing = "mything"->new(@_);
    return $thing;
}

sub main {
    my %m;
    $m{pre} = GetThing('Fred');
    print "1\n";
    print $m{pre};
    print "2\n";
    print $m{pre};
    print "3\n";
}


package mything;
use overload 'fallback' => 1, '""' => 'FETCH';

sub new {
    my $class = shift;
    bless {
        name    => shift || 'noname',
    }, $class;
}

sub FETCH {
    my $self = shift;
    print "ACCESS ALERT!\n";
    return "    NAME: '$self->{name}'\n";
}
于 2010-07-29T03:04:58.863 回答
3

如其他答案中所述,tie适用于容器,而不适用于值,因此无法将绑定变量分配给另一个变量并保留绑定属性。

由于分配已结束,您需要将容器传递到GetThing例程中。您可以通过参考来执行此操作,如下所示:

use strict;
use warnings;
main();

sub GetThing{
    tie ${$_[1]}, 'mything', $_[0];
}

sub main {
    my %m;
    GetThing('Fred' => \$m{pre});
    print "1\n";
    print $m{pre};
    print "2\n";
    print $m{pre};
    print "3\n";
}


package mything;
require Tie::Scalar;

my @ISA = qw(Tie::StdScalar);

sub TIESCALAR {
    my $class  = shift;
    bless {
        name    => shift || 'noname',
    }, $class;
}

sub FETCH {
    my $self = shift;
    print "ACCESS ALERT!\n";
    return "    NAME: '$self->{name}'\n";
}

这会产生正确的输出。

但是,如果要保留分配,则需要使用重载,这适用于值(实际上适用于对象,但它们本身就是值)。如果没有关于您的预期目的的更多详细信息,很难给出完整的答案,但这将满足您的要求:

use strict;
use warnings;
main();

sub GetThing{
    return mything->new( shift );
}

sub main {
    my %m;
    $m{pre} = GetThing('Fred');
    print "1\n";
    print $m{pre};
    print "2\n";
    print $m{pre};
    print "3\n";
}


package mything;

sub new {
    my $class  = shift;
    bless {
        name    => shift || 'noname',
    }, $class;
}

use overload '""' => sub {   # '""' means to overload stringification
    my $self = shift;
    print "ACCESS ALERT!\n";
    return "    NAME: '$self->{name}'\n";
};

绑定和重载都可能变得复杂,因此如果有任何不清楚的地方,请通读所有文档。

于 2010-07-29T22:34:31.670 回答
2

首先,做你所提议的确切方法在技术上似乎是不可能的:

  1. 绑定变量将绑定附加到变量本身,而不是其值。

  2. 在 Perl 中,子程序的返回值由 value返回,这意味着您获取传递给的值return,访问它(在您的情况下,访问绑定的变量并FETCH在进程中调用)-然后复制该值!这意味着调用者得到的是一个标量值,而不是一个标量变量(已绑定或未绑定)。

简而言之,您的困惑似乎源于将变量(程序符号表中的位置)和存储在这些变量中的值混合在一起。


其次,你有点不清楚你到底想达到什么目标,所以很难提出如何达到你想要的目标。但是,根据您的描述,假设您想在子例程返回时调用某个方法(可能将返回值传递给它),您可以这样做。

为此,您需要使用花哨的人所说的方面编程。在 Perl 中,政治上(和技术上)正确的做法是使用 Moose。

但是,您可以 DIY 它,基本上用包装器方法替换原始方法。

Moose 和 DIY 方法的确切机制可以在以下 SO 问题的前两个答案中看到,所以我不会在这里复制/粘贴它们,希望你不介意:

在鸭子类型语言中模拟静态类型的各个方面

于 2010-07-29T02:46:05.237 回答
0

如果您喜欢冒险,您还可以使用Scalar::Defer模块,它为标量变量提供了一种通用机制,可以懒惰地计算一个值,一次或每次访问。

于 2010-07-30T19:05:29.147 回答