syphilis has asked for the wisdom of the Perl Monks concerning the following question:

Hi,
Hidden below my sig (by readmore tags) is a full Inline::C script that demos the problem. It uses overload, so that the subroutine 'overload_add_eq' is invoked whenever a '+=' is applied to the blessed object. At one stage, 'overload_add_eq' looked like this:
SV * overload_add_eq(SV * obj, SV * addon, SV * third) { Card* c = (Card *)SvIV(SvRV(obj)); SvREFCNT_inc(obj); c->value += SvIV(addon); return obj; }
And that works fine - but it occurred to me that I should be able to rewrite the function as in 'in place' operation (ie one that did not return anything) so I rewrote it as:
void overload_add_eq(SV * obj, SV * addon, SV * third) { Card* c = (Card *)SvIV(SvRV(obj)); c->value += SvIV(addon); }
Now, when I do $obj += $addon; windows intervenes to tell me that perl.exe has stopped working (but fails to come up with any other information). On linux I get a segfault. Interestingly, I find that this modified 'overload_add_eq' works fine (on both linux and windows) when invoked explicitly in perl code as overload_add_eq($obj, $addon, 0);. It's only when it's invoked by the overload mechanism that the problem arises. Anyone know why that is, and what to do about it ?

Cheers,
Rob
use warnings; package My_struct; use Inline (C => Config => BUILD_NOISY => 1, ); use Inline C => <<'EOC'; typedef struct { char * suit; int value; } Card; SV * create_struct(SV * s, SV * v) { Card * cptr; New(123, cptr, sizeof(Card), Card); if(cptr == NULL) croak("Failed to allocate memory in create_struc +t function"); cptr->suit = SvPV_nolen(s); cptr->value = SvIV(v); return sv_setref_pv(newSViv(0), "My_struct", cptr); } void deref_ref(SV * obj) { Inline_Stack_Vars; Inline_Stack_Reset; Inline_Stack_Push(newSVpv(((Card *)SvIV(SvRV(obj)))->suit, 0)); Inline_Stack_Push(newSViv(((Card *)SvIV(SvRV(obj)))->value)); Inline_Stack_Done; Inline_Stack_Return(2); } void DESTROY(SV * obj) { printf("Destroying ..."); Card* c = (Card *)SvIV(SvRV(obj)); Safefree(c); printf("...destroyed\n"); } void overload_add_eq(SV * obj, SV * addon, SV * third) { Card* c = (Card *)SvIV(SvRV(obj)); // SvREFCNT_inc(obj); c->value += SvIV(addon); // return obj; } EOC use overload '+=' => \&overload_add_eq; $suit = ('Hearts', 'Spades', 'Clubs', 'Diamonds')[rand(4)]; $value = int(rand(13)) + 1; $obj = create_struct($suit, $value); $addon = 5; @members = deref_ref($obj); print "@members\n"; #$obj += $addon; # Crashes overload_add_eq($obj, $addon, 0); @members = deref_ref($obj); print "@members\n";

Replies are listed 'Best First'.
Re: Overload '+=' with XSub
by Anno (Deacon) on Mar 29, 2007 at 09:34 UTC
    Normal mutators return the SV after they have changed it. My guess is that overload relies on that.

    Anno

      Anno, that seems to me to be a reasonable guess .... but I'm looking at a '+=' overload XSub that neither increments the refcount nor (afaict) returns anything ... and I'm wondering "how the hell does that work ?". It's from the GMP module - which is not to be found on CPAN. (It ships with the gmp source - in the demos/perl folder.) I don't know if it's going to help the discussion, but here's that XSub:
      void overload_addeq (x, y, o) mpz_assume x mpz_coerce y order_noswap o ALIAS: GMP::Mpz::overload_subeq = 1 GMP::Mpz::overload_muleq = 2 GMP::Mpz::overload_diveq = 3 GMP::Mpz::overload_remeq = 4 GMP::Mpz::overload_andeq = 5 GMP::Mpz::overload_ioreq = 6 GMP::Mpz::overload_xoreq = 7 PREINIT: static_functable const struct { void (*op) (mpz_ptr, mpz_srcptr, mpz_srcptr); } table[] = { { mpz_add }, /* 0 */ { mpz_sub }, /* 1 */ { mpz_mul }, /* 2 */ { mpz_tdiv_q }, /* 3 */ { mpz_tdiv_r }, /* 4 */ { mpz_and }, /* 5 */ { mpz_ior }, /* 6 */ { mpz_xor }, /* 7 */ }; PPCODE: assert_table (ix); (*table[ix].op) (x->m, x->m, y); XPUSHs (ST(0));
      First up - I don't really understand this XSub at all. I gather it's also handling -=, *=, /=, %=, &=, |= and ^= via some sort of lookup. The XPUSHs() also puzzles me. What does that achieve ?

      Anyway, it seems to me that this particular module has overloaded '+=' and friends without any need to return anything, and without any need to increase the refcount. (I've searched the entire XS file and there's not a single SvREFCNT_inc() to be found.)

      Cheers,
      Rob
      PS: I haven't actually built that GMP module for a while. I assume it works fine with perl-5.8.8, but that's something that's unverified. Last time I built this module, I also benchmarked the overloading and found it to be very efficient - not surprising, given that the author is a smart and talented programmer.
        I'm looking at a '+=' overload XSub that neither increments the refcount nor (afaict) returns anything

        Ah, but it does return a value.

        The XPUSHs() also puzzles me. What does that achieve?

        It pushes a value on the return stack. See perlapi.

        Anno