【问题标题】:perl: function name clashperl:函数名冲突
【发布时间】:2011-12-26 08:41:52
【问题描述】:

假设我们的框架有两个模块:Handler.pm 和 Queries.pm

Queries.pm 是可选的,在 fastcgi 进程启动时加载

BEGIN {
&{"check_module_$_"}        () foreach (Queries);

}

sub check_module_queries {
...
    require Eludia::Content::Queries;
...
}

每个模块函数都加载到一个公共命名空间中

现在,Handler.pm 和 Queries.pm 中有两个同名函数(setup_page_content)

setup_page_content 在 Handler.pm 中被调用

看起来原作者建议 Queries.pm:setup_page_content 将在 Queries.pm 加载时被调用

有时它不会发生:在这些情况下,回溯(通过调用者 () 获得)表明 setup_page_content 是从模块 Handler.pm 调用的

我在通话前记录了 %INC,它包含 Queries.pm 以及在这些情况下的完整路径

这种行为是不一致的,并且在 2-3% 的生产安装尝试中会弹出,主要是在我发送两个并行相同的 http 请求时。由于大量的复制工作,我还不确定它是否是特定于安装的。

如何决定调用哪个版本的同名函数?

这是明确定义的行为吗?

应该是有原因的,原作者是这样写代码的

更新

perl 版本是v5.10.1 built for x86_64-linux-gnu-thread-multi

更新 2

代码:Handler.pmQueries.pm

Queries.pm 加载发生在 check_module_queries(BEGIN {} of Eludia.pm)中,

使用Loader.pm(通过use Loader.pm <params>)为每个请求加载

【问题讨论】:

    标签: perl module


    【解决方案1】:
    每当加载 Custom.pm 时,我想调用 Custom.pm:setup_page_content

    所以你想在 Custom::setup_page_content 存在时调用它。

    if (exists(&Custom::setup_page_content)) {
       Custom::setup_page_content(...);
    } else {
       Handler::setup_page_content(...);
    }
    

    有时它不会发生。

    完全缺乏这方面的信息使我无法发表评论。代码?有什么事吗?


    有没有办法“卸载”一个模块但将其保留在 %INC 中?

    加载一个模块只是运行它。您不能“取消运行”代码。充其量,您可以手动撤消运行它的一些效果。是否包括更改 %INC 完全取决于您。

    【讨论】:

    • @jonny,你真的希望通过 1200 行来找到决定使用哪个模块的那个吗?
    • 不,这就是为什么我没有首先发布代码并试图告诉我发生了什么。在那些定义的情况下 (&Eludia::Content::Queries::setup_page_content) 什么都不显示,但是 main::setup_page_content 做,我认为,这意味着它已成功导入到全局命名空间
    • @jonny,如果不清楚,请您指出检查所在的相关代码。
    • 这不是支票,而是require 电话。正如我所写,它是从Eludia.pm(第 945 行)的 BEGIN {} 部分调用的:&{"check_module_$_"} () foreach sort grep {!/^_/} keys %{$conf -> {core_modules}};。 $conf -> {core_modules} 在每个坏情况下都包含 Queries.pm
    • 对不起我的英语:D。我的意思是,它站在那里运行 Queries.pm 并在全局命名空间中加载它的所有功能。据我所知,我不能使用 exists(&Eludia::Content::Queries::setup_page_content) 检查来决定调用哪个版本,因为它不存在并且 main::setup_page_content - 试图找出原因...
    【解决方案2】:

    当发生命名空间冲突时,最近定义/加载的函数将优先。

    【讨论】:

    • 在 perl 文档中的什么地方可以解释/指定?
    • 很奇怪,我在 perl 官方文档中找不到它,但请检查一下 -- docstore.mik.ua/orelly/perl4/porm/ch02_06.htm
    • 我怀疑它不是那样的文档,因为真正发生的所有事情都是你在重新定义一些东西;在这种情况下,一个函数。我们都应该很好地理解它是如何工作的。
    • 如果只是重新定义,好吧,I GUARANTEE THAT 会帮我:)
    • @Mike Samuel,没有命名空间冲突之类的东西,所以你不会找到任何文档
    【解决方案3】:

    这些不是合适的模块。这些只是库。如果您将它们转换为具有适当名称间距的适当模块,您将不会遇到此问题。您几乎不需要做任何事情:

    包查询;

    子新{

    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self = {};
    
    bless ( $self, $class );
    
    return $self;
    

    }

    你会一路走好。当然,您的程序需要进行一些更改才能实际访问类函数。您可以查看导出到函数以可能节省一些重构时间。

    我已经在 github 上分叉了代码并发起了一个拉取请求,希望能解决您的问题。

    【讨论】:

    • 我刚刚在 Eulida.pm 中注意到了这一点,“我们完全忽略了大多数所谓的‘良好风格’惯例。我们确实觉得它真的很尴尬而且毫无用处。”那是你的问题。
    【解决方案4】:

    行为是完美定义的:当您调用 PACKAGENAME::function_name 时,会调用 PACKAGENAME::function_name。一次不能超过一个 PACKAGENAME::function_name。

    可以根据需要多次重新定义每个函数。例如,当您执行一些代码时,例如

    eval "sub PACKAGENAME::function_name {...}";
    

    函数是[重新]定义的。当你require 一些.pm 文件时,它很像eval 带有其内容的字符串。

    如果没有明确定义 PACKAGENAME,则使用 __PACKAGE__ 值。

    所以如果你说setup_page_content的版本取自Handler.pm,那一定表明Handler.pm的内容被执行了(by use,require,read/eval等)在加载Queries.pm 之后(最后一次)。

    因此您必须跟踪Handler.pm 加载的所有事件。简单的方法是在Handler.pm 的开头添加一些调试打印。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-15
      • 2016-05-03
      • 2020-04-23
      • 2015-02-26
      • 1970-01-01
      • 2016-03-27
      相关资源
      最近更新 更多