in reply to Re: Puzzled by value of $overload::ops{binary}
in thread Puzzled by value of $overload::ops{binary}

As with pretty much every other thing involving the perl parser, the answer is a resounding yes

It seems that there are often limitations regarding the liberties that can be taken regarding overloading.
I have overloading subs (XSubs) that will overload '++' as:
void overload_inc($V * object, SV * second, SV * switch) { /* code that adds 1 to the value held by object */ }
That always works perfectly well.
I've then considered that I should be able to do the same thing with the overloading of the '+=' operator:
void overload_plus_eq(SV * object, SV * addon, SV * switch) { /*code that adds the value of addon to the value held by object */ }
That attempt (in overload_plus_eq), to modify the object in place always fails when called by the '+=' operator - and I have never been able to write that XSub such that it is successfully called via overloading of '+=', unless it returns a new object that overwrites the existing one.
(Which, BTW, I think is what happens with the overloading of '+=' when only '+' is overloaded.)

I've assumed the incapacity of '+=' overloading to "modify in place" is related to '+=' NOT being seen as a "mutator" by overload.pm.
But maybe it's just that I haven't found the right way of doing it.

I've often wished that overloading of "log2" and "log10" was available ... but not to the extent that I'm going to commandeer a couple of these 'bitwise' operators for that purpose ;-)
(I doubt that could readily be achievable, anyway.)

I've noticed that overload.pm recognizes these additional bitwise operators as being valid, even though"feature 'bitwise'" has not been loaded:
D:\>perl -Mwarnings -Moverload -e "use overload 'fu' => sub {return 1} +;" overload arg 'fu' is invalid at -e line 1. D:\>perl -Mwarnings -Moverload -e "use overload '&.' => sub {return 1} +;" D:\>perl -Mwarnings -Moverload -e "use overload '&.=' => sub {return 1 +};" D:\>
Seems a bit odd .... but perhaps of little or no importance.

Cheers,
Rob

Replies are listed 'Best First'.
Re^3: Puzzled by value of $overload::ops{binary}
by etj (Priest) on Jun 25, 2024 at 12:11 UTC
    I'd suggest PDL is the ultimate operator-overloader "source of truth". Mutating overloads is implemented in PDL::Ops, specifically (https://github.com/PDLPorters/pdl/blob/ad2783ad4b00583f14af3a3c24a93bb9f1a4ff33/Basic/Ops/ops.pd#L191-L196):
    $ret .= pp_line_numbers(__LINE__, <<EOF) if $mutator; BEGIN { # in1, in2, out, swap if true \$OVERLOADS{'$op='} = sub { PDL::$name(\$_[0]->inplace, \$_[1]); \$_[0 +] }; } EOF
    Modifying the generated code for += to this:
    #line 192 "ops.pd" BEGIN { # in1, in2, out, swap if true $OVERLOADS{'+='} = sub { use Carp; use Test::More; diag "RUN ", Test::More::explain \@_; PDL::plus($_[0]->inplace, $_[1]); $_[0] }; }
    and running it like this:
    $ perl -MPDL -e '$p=pdl(1); $p+=2'
    gives:
    # RUN [ # bless( do{\(my $o = '94749946365136')}, 'PDL' ), # 2, # undef # ]
    so your XS function should work. Did you make an overload for +=?
      In Math::GMPz, the overloading of '++' is:
      void overload_inc(pTHX_ SV * p, SV * a, SV * b) { mpz_add_ui(*(INT2PTR(mpz_t *, SvIVX(SvRV(p)))), *(INT2PTR(mpz_t * +, SvIVX(SvRV(p)))), 1); }
      which is simply doing p = p + 1, with "p" being a Math::GMPz object.
      Then, in the bottom section of the XS file I have:
      void overload_inc (p, a, b) SV * p SV * a SV * b CODE: overload_inc(aTHX_ p, a, b); XSRETURN_EMPTY; /* return empty stack */
      Works fine.
      For the overloading of '+=', it gets a bit drawn out because we might be adding on something other than an IV.
      But if we were just dealing with adding on an unsigned long int we would actually have:
      SV * overload_add_eq(pTHX_ SV * p, SV * a, SV * b) { mpz_add_ui(*(INT2PTR(mpz_t *, SvIVX(SvRV(p)))), *(INT2PTR(mpz_t * +, SvIVX(SvRV(p)))), SvUV(a)); SvREFCNT_inc(p); return p; }
      which is just doing p = p + a, and returning p (after increasing the refcount).
      The corresponding code in the bottom section of the XS file would be:
      SV * overload_add_eq (p, a, b) SV * p SV * a SV * b CODE: RETVAL = overload_add_eq (aTHX_ p, a, b); OUTPUT: RETVAL
      Now, it's not hard to work out what that overload_add_eq() would look like if I were to apply the same approach as used by overload_inc():
      void overload_add_eq(pTHX_ SV * p, SV * a, SV * b) { mpz_add_ui(*(INT2PTR(mpz_t *, SvIVX(SvRV(p)))), *(INT2PTR(mpz_t * +, SvIVX(SvRV(p)))), SvUV(a)); }
      and
      void overload_add_eq (p, second, third) SV * p SV * a SV * b CODE: overload_add_eq(aTHX_ p, a, b); XSRETURN_EMPTY; /* return empty stack */
      But I've never managed to get that to work.
      It has, however, been a while since I've tried - so I'll have another crack at it tomorrow (as it's now getting late over here) and see if I can get lucky.
      (I can't actually remember just what the problem is.)

      BTW, I've fiddled about with that code that I've posted, so it might contain one or more typos.

      Cheers,
      Rob
        .... so I'll have another crack at it tomorrow ....

        In GMPz.xs, in the bottom section of the file I have replaced:
        SV * overload_add_eq (a, b, third) SV * a SV * b SV * third CODE: RETVAL = overload_add_eq (aTHX_ a, b, third); OUTPUT: RETVAL
        with
        void overload_add_eq (a, b, third) SV * a SV * b SV * third CODE: overload_add_eq (aTHX_ a, b, third); XSRETURN_EMPTY; /* return empty stack */
        And in the top section of the file I have replaced:
        SV * overload_add_eq(pTHX_ SV * a, SV * b, SV * third) { /* lengthy code snipped */ }
        with
        void overload_add_eq(pTHX_ SV * a, SV * b, SV * third) { PERL_UNUSED_ARG(third); /* UPDATE: This line can be removed as ir +relevant */ mpz_add_ui(*(INT2PTR(mpz_t *, SvIVX(SvRV(a)))), *(INT2PTR(mpz_t * +, SvIVX(SvRV(a)))), SvUV(b)); }
        This means that we're always adding SvUV(b) to a, irrrespective of what b actually is.
        This is good enough for demonstration purposes if we make sure that the second arg (b) is an IV, but we probably don't want to run make test.

        On Windows 11, perl-5.38.0, I then run perl Makefile.PL, followed by make.
        The build emits no warnings.
        Then:
        > perl -Mblib -MMath::GMPz -MDevel::Peek -wle "$x = Math::GMPz->new(12 +34); $x++;print $x; $x += 15; print $x; Dump($x);" 1235 Use of uninitialized value $x in print at -e line 1. SV = PVIV(0x1abd93865e0) at 0x1abdad3ae48 REFCNT = 1 FLAGS = () IV = 1837594613288 PV = 0
        As you can see from that, the Math::GMPz object ($x) has been well and truly clobbered by the '+=' overloading.
        Anyone interested can try this out by making the above alterations to GMPz.xs in https://cpan.metacpan.org/authors/id/S/SI/SISYPHUS/Math-GMPz-0.61.tar.gz.
        Thoughts, observations, opinions and revelations are all most welcome.

        UPDATE: I see the same behaviour on Cygwin, and also on Ubuntu-22.04.4-LTS.

        UPDATE 2: I'll also point out that this altered version of overload_add_eq() works as expected when called as a function:
        > perl -Mblib -MMath::GMPz -MDevel::Peek -wle "$x = Math::GMPz->new(12 +34); $x++; print $x; Math::GMPz::overload_add_eq($x, 15, 0); print $x +;" 1235 1250
        UPDATE 3: Also, if I remove the '+=' overloading (by commenting out the appropriate line in GMPz.pm), then the '+=' overloading (which then uses the '+' overloading) works fine:
        > perl -Mblib -MMath::GMPz -MDevel::Peek -wle "$x = Math::GMPz->new(12 +34); $x++;print $x; $x += 15; print $x; Dump($x);" 1235 1250 SV = IV(0x2bfffa61b78) at 0x2bfffa61b88 REFCNT = 1 FLAGS = (ROK) RV = 0x2bfff38aac8 SV = PVMG(0x2bfffa24a58) at 0x2bfff38aac8 REFCNT = 1 FLAGS = (OBJECT,IOK,READONLY,pIOK) IV = 3023652457216 NV = 0 PV = 0 STASH = 0x2bfffa5e5e8 "Math::GMPz"
        Cheers,
        Rob