0

次のシナリオで問題が発生しています。somtimesを使用しAUTOLOADて別のモジュール (私のものではない) から関数を呼び出すクラスを作成しています。この他のモジュールは、プロトタイプが定義されているいくつかの関数を使用します。一例はこれです:

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"}(@_);

またはシンボルテーブルを使用したいくつかの同様のもの。ただし、問題はその呼び出しに引数を渡す方法です。私のコードにある場合:

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私のモジュールで外部モジュールを必要とし、呼び出しを実際のprototypedに渡すポイントsub external_sub(\@$)です。問題は、受け取った引数を として渡すことです@_。ここで、@arr'other_argument'はすべて単一のリストの一部になりました。

このような状況を解決する方法はありますか? になる前に元の引数が何であったかを検出する方法はあり@_ますか?

私は外部モジュールを制御できないこと、およびそれがプロトタイプ関数を使用しているという事実に注意してください。

コメントをお寄せいただきありがとうございます。

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 mismatchset prototypeコードリファレンスを実際のシンボルに割り当てる直前に、必要に応じて を使用して、同じプロトタイプをそのコードリファレンスに割り当てることができます。

例えば:

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 に答える