我有一个Perl脚本,我试图与两个不同的Perl环境兼容。为了解决我所使用的两个不同版本的Socket,我对require和import做了一些小问题。我已经开始工作了,但我对这种行为并不满意。
Mod.pm:
package Mod;
use base 'Exporter';
our @EXPORT = qw( MAGIC_CONST );
sub MAGIC_CONST() { 42; }
test.pl:
use Mod;
#require Mod;
#import Mod;
printf "MAGIC_CONST = ". MAGIC_CONST ."\n";
printf "MAGIC_CONST = ". MAGIC_CONST() ."\n";
输出:
MAGIC_CONST = 42
MAGIC_CONST = 42
但是使用'require'和'import'代替,我得到了这个:
输出:
MAGIC_CONST = MAGIC_CONST
MAGIC_CONST = 42
所以问题是:是否有一种干净的方法可以获得常量的正常行为?我当然可以做sub MAGIC_CONST { Mod::MAGIC_CONST(); }
,但这非常难看。
我实际上在做的是这样的事情:
use Socket;
if ($Socket::VERSION > 1.96) {
import Socket qw(SO_KEEPALIVE); # among others
setsockopt($s, SOL_SOCKET, SO_KEEPALIVE); # among others
}
答案 0 :(得分:4)
require
版本打印MAGIC_CONST
而不是42
的原因是因为use
告诉perl将符号从一个模块导入另一个模块。如果没有use
,则没有定义名为MAGIC_CONST
的函数,因此perl会将其解释为字符串。您应该use strict
禁用将这样的裸字自动转换为字符串。
#!/usr/bin/env perl
no strict;
# forgot to define constant MAGIC_CONST...
print 'Not strict:' . MAGIC_CONST . "\n";
产生
不严格:MAGIC_CONST
但是
#!/usr/bin/env perl
use strict;
# forgot to define constant MAGIC_CONST...
print 'Strict:' . MAGIC_CONST . "\n";
产生错误:
在使用“严格潜艇”时不允许使用Bareword“MAGIC_CONST” ./test.pl第4行。./test.pl的执行因编译而中止 错误。
因此,如果您想在另一个模块中使用一个模块的功能,您必须使用use
导入它们,或者使用完整的包名称来调用它们:
package Foo;
sub MAGIC_CONST { 42 };
package Bar;
print 'Foo from Bar: ' . Foo::MAGIC_CONST . "\n";
来自Bar的Foo:42
通常最好避免有条件地输入东西。您可以按如下方式解决问题:
use Socket;
if ($Socket::VERSION > 1.96) {
setsockopt($s, SOL_SOCKET, Socket::SO_KEEPALIVE);
}
如果你真的想要导入,你仍然需要在编译时这样做。
use Socket;
use constant qw( );
BEGIN {
if ($Socket::VERSION > 1.96) {
Socket->import(qw( SO_KEEPALIVE ));
} else {
constant->import({ SO_KEEPALIVE => undef });
}
}
setsockopt($s, SOL_SOCKET, SO_KEEPALIVE) if defined(SO_KEEPALIVE);
答案 1 :(得分:3)
Adam's answer可以很好地解释发生了什么以及如何获得所需的行为。我建议不要使用constant.pm来定义常量的符号名称。一个相当漂亮且方便的替代方案,使用较少的陷阱来使用Const::Fast定义的常量并允许它们被导入。
此外,通过在要导入常量的模块中使用Importer,定义常量的模块可以避免继承Exporter的沉重包袱,或者必须使用{ {1}} Exporter
。
import
允许您定义实常数组和散列的事实是一个额外的好处。
例如:
Const::Fast
在脚本中使用这些常量:
package MyConstants;
use strict;
use warnings;
use Const::Fast;
use Socket;
const our @EXPORT => ();
const our @EXPORT_OK => qw(
%SOCKET_OPT
);
const our %SOCKET_OPT => (
keep_alive => ($Socket::VERSION > 1.96) ? Socket::SO_KEEPALIVE : undef,
);
__PACKAGE__;
__END__
f我想从配置文件中读取常量,这是微不足道的。如果我想将它们导出为JSON,YAML,INI或其他任何东西,那也是微不足道的。我可以毫不犹豫地插入它们。
对于那些认真对待导出变量
#!/usr/bin/env perl use strict; use warnings; use Socket; use Importer 'MyConstants' => qw( %SOCKET_OPT ); if ( defined $SOCKET_OPT{keep_alive} ) { setsockopt($s, SOL_SOCKET, $SOCKET_OPT{keep_alive}); }
的立场的人来说,这需要一些人习惯。请记住,警告是为了确保您不会编写无法修改全局变量的代码。但是,在这种情况下,我们导出的变量不可修改。也许你可以说服自己在这种情况下这种担忧真的不适用。如果您尝试引用不存在的常量,则会出现错误(尽管在运行时)...
在大多数情况下,这种方法的好处超过了不使用Exporter
的速度惩罚。