【问题标题】:In Perl, how can I check from which module a given function was imported?在 Perl 中,如何检查给定函数是从哪个模块导入的?
【发布时间】:2011-04-10 17:45:51
【问题描述】:

我有一个调用该函数的代码。但我不知道这个函数属于哪个模块。我需要它来修改这个功能。

如何查看?

【问题讨论】:

  • 通过了解这一点,您想解决什么问题?可能有办法在它发生之前解决它。 :)
  • 从其他类中修改函数在 Ruby 中可能很常见,但在 Perl 中却是一种黑色艺术,应该为非常罕见的情况保留。为什么需要这样做?
  • @'brian d foy' 三年后,我正在尝试编写一个单元测试,以确认我在一个条件下正确获得了纯 Perl 版本的函数,或者在其他条件下正确获得了 XS 版本另一种情况,突然间我觉得这个OP的问题是一个合法的用例。 ;)

标签: perl function module subroutine


【解决方案1】:

您可以将任何子程序引用传递给Sub::Identify::sub_fullname,它会向您显示定义此子程序的模块:

use Sub::Identify qw/sub_fullname/;
sub foo {
    print sub_fullname( \&foo );  # main::foo
    print sub_fullname( sub{} );  # main::__ANON__
}

foo();

详情见Sub::Identify

【讨论】:

    【解决方案2】:

    如果该函数是使用Exporter从另一个模块自动导入的,则可以在该模块的@EXPORT全局变量中找到:

    perl -MEncode -e 'print join "\n", @Encode::EXPORT'
    decode   
    decode_utf8
    ...   
    

    您可以向use 提供函数列表。这样你就永远知道函数属于哪个包:

    use Encode       qw[ encode ]; # encode() imported from the Encode module
    use Data::Dumper qw[];         # no functions imported from Data::Dumper
    

    【讨论】:

      【解决方案3】:

      Perl 的调试器可以按照您想要的方式进行挖掘。例如:

      main::(-e:1): 0
        DB 子 foo {}
      
        DB x \&foo
      0代码(0xca6898)
         -> &main::foo in (eval 5)[/usr/share/perl/5.10/perl5db.pl:638]:2-2

      它使用Devel::Peek

      =head2 C<CvGV_name_or_bust> I<coderef>
      
      Calls L<Devel::Peek> to try to find the glob the ref lives in; returns
      C<undef> if L<Devel::Peek> can't be loaded, or if C<Devel::Peek::CvGV> can't
      find a glob for this ref.
      
      Returns C<< I<package>::I<glob name> >> if the code ref is found in a glob.
      
      =cut
      
      sub CvGV_name_or_bust {
          my $in = shift;
          return unless ref $in;
          $in = \&$in;            # Hard reference...
          eval { require Devel::Peek; 1 } or return;
          my $gv = Devel::Peek::CvGV($in) or return;
          *$gv{PACKAGE} . '::' . *$gv{NAME};
      } ## end sub CvGV_name_or_bust
      

      你可以用它来锻炼它

      #! /usr/bin/perl
      
      use warnings;
      use strict;
      
      package Foo;
      
      sub bar {}
      
      package main;
      
      BEGIN { *baz = \&Foo::bar }
      
      sub CvGV_name_or_bust { ... }
      
      print CvGV_name_or_bust(\&baz), "\n";
      

      输出:

      Foo::bar

      请注意,上面的示例为 Foo:bar 提供了不同的名称,但您会得到别名子所在的包及其名称。

      【讨论】:

        【解决方案4】:

        Devel::Peek 模块非常方便地获取有关变量的各种信息。您可以用它做的一件事是转储对子例程的引用并获取它来自的 glob 的名称:

        $  perl -MDevel::Peek -MList::Util=first -e'Dump(\&first)'
        SV = IV(0x1094e20) at 0x1094e28
          REFCNT = 1
          FLAGS = (TEMP,ROK)
          RV = 0x11183b0
          SV = PVCV(0x10ff1f0) at 0x11183b0
            REFCNT = 3
            FLAGS = (POK,pPOK)
            PROTOTYPE = "&@"
            COMP_STASH = 0x0
            XSUB = 0x7f7ecbdc61b0
            XSUBANY = 0
            GVGV::GV = 0x11183c8        "List::Util" :: "first"
            FILE = "ListUtil.c"
            DEPTH = 0
            FLAGS = 0x800
            OUTSIDE_SEQ = 0
            PADLIST = 0x0
            OUTSIDE = 0x0 (null)
        

        其中的GVGV::GV 部分是重要的部分。

        另一种解决方案是Sub::Identify,它实际上只为您提供您交给它的代码引用的名称。但是,了解Devel::Peek 在许多其他情况下也很方便,所以我首先提到了这一点。

        【讨论】:

        • 这比我做的要好得多。我会在使用每个模块后添加(),直到代码编译失败。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-02-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多