4

在下面的片段中,我如何区分我的 sub 的第二个调用实例和foo第一个?

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

caller()返回文件的 super- 这样的东西就可以解决问题。我不想使用源过滤器。

背景,或者,这不是一个 XY 问题吗?

我有一个便利模块 Local::Thread::Once,它以面向对象的方式公开pthread_once/之类的功能,也作为子例程属性。std::call_once这些很容易,因为在任何一种情况下都有一个自然且明确的“once_control”或“once_flag”。

但是,还有一个过程接口——once { ... }当前基于$filename$line返回的 进行序列化caller。像这样的东西:

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;
}

这并不是它的工作原理——真正的效率更高——但重点是调用是从调用者的文件和行中分离出来的。这有效,只是它无法区分同一行上的两个调用。

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不能将 的地址用作附加判别式,因为 subs 是从一个 ithread 复制到另一个 ithread。

我可以忍受这个问题作为一个非常人为的用例的记录限制,但我更愿意以某种方式修复它。

4

3 回答 3

6

Devel::Callsite正是为此目的而编写的。

于 2013-08-28T21:33:09.380 回答
3

I had to read this a couple of times before I understood what you are talking about. How about a "super caller" function like:

my @last_caller = ("","","",0);
sub super_caller {
    my ($pkg,$file,$line) = caller(1 + shift);
    if ($pkg eq $last_caller[0] &&
        $file eq $last_caller[1] &&
        $line eq $last_caller[2]) {
        $last_caller[3]++;
    } else {
        @last_caller = ($pkg,$file,$line,1);
    }
    return @last_caller;
}

It's like caller but the 4th element is a count of how many times we've seen this exact package, file, and line in a row.

于 2013-08-28T17:56:45.630 回答
1

optree 对我来说仍然是黑魔法,但以下是我的观察:

  1. 在遍历代码引用的 optree 时,您会遇到一种B::COP结构
  2. B::COP结构具有filelinecop_seq属性(以及其他)
  3. 不同的cop_seq子程序定义属性不同

Ass-u-me-ing 这些都是真实的,而不是一个非常不完整的正在发生的模型,您可以使用文件、行和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 回答