【问题标题】:Prevent strings from being interpreted as a file handle防止字符串被解释为文件句柄
【发布时间】:2014-04-26 14:18:44
【问题描述】:

Perl 有这样一个特性,即像文件句柄一样命名的字符串被成为一个文件句柄:

# let this be some nice class I wrote
package Input {
    sub awesome { ... }
}

所以当我们执行Input->awesome 或特别小心:'Input'->awesome 时,该方法将被调用。除非:

# now somewhere far, far away, in package main, somebody does this:
open Input, "<&", \*STDIN or die $!;  # normally we'd open to a file

这段代码甚至不需要被执行,而只是被解析器看到,以便让 Perl 从现在开始将字符串 'Input' 解释为文件句柄。因此,一个方法调用'Input'-&gt;awesome 会死掉,因为代表文件句柄的对象没有很棒的方法。

由于我只控制我的类而不控制其他代码,我不能简单地决定只在任何地方使用词法文件句柄。

有什么办法可以强制Input-&gt;awesome 始终是Input 包上的方法调用,但绝不是文件句柄(至少在我控制的范围内)?我认为不应该有任何名称冲突,因为 Input 包实际上是 %Input:: stash。

重现问题的完整代码(另见ideone):

use strict;
use warnings;
use feature 'say';

say "This is perl $^V";

package Input {
    sub awesome {
        say "yay, this works";
    }
}

# this works
'Input'->awesome;

# the "open" is parsed, but not actually executed
eval <<'END';
    sub red_herring {
        open Input, "<&", \*STDIN or die $!;
    }
END
say "eval failed: $@" if $@;

# this will die
eval {
    'Input'->awesome;
};
say "Caught: $@" if $@;

示例输出:

This is perl v5.16.2
yay, this works
Caught: Can't locate object method "awesome" via package "IO::File" at prog.pl line 27.

【问题讨论】:

  • @Zaid 不,awesome Input 的行为与Input-&gt;awesome 相同。有趣的是,awesome 'Input' 无法编译,因为它被视为函数调用。
  • @Zaid: awesome Class:: 等价于Class::-&gt;awesome
  • @amon :awesome Input:: 似乎也没有强制 Perl 将裸词解释为类名,尽管文档表明:You can force Perl to interpret the bareword as a class name by appending "::" to it
  • Input::awesome 工作
  • @Zaid 我一般没有这个选项。这与这个问题的目的无关,但我真正的问题集中在import 周围,当我的包是used 时,它将被隐式调用。这非常巧妙地失败了,因为 IO::File-&gt;import 被调用了。

标签: perl filehandle method-call


【解决方案1】:

对两个不同的事物(一个使用的类和文件句柄)使用相同的标识符会带来问题。如果您的类是从使用文件句柄的代码中使用的不同类中使用的,则不会出现错误:

我的1.pm

package My1;

use warnings;
use strict;

sub new { bless [], shift }
sub awesome { 'My1'->new }

__PACKAGE__

我的2.pm

package My2;

use warnings;
use strict;
use parent 'My1';

sub try {
    my $self = shift;
    return ('My1'->awesome, $self->awesome);
}

__PACKAGE__

script.pl

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

use My2;
open My1, '<&', *STDIN;
my $o = 'My2'->new;
print $o->awesome, $o->try;

【讨论】:

  • 我想我会接受这个“不要这样做”的答案,直到出现能够消除方法调用本身歧义的答案。使用两个单独的包(它们甚至不必是单独的文件)可以正常工作,前提是文件句柄名称是普通的而不是像 _ 文件句柄那样的超全局名称,并且我们不做类似 main::My1-&gt;awesome 的事情这应该可以工作,但会再次解析为文件句柄。
【解决方案2】:

使用裸字 Input 作为文件句柄违反了命名约定FILEHANDLEs 只包含大写裸字,Classes 和 Package 只包含大写/驼峰式裸字秒。

此外,词汇$filehandles 早就被引入和鼓励了。

因此,使用您的类的程序员显然行为不端,并且由于命名空间是按定义全局的,因此 Perl 很难解决此问题(支持 chorobas 关于乞求问题的声明)。

一些命名约定对于所有(动态)语言都至关重要。

感谢这个有趣的问题,当我第一次在 SO 中看到 Perl 问题时,我更愿意在 perlmonks 上看到! :)

更新:这里的讨论已经深入:http://www.perlmonks.org/?node_id=1083985

【讨论】:

  • 尽管您可以运行检查裸字是否已经严重超载并抛出错误。使用您的模块时,源代码在 caller 的上下文中是 evaled,甚至在调用 import 之前,您可能会在此处运行此类检查(在打包之前)。
  • 啊,我完全同意你的风格建议。但是我的问题中的代码只是说明性地重现问题,而我的真实代码在引发问题方面做得更糟。 (我试图诱捕use _;——因为有什么病人会考虑这样做——如果有人以前做过-e _,那是行不通的)。我想我会在今天晚些时候向修道院发布更新版本。
  • 当然,如果你想让use _ 爆炸,最好的方法是在你的@INC 中不要有任何名为_.pm 的文件?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-13
  • 1970-01-01
  • 2014-03-04
相关资源
最近更新 更多