4

次のフラグメントでは、サブルーチンの 2 番目の呼び出しインスタンスを最初の呼び出しインスタンスとどのように区別できますfooか?

while ($whatever) {
  foo(); foo();     # foo() and foo() have the same caller package, file, and line
}

filelinecolumncaller()を返すsuper- のようなものがうまくいきます。ソース フィルターは使用しない方がよいでしょう。

背景、または、これは少し XY 問題ではありませんか?

私は便利なモジュール、Local::Thread::Once を持っています。これはpthread_once/のような機能std::call_onceを OO 風の方法で、またサブルーチン属性として公開します。どちらの場合も、自然で明確な「once_control」または「once_flag」があるため、これらは簡単です。

ただし、現在、およびによって返されるonce { ... }に基づいてシリアル化する手続き型インターフェイス — — が追加されています。このようなもの:$filename$linecaller

sub once(&) {
  my $user_routine = shift;
  my (undef, $file, $line) = caller;

  my $once_control = get_a_shared_flag_just_for_this_invocation($file, $line);

  lock($once_control);
  if (! $once_control) { $once_control++; $user_routine->(); }
  return;
}

それは正確にはどのように機能するわけではありません — 実際のものはより効率的です — しかし、要点は、呼び出し元のファイルと行に基づいて呼び出しが行われるということです。これは機能しますが、同じ行での 2 つの呼び出しを区別できないことを除きます。

while ($whatever) {
  once { foo(); }
  once { bar(); }                    # OK, foo() and bar() each called only once
  once { baz(); }; once { buz(); };  # :(  buz() not called, not even once
}

$user_routineサブルーチンはある ithread から別の ithread にコピーされるため、 のアドレスを追加の判別式として使用できないことに注意してください。

この問題は、非常に不自然なユースケースの文書化された制限として受け入れることができますが、どうにかして修正したいと考えています。

4

3 に答える 3

1

optree は依然として私にとって非常に多くの黒魔術ですが、ここに私の観察があります。

  1. コード参照の optree をたどると、1 つのB::COP構造に遭遇します。
  2. B::COP構造体には、filelineおよびcop_seqプロパティがあります (特に)
  3. プロパティはcop_seqサブルーチン定義ごとに異なります

これらは真実であり、起こっていることの恐ろしく不完全なモデルではありません.file、line、およびcop_seqをキーとして、または単にcop_seq. 概念実証は次のとおりです。

use B;

sub once (&) {
    my $code = shift;
    my $key = get_cop_seq($code);
    print "once called with code '$key'\n";
}

my $optreedata;
sub get_cop_seq {
    my $code = shift;
    $optreedata = "";
    B::walkoptree( B::svref_2object($code)->ROOT, "find_cop_seq" );
    return $optreedata;
}
sub B::OP::find_cop_seq {
    my $op = shift;
    if (ref $op eq 'B::COP') {
        $optreedata .= sprintf "%s:%d:%d", $op->file, $op->line, $op->cop_seq;
    }
}

sub foo { 42 }
sub bar { 19 };

once { foo };                  # this is line 26
once { bar };
once { foo }; once { bar };
once { bar } for 1..5;         # line 29

出力は次のとおりです (結果は異なる場合があります)。

once called with code 'super-caller2.pl:26:205'
once called with code 'super-caller2.pl:27:206'
once called with code 'super-caller2.pl:28:207'  <--- two calls for line 28
once called with code 'super-caller2.pl:28:208'    |- with different cop_seq
once called with code 'super-caller2.pl:29:209'      
once called with code 'super-caller2.pl:29:209'      
once called with code 'super-caller2.pl:29:209'  <--- but 5 calls for line 29
once called with code 'super-caller2.pl:29:209'       with the same cop_seq
once called with code 'super-caller2.pl:29:209'
于 2013-08-28T20:51:13.803 に答える