Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

xs modifying passed value

by Festus Hagen (Novice)
on Nov 17, 2012 at 11:03 UTC ( [id://1004300] : perlquestion . print w/replies, xml ) Need Help??

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

Hey y'all,

Trying to get the hang of this xs stuff and at a hump I can't seem to get over ...

Modifying a passed parameter.
The goal is to return the Int value and modify the passed arg to true or false (pass or fail on the conversion)

MyLib is a stupid simple string class, strToInt() works correctly, both in the return value as well as modifying the passfail value.
However, It does not show the changes in Perl.

The xs code. (with some printf's)

int MyLib::strToInt(passfail) bool passfail; CODE: if(passfail) { printf("It's true in xs. %i\n", passfail); } else { printf("It's false in xs. %i\n", passfail); } RETVAL = THIS->strToInt(&passfail); if(passfail) { printf("It's true out xs. %i\n", passfail); } else { printf("It's false out xs. %i\n", passfail); } OUTPUT: RETVAL passfail

The Perl calling code.

my $passfail = 0; my $tmp = MyLib->new("12"); my $t = $tmp->strToInt(\$passfail); print "It's $t\n" if $passfail eq 1;

The above code produces the following output:

It's false in xs. 0 It's true out xs. 1
So the xs and C code works

What the blazes am I doing wrong!
Perlxstut makes it look simple ...


fh : )_~

Replies are listed 'Best First'.
Re: xs modifying passed value
by space_monk (Chaplain) on Nov 17, 2012 at 17:55 UTC
    print "It's $t\n" if $passfail eq 1;

    Maybe a minor point, but "eq" is a string comparison operator, not a numeric comparison operator.

    A Monk aims to give answers to those who have none, and to learn from those who know more.
      Yup, Thank you.

      I always get em backwards ... :(

      fh : )_~

        I always get em backwards

        Easy: If you want to compare characters (strings, char* or char[] if you know C), use the operators made from characters (eq, ne, lt, gt, le, ge, cmp). If you want to compare numbers like in mathematics, use the operators you know from mathematics (==, !=, <, >, <=, >=, <=>).


        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: xs modifying passed value
by bulk88 (Priest) on Nov 17, 2012 at 18:02 UTC
    my $t = $tmp->strToInt(\$passfail);
    wrong. do "$passfail" not \$passfail. "\$passfail" is an anonymous scalar that you will never access again. "$passfail" you can access again. You passed a SV RV BTW and the outer SV was anonymous as I said.

    If you really want to accept in the in params a scalar with a reference, it will be a bit more complicated. The XS parameters processing is done from a file called lib/ExtUtils/typemap in perl.git. That contains what the XSUB interface can automate for you. For more complicated stuff I recommend you directly manipulate the SV*s using sv_set*v().

    If you want help, come back. Also use a C debugger with a watch window. printf debugging very slow for a programmer to work with.
      I originally had it as "$passfail" (and just tried again), then reading up led me to passing by reference and led to that.
      I've been hacking/reading on this for a few hours now ... So I've tried a few different angles, Apparently all wrong!

      I'm swimming in all this Sv SV PV Pv Rv RV blah blah ... :(

      Simple is my pleasure, I'll complicate it as my mind expands, hopefully before it explodes!

      Just noticed I forgot the proto for mylib strToInt in my OP, Here it is:
      int strToInt(bool *ok) const;

      I do have stepping capabilities on each side of the fence, Perl and C, Just not across.
      Qt Creator w/dbg on the C/C++ side and of course Perldbg on the Perl side.
      I have many times ran them together to see both sides of the fence, nothing jumps out at me other then the passed value between Perl and XS doesn't get modified, Between XS and my Lib does as shown by the printf(...)'s in the XS.

      I do have many other functions that work fine, though none of them require a passed argument modification and were for the most part simple. (looking back :))

      Shoot, I figured out without asking for help how to do the new() that has variable arguments as well as several different argument types and got it all to work, so far perf!, but this stupid modified passed argument has me stumped. Arrrrgggg!


      fh : )_~

        I don't really know C++, that is C++ code. Can you post the function from the .c file that xsubpp generates from your XS syntax func?

        I suggest you use Devel::Peek's Dump function. You can also call it from XS/C as dump.c#l2139 in perl.git as "void sv_dump(SV *sv)" It will need a SV *, I think that ST(1) is the passfail param as a SV *, since ST(0) will be your THIS pointer in a SV * I think from Script land. Using sv_dump with your code unmodified is impossible as written since the setting of SV * that matches passfail is after your CODE: section (again look at the .c file produced), so you would need to
        int MyLib::strToInt(passfailSV) SV* passfailSV PREINIT: bool passfail; CODE: foo(&passfail); SvSetMagicSV(passfailSV, passfail ? &PL_sv_yes : &PL_sv_no); OUTPUT: RETVAL
        I think there is a bug in the typemap,
        T_BOOL $arg = boolSV($var);
        That is from the default typemap. But look at boolSV.
        /* =for apidoc Am|SV *|boolSV|bool b Returns a true SV if C<b> is a true value, or a false SV if C<b> is 0. See also C<PL_sv_yes> and C<PL_sv_no>. =cut */ #define boolSV(b) ((b) ? &PL_sv_yes : &PL_sv_no)
        We assigned a SV * to a SV *, but if this is a in parameter and not an out parameter, you can't just change the SV * to another SV *, you have to change the SV * you got from the caller (return/result's of XSUBs are *usually* brand new SV *s you created and mortalized, then boolSV would work, it won't work to change the inside value of an existing scalar). BTW, you never need to mortal in SV *s you get through the param list (AKA ST(123456) macros), your caller owns the param list SV *s. Also never mortal &PL_sv_yes, &PL_sv_no, &PL_sv_undef, they are process globals/statics.

        I probably should go file a bug report with ExtUtils::ParseXS but I dont have the time.

        update: bug filed
Re: xs modifying passed value
by Festus Hagen (Novice) on Nov 18, 2012 at 01:43 UTC
    Results after hacking up Bulk88's righteous work into my deeper needs!

    Mucho Thanks for the kick in the git go.

    int MyLib::strToInt(...) PREINIT: SV* passfailSV; bool passfail; CODE: if(items == 2) { SV* passfailSV = ST(1); RETVAL = THIS->strToInt(&passfail); SvSetMagicSV(passfailSV, passfail ? &PL_sv_yes : &PL_sv_no); } else if(items == 1) { RETVAL = THIS->strToInt(); } else croak("Usage: MyLib->strToInt(...)"); OUTPUT: RETVAL

    Critiques ??
    Comments ??
    Suggestions ??
    Improvements ??

    fh : )_~

      Considered having the return of MyLib::strToInt be the bool, and the param be the int? Usually subs/funcs return true false, and details of the operation are inplace updates/ pass by reference/ $_[0] = "hello";/*status = 1;/. Or the author of C++ method strToInt did it that way and thats the way you will remember it in Perl? You could also return 2 elements "($truefalse, $theint) = $instance->strToInt();", and have MyLib::strToInt method take only one param (THIS scalar). If the caller in perl doesn't want the int, they dont need to collect it in list context.

      edit: With XS/C/Perl internals, the biggest XSUB debugging tool is looking at the .c code. Try posting that. For C code, you should learn how to create post C processor code, then run it through a code formatter. A 7 character named macro taking 1 param, barely or terribly documented, can expand to a screenful of code after 2-5 layers of macros are expanded. To understand why it failed, looking at the post preprocessor output it the only way. Also compile with optimize=debug and symbols, as you said already, you have a C debugger, so thats correct. Also instead of returning 1 bool, or a list, consider returning a hashref, a hashref is probably overkill for this function.
        Some people for param lists do,
        my %localdog; if(@_ == 2 && ref($_[1]) eq 'HASH') { #named params my $hvref = $_[1]; $localdog{name} = $hvref->{name}; $localdog{color} = $hvref->{color}; } else{ $localdog{name} = $_[1]; $localdog{color} = $_[2]; }
Re: xs modifying passed value
by Festus Hagen (Novice) on Nov 18, 2012 at 14:32 UTC
    Thanks for the thoughts, ideas and suggestions...

    The C++ lib is what it is for it's purpose and the reasons are beyond Perl ... The goal is to fit Perl to it, not it to Perl!

    If it was absolutely impossible (or a horrendous mess to do so), then a lib change could/would be made unless (highly unlikely) it adversely affected the Lib's purpose.

    However, I do not believe in "impossible"! ... If there is a will, there is a way!

    So with the lack of suggestions on the latest xs code I'll assume it's good to go and stick a fork in it, call it done!

    Thanks to all

    fh : )_~

      I thought of something. For
      croak("Usage: MyLib->strToInt(...)");
      croak("Usage: MyLib->strToInt([passfail])");
      square brackets mean optional arg. "..." means 0 to infinity additional args which is not your case. Some people refuse to RTFM and demand that the usage croaks be the only thing they need to look at to call the func/sub/meth.
        Ok, I'm laughing!

        Not at you ... About your description of ME!
        "Some people refuse to RTFM and demand that the usage croaks be the only thing they need to look at to call the func/sub/meth."

        That's me!

        Also over the Anal Perfectionism.

        That would also be me!


        fh : )_~