in reply to accessing mpz_t * of Math::BigInt::GMP from XS

Does it *have* to be Math::BigInt::GMP ?
It's very simple with Math::GMP or Math::GMPz objects:
use Math::GMP; use Math::GMPz; use Inline C => Config => LIBS => '-lgmp', BUILD_NOISY => 1, USING => ParseRecDescent; use Inline C => <<'EOC'; #include <gmp.h> void my_mul_si(SV * rop, SV * op, SV * si) { mpz_mul_si (*(INT2PTR(mpz_t *, SvIV(SvRV(rop)))), *(INT2PTR(mpz_t *, SvIV(SvRV(op)))), SvIV(si)); } EOC my $ x = -10; my $g_rop = Math::GMP->new(); my $g_op = Math::GMP->new(1234567); my $z_rop = Math::GMPz->new(); my $z_op = Math::GMPz->new(1234567); my_mul_si($g_rop, $g_op, $x); my_mul_si($z_rop, $z_op, $x); my_mul_si($g_op, $g_op, $x); my_mul_si($z_op, $z_op, $x); print $g_rop, "\n", $z_rop, "\n", $g_op, "\n", $z_op, "\n"; __END__ Outputs: -12345670 -12345670 -12345670 -12345670
I feel that it should also be do-able with Math::BigInt::GMP, too - but I'm currently stuck on trying to work out how that module's mpz_from_sv_nofail() passes its own sv_derived_from() check.
Every object I pass to it in my own (speculative) code turns out to be a Math::BigInt (not the required Math::BigInt::GMP) object:
use Math::BigInt lib => 'GMP'; use warnings; use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'EOC'; void derivation(SV * obj) { if(sv_derived_from(obj, "Math::BigInt::GMP")) printf("A Math::BigInt::GMP object\n"); else printf("Not a Math::BigInt::GMP object\n"); if(sv_derived_from(obj, "Math::BigInt")) printf("A Math::BigInt object\n"); else printf("Not a Math::BigInt object\n"); } EOC my $o = Math::BigInt->new(1234); derivation($o); __END__ Outputs: Not a Math::BigInt::GMP object A Math::BigInt object
What's the trick ?

Cheers,
Rob

Replies are listed 'Best First'.
Re^2: accessing mpz_t * of Math::BigInt::GMP from XS
by dkg (Acolyte) on Mar 18, 2011 at 20:25 UTC
    yah, your approach is the approach i tried as well, and i'm seeing the same "not a Math::BigInt::GMP object" business. Devel::Peek shows some interesting details, but i don't know this stuff deeply enough to interpret it:
    0 dkg@pip:~$ perl -MDevel::Peek -e 'use Math::BigInt only => "GMP"; m +y $x = Math::BigInt->new(34); printf("%s\n", Dump($x));' SV = RV(0x907fe54) at 0x907fe48 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x9065818 SV = PVHV(0x906ad24) at 0x9065818 REFCNT = 1 FLAGS = (OBJECT,OVERLOAD,SHAREKEYS) STASH = 0x90a0f30 "Math::BigInt" ARRAY = 0x90872d8 (0:6, 1:2) hash quality = 125.0% KEYS = 2 FILL = 2 MAX = 7 RITER = -1 EITER = 0x0 Elt "value" HASH = 0x1e720953 SV = RV(0x907fe24) at 0x907fe18 REFCNT = 1 FLAGS = (ROK) RV = 0x90656f8 SV = PVMG(0x90e21a4) at 0x90656f8 REFCNT = 1 FLAGS = (OBJECT,RMG) IV = 0 NV = 0 PV = 0 MAGIC = 0x9097ba0 MG_VIRTUAL = 0xb781aea0 MG_TYPE = PERL_MAGIC_ext(~) MG_FLAGS = 0x10 MG_PTR = 0x9083500 "" STASH = 0x91ab538 "Math::BigInt::GMP" Elt "sign" HASH = 0xe7455c8f SV = PV(0x9063748) at 0x9065998 REFCNT = 1 FLAGS = (POK,pPOK) PV = 0x90833b0 "+"\0 CUR = 1 LEN = 4 0 dkg@pip:~$
    And here's the same thing with Math::GMP:
    0 dkg@pip:~$ perl -MDevel::Peek -e 'use Math::GMP; my $x = Math::GMP- +>new(34); printf("%s\n", Dump($x));' SV = RV(0x868ce44) at 0x868ce38 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x8672998 SV = PVMG(0x86cc918) at 0x8672998 REFCNT = 1 FLAGS = (OBJECT,IOK,OVERLOAD,pIOK) IV = 141099448 NV = 0 PV = 0 STASH = 0x86adf00 "Math::GMP" 0 dkg@pip:~$
Re^2: accessing mpz_t * of Math::BigInt::GMP from XS
by dkg (Acolyte) on Mar 18, 2011 at 20:50 UTC
    aha -- if i change your code from:
    derivation($o);
    to:
    derivation($o->{value});
    then i get:
    A Math::BigInt::GMP object Not a Math::BigInt object
    So i think i can actually get access to the Math::BigInt::GMP at least.

    But i think i'm still stuck on how to deal with the #defines so that my module will be reasonably portable without requiring that the person building it know exactly what #defines were used when their instance of Math::BigInt::GMP was built.

    Any suggestions? Thanks for pointing me part of the way forward, at least! It's very helpful :)

      Yes, I eventually got around to passing $obj->{value}, but there's probably still something I'm missing. The following works for me - but it avoids a lot of the pre-processor stuff that Math::BigInt::GMP's GMP.xs contains, and I therefore wonder about its portability
      use Math::BigInt lib => 'GMP'; use Inline C => Config => LIBS => '-lgmp', USING => 'ParseRegExp', BUILD_NOISY => 1; use Inline C => <<'EOC'; #include <gmp.h> mpz_t * mpz_from_sv_nofail (SV *sv) { MAGIC *mg; if (!sv_derived_from(sv, "Math::BigInt::GMP")) croak("not of type Math::BigInt::GMP"); mg = SvMAGIC(SvRV(sv)); return (mpz_t *)mg->mg_ptr; } mpz_t * mpz_from_sv (SV *sv) { mpz_t *mpz; if (!(mpz = mpz_from_sv_nofail(sv))) croak("failed to fetch mpz pointer"); return mpz; } void my_mul(SV * bi_rop, SV * bi_op, SV * si) { mpz_t *t1, *t2; t1 = mpz_from_sv(bi_rop); t2 = mpz_from_sv(bi_op); mpz_mul_si(*t1, *t2, SvIV(si) * 10); } EOC my $rop = Math::BigInt->new(0); my $op = Math::BigInt->new(123456); my $x = -110; my_mul($rop->{value}, $op->{value}, $x); print $rop, "\n"; __END__ Outputs: -13580160
      I also wonder:
      1) How is the change of sign being effected by my_mul() - given that it should be working only with 'value';
      (Update: Setting $op to -123456 and multiplying by -110 still results in an output of -13580160. So 'sign' is *not* being dealt with correctly ... which is what I expected. )
      2) Why do I have to multiply the 3rd arg of my_mul() by 10 in order to have it produce the correct value in the Math::BigInt::GMP object.

      I'm sure there's a very good (and likely very simple) explanation for both.

      Cheers,
      Rob
        Eventually got it down to the following (which works correctly for me, afaik):
        use Math::BigInt lib => 'GMP'; use Inline C => Config => LIBS => '-lgmp', USING => 'ParseRegExp', BUILD_NOISY => 1; use Inline C => <<'EOC'; #include <gmp.h> mpz_t * mpz_from_sv_nofail (SV *sv) { MAGIC *mg; if (!sv_derived_from(sv, "Math::BigInt::GMP")) croak("not of type Math::BigInt::GMP"); mg = SvMAGIC(SvRV(sv)); return (mpz_t *)mg->mg_ptr; } void _my_mul(SV * bi_rop, SV * bi_op, SV * si, SV * rop_sign, SV * op_ +sign) { mpz_t *t1, *t2; sv_setpv(rop_sign, SvPV_nolen(op_sign)); t1 = mpz_from_sv_nofail(bi_rop); t2 = mpz_from_sv_nofail(bi_op); mpz_abs(*t2, *t2); mpz_mul_si(*t1, *t2, abs(SvIV(si))); if(SvIV(si) < 0) { if(strEQ(SvPV_nolen(op_sign), "-")) sv_setpv(rop_sign, "+"); else sv_setpv(rop_sign, "-"); } } EOC my $rop = Math::BigInt->new(0); my $op = Math::BigInt->new(-123456); my $x = 1103; my_mul($rop, $op, $x); print $rop, "\n"; my_mul($op, $op, 5); print $op, "\n"; sub my_mul { _my_mul($_[0]->{value}, $_[1]->{value}, $_[2], $_[0]->{sign}, $_[1]- +>{sign}); }
        Cheers,
        Rob