0

我在以下情况下遇到问题。我正在编写一个使用AUTOLOADsomtimes 从另一个模块(不是我的)调用函数的类。这个其他模块使用了一些定义原型的函数。一个例子是这样的:

sub external_sub (\@$) { ... }

当直接从该模块导入时,这些函数可以正常工作,调用如下:

my @arr = (1..10);
external_sub(@arr, 'something else');

现在,在运行时需要我的类中的外部模块并导入该函数时,我遇到的问题是我找不到将正确参数传递给它的方法。

在我的AUTOLOAD函数内部,我只依靠@_,我不知道 Perl 中是否有办法区分作为第一个参数传递给AUTOLOAD. 所以,在里面AUTOLOAD,到目前为止,我能想到的重定向呼叫的唯一选项是:

no strict 'refs';
my $sym = *{"ExternalModule\::$called_sub"};
*$AUTOLOAD = $sym;
goto &$AUTOLOAD;

...或类似的东西:

no strict 'refs';
return &{"ExternalModule\::$called_sub"}(@_);

或使用符号表的几个类似的东西。但是,问题是我如何将参数传递给该 call。如果在我的代码中我有:

package main;

use strict;
use MyModule qw(:some_external_subs); # This will import *names only but will decide later from which modules to take the actual code refs

# Here I have imported the sub 'external_sub' as symbol but it won't be actually loaded until MyModule::AUTOLOAD decides which external module will actually use to import the code for that function:

my @arr = ('some', 'values');

my $result = external_sub(@arr, 'other_argument');

然后,这就是AUTOLOAD我的模块中需要一个外部模块并将调用传递给实际原型的地方sub external_sub(\@$)。问题是它会将接收到的参数作为@_, where@arr'other_argument'now 都作为单个列表的一部分传递。

有没有办法解决这样的情况?有没有办法检测出原来的论点是@_什么?

请记住,我无法控制外部模块以及它使用原型函数的事实。

提前感谢您的任何评论!

4

1 回答 1

2

对于任何有类似问题的人,这是我迄今为止发现的:

  1. 您只能在编译时定义原型,否则将被忽略。

  2. 如果您在编译时知道函数的名称,但计划稍后(在运行时)加载符号的代码,那么您可以只定义没有代码的原型:

    sub some_sub(\@$);
    
  3. 如果您不知道函数的名称但您可以在编译时动态获取它,那么您可以使用Scalar::Util::set_prototype仅声明本地原型:

    package MyModule;
    use strict;
    use Scalar::Util qw(set_prototype);
    my $protos;
    
    BEGIN { # compile time
            my @functions;
    
            # Imagine you load here @functions with hashrefs containing name and proto values.
    
            no strict 'refs'
            for my $i (@functions) {
                # This defines the prototype without actually defining the sub
                set_prototype \&{"MyModule::$i->{name}"}, $i->{proto};
                # If I want to recall the name/proto later in AUTOLOAD:
                $protos->{$i->{name}} = $i->{proto};
            }
    }
    

由于只有原型的声明准备好了,而子程序本身的定义没有准备好,所以当第一次调用该子程序时,它将触发您的AUTOLOAD子程序,您可以在其中继续为符号分配一个实际的 coderef。您使用的 coderef 必须具有与您声明为该名称的原型相同的原型,否则您将收到prototype mismatch. set prototype如有必要,可以在将 coderef 分配给真实符号之前将相同的原型分配给该 coderef。

例如:

sub AUTOLOAD {
    our $AUTOLOAD;
    my $name = substr $AUTOLOAD, rindex($AUTOLOAD, ':') + 1;

    # Here I do my process and decide that I'll call OtherModule::some_sub for the name in $AUTOLOAD

    no strict 'refs';
    my $coderef = *{"OtherModule::some_sub"}{CODE};
    # Prototypes must match!
    unless (defined(prototype $coderef) and $protos->{$name} eq prototype $coderef) {
        set_prototype(\&$coderef, $protos->{$name});
    }
    *$AUTOLOAD = $coderef;
    goto &$AUTOLOAD;
}

如果其他人知道在运行时更改原型并让它们按预期工作的实际方法,我将非常高兴知道它!

同时,我希望这对将来面临类似情况的人有所帮助。

于 2013-08-24T11:25:26.987 回答