perl use vs require 和 import,常量只是函数

perl use vs require and import, constants are only functions

我有一个 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
}

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";

产生

Not strict:MAGIC_CONST

但是

#!/usr/bin/env perl
use strict;
# forgot to define constant MAGIC_CONST...
print 'Strict:' . MAGIC_CONST . "\n";

产生错误:

Bareword "MAGIC_CONST" not allowed while "strict subs" in use at ./test.pl line 4. Execution of ./test.pl aborted due to compilation errors.

所以如果你想在另一个模块中使用一个模块的功能,你要么必须使用 use 导入它们,要么使用完整的包名称调用它们:

package Foo;
sub MAGIC_CONST { 42 };

package Bar;
print 'Foo from Bar: ' . Foo::MAGIC_CONST . "\n";

Foo from Bar: 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);

gives a good explanation of what is going on and how to get the desired behavior. I am going to recommend not using constant.pm to define symbolic names for constants. A fairly nice looking and convenient alternative with fewer gotchas is to use constants defined using Const::Fast 并允许导入它们。

此外,使用Importer in the module which wants to import the constants, the module that defines the constants can avoid inheriting Exporter的大件行李,或必须使用Exporterimport

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__

在脚本中使用这些常量:

#!/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});
}

I note in my blog post:

f I want to read the constants from a configuration file, that's trivial. If I want to export them to JSON, YAML, INI, or whatever else, that's also trivial. I can interpolate them willy-nilly.

For those who have taken seriously Exporter's stance against exporting variables, this takes some getting used to. Keep in mind that the admonition is there to make sure you don't write code that willy nilly modifies global variables. However, in this case, the variables we are exporting are not modifiable. Maybe you can convince yourself that the concern really does not apply in this instance. If you try to refer to a non-existent constant, you get an error (albeit during run time) ...

在大多数情况下,这种方法的好处超过不使用 constant.pm 的速度损失。