简体   繁体   English

您如何检查 object 是否使 XS 中的运算符超载?

[英]How do you check to see if an object overloads an operator in XS?

If my XS function has been passed an SV containing a blessed object, how can I check to see if that object overloads a particular Perl operator?如果我的 XS function 已通过包含祝福 object 的 SV,我如何检查该 object 是否重载了特定的 Perl 运算符? For example, overloading "" .例如,重载""

One way I can think of would be to loop through its class and all parent classes, looking for a method called ("" . That sounds kinda yuck though, and it gets complicated when you consider fallbacks. (By fallbacks, I mean a class might not overload the + operator, but if it overloads conversion to a number, Perl is able to fall back to using that to implement addition.)我能想到的一种方法是遍历它的 class 和所有父类,寻找一个名为(""的方法。虽然这听起来有点恶心,但当你考虑后备时它会变得复杂。(后备,我的意思是 class可能不会重载+运算符,但如果它重载到数字的转换,Perl 可以回退到使用它来实现加法。)

There is a macro that checks if there's any overloading for the class ( SvAMAGIC ), but there's no ready-made function to check for specific kinds of overloading.有一个宏检查 class ( SvAMAGIC ) 是否有任何重载,但没有现成的 function 来检查特定类型的重载。 Perl always wants to follow up the check with the actual overloading, so the two are bundled together in Perl_amagic_call in gv.c . Perl 总是想跟进实际重载的检查,所以两者在Perl_amagic_callgv.c中捆绑在一起。

The following checks if an object's class overloads a specific kind of magic:以下检查对象的 class 是否超载了特定类型的魔法:

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

The problem with this is that it doesn't check for fallbacks.这个问题是它不检查回退。 The code that does that is literally thousands of lines long.执行此操作的代码实际上有数千行。 (That probably includes some code to prepare for doing the fallback.) (这可能包括一些代码来准备执行回退。)


Full test:全面测试:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM