如何检查对象是否重载XS中的运算符



如果我的XS函数被传递了一个包含祝福对象的SV,我如何检查该对象是否重载了特定的Perl运算符?例如,使""过载。

我可以想到的一种方法是循环遍历它的类和所有父类,寻找一个名为(""的方法。虽然这听起来有点恶心,但当你考虑到失误时,情况会变得复杂起来。(所谓回退,我的意思是一个类可能不会重载+运算符,但如果它重载了对数字的转换,Perl就可以回退到使用它来实现加法。(

有一个宏可以检查类(SvAMAGIC(是否存在任何重载,但没有现成的函数来检查特定类型的重载。Perl总是想用实际的重载来跟踪检查,所以这两者在Perl_amagic_callgv.c中捆绑在一起。

以下检查对象的类是否重载了特定类型的魔术:

void has_amagic(SV *sv, IV method) {
dXSARGS;
SvGETMAGIC(sv);
HV *stash;
MAGIC *mg;
AMT *amtp;
CV **cvp;
if
(  SvAMAGIC(sv)
&& ( stash = SvSTASH(SvRV(sv)) )
&& Gv_AMG(stash)
&& ( mg = mg_find((const SV*)stash, PERL_MAGIC_overload_table) )
&& AMT_AMAGIC( amtp = (AMT*)mg->mg_ptr )
&& ( cvp = amtp->table )
&& cvp[method]
) {
XSRETURN_YES;
} else {
XSRETURN_NO;
}
}

这样做的问题在于,它无法检查是否存在回退。这样做的代码实际上有数千行长。(这可能包括一些为执行回退做准备的代码。(


完整测试:

use 5.014;
use warnings;
BEGIN {
package Foo;
use overload
fallback => 1,
'cmp' => sub { };
sub new {
my $class = shift;
return bless({ @_ }, $class);
}
}
use Inline C => <<'__EOS__';
void has_amagic(SV *sv, IV method) {
dXSARGS;
SvGETMAGIC(sv);
HV *stash;
MAGIC *mg;
AMT *amtp;
CV **cvp;
if
(  SvAMAGIC(sv)
&& ( stash = SvSTASH(SvRV(sv)) )
&& Gv_AMG(stash)
&& ( mg = mg_find((const SV*)stash, PERL_MAGIC_overload_table) )
&& AMT_AMAGIC( amtp = (AMT*)mg->mg_ptr )
&& ( cvp = amtp->table )
&& cvp[method]
) {
XSRETURN_YES;
} else {
XSRETURN_NO;
}
}
__EOS__

my %overloads;
BEGIN {
# Based on overload.h
%overloads = (
AMG_TO_SV      => 0x01,  #  ${}
AMG_TO_AV      => 0x02,  #  @{}
AMG_TO_HV      => 0x03,  #  %{}
AMG_TO_GV      => 0x04,  #  *{}
AMG_TO_CV      => 0x05,  #  &{}
AMG_INC        => 0x06,  #  ++
AMG_DEC        => 0x07,  #  --
AMG_BOOL       => 0x08,  #  bool
AMG_NUMER      => 0x09,  #  0+
AMG_STRING     => 0x0a,  #  ""
AMG_NOT        => 0x0b,  #  !
AMG_COPY       => 0x0c,  #  =
AMG_ABS        => 0x0d,  #  abs
AMG_NEG        => 0x0e,  #  neg
AMG_ITER       => 0x0f,  #  <>
AMG_INT        => 0x10,  #  int
AMG_LT         => 0x11,  #  <
AMG_LE         => 0x12,  #  <=
AMG_GT         => 0x13,  #  >
AMG_GE         => 0x14,  #  >=
AMG_EQ         => 0x15,  #  ==
AMG_NE         => 0x16,  #  !=
AMG_SLT        => 0x17,  #  lt
AMG_SLE        => 0x18,  #  le
AMG_SGT        => 0x19,  #  gt
AMG_SGE        => 0x1a,  #  ge
AMG_SEQ        => 0x1b,  #  eq
AMG_SNE        => 0x1c,  #  ne
AMG_NOMETHOD   => 0x1d,  #  nomethod
AMG_ADD        => 0x1e,  #  +
AMG_ADD_ASS    => 0x1f,  #  +=
AMG_SUBTR      => 0x20,  #  -
AMG_SUBTR_ASS  => 0x21,  #  -=
AMG_MULT       => 0x22,  #  *
AMG_MULT_ASS   => 0x23,  #  *=
AMG_DIV        => 0x24,  #  /
AMG_DIV_ASS    => 0x25,  #  /=
AMG_MODULO     => 0x26,  #  %
AMG_MODULO_ASS => 0x27,  #  %=
AMG_POW        => 0x28,  #  **
AMG_POW_ASS    => 0x29,  #  **=
AMG_LSHIFT     => 0x2a,  #  <<
AMG_LSHIFT_ASS => 0x2b,  #  <<=
AMG_RSHIFT     => 0x2c,  #  >>
AMG_RSHIFT_ASS => 0x2d,  #  >>=
AMG_BAND       => 0x2e,  #  &
AMG_BAND_ASS   => 0x2f,  #  &=
AMG_SBAND      => 0x30,  #  &.
AMG_SBAND_ASS  => 0x31,  #  &.=
AMG_BOR        => 0x32,  #  |
AMG_BOR_ASS    => 0x33,  #  |=
AMG_SBOR       => 0x34,  #  |.
AMG_SBOR_ASS   => 0x35,  #  |.=
AMG_BXOR       => 0x36,  #  ^
AMG_BXOR_ASS   => 0x37,  #  ^=
AMG_SBXOR      => 0x38,  #  ^.
AMG_SBXOR_ASS  => 0x39,  #  ^.=
AMG_NCMP       => 0x3a,  #  <=>
AMG_SCMP       => 0x3b,  #  cmp
AMG_COMPL      => 0x3c,  #  ~
AMG_SCOMPL     => 0x3d,  #  ~.
AMG_ATAN2      => 0x3e,  #  atan2
AMG_COS        => 0x3f,  #  cos
AMG_SIN        => 0x40,  #  sin
AMG_EXP        => 0x41,  #  exp
AMG_LOG        => 0x42,  #  log
AMG_SQRT       => 0x43,  #  sqrt
AMG_REPEAT     => 0x44,  #  x
AMG_REPEAT_ASS => 0x45,  #  x=
AMG_CONCAT     => 0x46,  #  .
AMG_CONCAT_ASS => 0x47,  #  .=
AMG_SMART      => 0x48,  #  ~~
AMG_FTEST      => 0x49,  #  -X
AMG_REGEXP     => 0x4a,  #  qr
);
}
use constant %overloads;
my $o = Foo->new();
my @overloads =
grep { has_amagic($o, $overloads{$_}) }
sort { $overloads{$a} <=> $overloads{$b} }
keys(%overloads);

if (@overloads) {
say join ", ", @overloads;
} else {
say "[none]";
}

相关内容

  • 没有找到相关文章

最新更新