问题出在这部分Carp.pm
:
sub format_arg {
my $arg = shift;
if ( ref($arg) ) {
$arg = defined($overload::VERSION) ? overload::StrVal($arg) : "$arg";
}
...
}
也就是说,当参数可能是重载对象时,任何字符串化重载都会被StrVal
helper规避,这会强制默认字符串化。
不幸的是,没有直接的解决方法。我们所能做的就是给Carp::format_arg
潜艇打猴子补丁,例如
BEGIN {
use overload ();
use Carp ();
no warnings 'redefine';
my $orig = \&Carp::format_arg;
*Carp::format_arg = sub {
my ($arg) = @_;
if (ref $arg and my $stringify = overload::Method($arg, '""')) {
$_[0] = $stringify->($arg);
}
goto &$orig;
};
}
事实上,这是不优雅的,应该放入一个 pragma 中:
文件Carp/string_overloading.pm
:
package Carp::string_overloading;
use strict; use warnings;
use overload ();
use Carp ();
# remember the original format_arg method
my $orig = \&Carp::format_arg;
# This package is internal to Perl's warning system.
$Carp::CarpInternal{ __PACKAGE__() }++;
{
no warnings 'redefine';
*Carp::format_arg = sub {
my ($arg) = @_;
if ( ref($arg)
and in_effect(1 + Carp::long_error_loc)
and my $stringify = overload::Method($arg, '""')
) {
$_[0] = $stringify->($arg);
}
goto &$orig;
};
}
sub import { $^H{__PACKAGE__ . "/in_effect"} = 1 }
sub unimport { $^H{__PACKAGE__ . "/in_effect"} = 0 }
sub in_effect {
my $level = shift // 1;
return (caller $level)[10]{__PACKAGE__ . "/in_effect"};
}
1;
然后代码
use strict; use warnings;
package Foo {
use Carp ();
use overload '""' => sub {
my $self = shift;
return sprintf '%s[%s]', ref $self, join ", ", @$self;
};
use Carp::string_overloading;
sub foo { Carp::confess "as requested" }
no Carp::string_overloading;
sub bar { Carp::confess "as requested" }
}
my $foo = bless [1..3] => 'Foo';
eval { $foo->foo("foo") };
print $@;
eval { $foo->bar("bar") };
print $@;
输出:
as requested at test.pl line 12.
Foo::foo('Foo[1, 2, 3]', 'foo') called at test.pl line 20
eval {...} called at test.pl line 20
as requested at test.pl line 15.
Foo::bar('Foo=ARRAY(0x85468ec)', 'bar') called at test.pl line 22
eval {...} called at test.pl line 22