在下面的片段中,如何区分子foo
的第二个调用实例与第一个调用实例?
while ($whatever) {
foo(); foo(); # foo() and foo() have the same caller package, file, and line
}
像返回文件,行和列的超级caller()
之类的东西可以解决问题。我不想使用源过滤器。
背景,或者,这不是一个XY问题吗?
我有一个便利模块,Local :: Thread :: Once,以OO-ish方式公开像pthread_once
/ std::call_once
这样的功能,也作为子例程属性。这些都很容易,因为在任何一种情况下都有一个自然而明确的“once_control”或“once_flag”。
但是,还有一个程序界面 - once { ... }
- 目前根据caller
返回的$filename
和$line
进行序列化。像这样:
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复制到另一个。
我可以将这个问题视为一个非常人为的用例的文档限制,但我更愿意以某种方式修复它。
答案 0 :(得分:6)
Devel::Callsite正是为了这个目的写的。
答案 1 :(得分:3)
在我明白你在说什么之前,我必须阅读几次。 “超级来电”功能怎么样:
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;
}
就像caller
,但第4个元素是我们连续多少次看到这个确切的包,文件和行的计数。
答案 2 :(得分:1)
对我来说,光谱仍然是一种黑魔法,但这是我的观察:
B::COP
结构B::COP
结构包含file
,line
和cop_seq
属性(以及其他)cop_seq
属性不同这些都是正确的,而不是一个非常不完整的模型,你可以使用文件,行和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'