在Perl中检测编译阶段

4

我正在使用一个模块,它利用一些原型来允许代码块。例如:

sub SomeSub (&) { ... }

由于原型只在编译时解析有效,因此如果模块在编译时未被解析,我希望发出警告甚至是致命错误。例如:

require MyModule; # Prototypes in MyModule won't be parsed correctly

有没有一种方法可以在Perl中检测某个东西是在编译时还是运行时/阶段执行的?

那是一个不好的想法。即使是使用原型的模块,在运行时加载也有完全合理的理由。 - tobyink
根据每种情况的原因,这可能是一个好主意或坏主意。如果特定模块的整个目的是创建仅在编译时加载时才能工作的功能,则至少警告用户有关在运行时无用地加载模块的良好理由。 - Francisco Zarabozo
在运行时加载模块的另一个好处是,我可能不想实际使用您的模块,而只是报告其版本。require Your::Module; printf "Your::Module %s installed\n", Your::Module->VERSION;。这可以在设置或诊断脚本中完成,以报告应用程序所有先决条件的安装版本。 - tobyink
如果你在编译时在脚本中调用了 use module,那么“module”中的所有内容都会在编译时解析,包括你刚才使用的示例。无论如何,这与我提出的场景无关,我的场景是确保原型在全局编译时被正确解析,因为在此之后,就没有其他方法了。 - Francisco Zarabozo
“根本没有绕过的方法”...实际上,绕过原型是相当容易的。require Your::Module; &Your::Module::SomeSub(sub { ... });如果人们使用您的模块不寻常,为什么不假设他们知道自己在做什么呢?我写了很多模块 带有 编译- 效果,但从未像这样向它们添加警告。requireuse并不做同样的事情,但这并不意味着它毫无用处。 - tobyink
显示剩余2条评论
2个回答

5

如果您正在运行Perl 5.14或更高版本,则可以使用特殊的${^GLOBAL_PHASE}变量,其中包含当前编译器状态。以下是一个示例。

use strict;
use warnings;

sub foo {
    if ( ${^GLOBAL_PHASE} eq 'START' ) {
        print "all's good\n";
    } else {
        print "not in compile-time!\n";
    }
}

BEGIN {
    foo();
};

foo();

输出:

all's good
not in compile-time!

很好,谢谢你的回答。你知道早期版本的方法吗? - Francisco Zarabozo
2
@FranciscoZarabozo 您可以尝试使用 INIT {} 块。它应该只在模块在 BEGIN 中加载时起作用(例如使用 use)。 - kobame

4

在5.14版本之前(或之后),您可以执行以下操作:

package Foo;
BEGIN {
    use warnings 'FATAL' => 'all';
    eval 'INIT{} 1' or die "Module must be loaded during global compilation\n";
}

但这个(以及${^GLOBAL_PHASE})并不能完全检查您想要知道的内容,即包含use/require语句的代码是正在编译还是运行。


我尝试了这个答案,但它没有起作用。当我使用require而不是use时,我只得到一个警告(当然启用了警告),警告信息为:Too late to run INIT block at (eval 11) line 1.,但它并没有真正失败,也从未到达die调用(退出代码为0)。 - Francisco Zarabozo
在阅读了您的版本问题后,我在Perl 5.20和5.12上尝试了它。eval 在任何一个版本上都没有失败。 - Francisco Zarabozo
哦,那就是为什么了。 :-) - Francisco Zarabozo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接